diff --git a/dubbo-plugin/dubbo-compiler/pom.xml b/dubbo-plugin/dubbo-compiler/pom.xml index adaeb057de4..48c800e7fe4 100644 --- a/dubbo-plugin/dubbo-compiler/pom.xml +++ b/dubbo-plugin/dubbo-compiler/pom.xml @@ -100,5 +100,4 @@ - diff --git a/dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java b/dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java index c434ec47fcb..7ea75cf5392 100644 --- a/dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java +++ b/dubbo-plugin/dubbo-compiler/src/main/java/org/apache/dubbo/gen/AbstractGenerator.java @@ -33,6 +33,9 @@ import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; import com.github.mustachejava.MustacheFactory; +import com.google.api.AnnotationsProto; +import com.google.api.HttpRule; +import com.google.api.HttpRule.PatternCase; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.base.Strings; @@ -42,6 +45,7 @@ import com.google.protobuf.DescriptorProtos.MethodDescriptorProto; import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; import com.google.protobuf.DescriptorProtos.SourceCodeInfo.Location; +import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.compiler.PluginProtos; public abstract class AbstractGenerator { @@ -67,24 +71,40 @@ protected String getInterfaceTemplateFileName() { } private String getServiceJavaDocPrefix() { - return " "; + return ""; } private String getMethodJavaDocPrefix() { - return " "; + return " "; } public List generateFiles(PluginProtos.CodeGeneratorRequest request) { - final ProtoTypeMap typeMap = ProtoTypeMap.of(request.getProtoFileList()); + // 1. build ExtensionRegistry and registry + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + AnnotationsProto.registerAllExtensions(registry); + // 2. compile proto file List protosToGenerate = request.getProtoFileList().stream() .filter(protoFile -> request.getFileToGenerateList().contains(protoFile.getName())) + .map(protoFile -> parseWithExtensions(protoFile, registry)) .collect(Collectors.toList()); + // 3. use compiled proto file build ProtoTypeMap + final ProtoTypeMap typeMap = ProtoTypeMap.of(protosToGenerate); + + // 4. find and generate serviceContext List services = findServices(protosToGenerate, typeMap); return generateFiles(services); } + private FileDescriptorProto parseWithExtensions(FileDescriptorProto protoFile, ExtensionRegistry registry) { + try { + return FileDescriptorProto.parseFrom(protoFile.toByteString(), registry); + } catch (Exception e) { + return protoFile; + } + } + private List findServices(List protos, ProtoTypeMap typeMap) { List contexts = new ArrayList<>(); @@ -171,6 +191,40 @@ private MethodContext buildMethodContext( methodContext.isManyOutput = methodProto.getServerStreaming(); methodContext.methodNumber = methodNumber; + // compile google.api.http option + HttpRule httpRule = parseHttpRule(methodProto); + if (httpRule != null) { + PatternCase patternCase = httpRule.getPatternCase(); + String path; + switch (patternCase) { + case GET: + path = httpRule.getGet(); + break; + case PUT: + path = httpRule.getPut(); + break; + case POST: + path = httpRule.getPost(); + break; + case DELETE: + path = httpRule.getDelete(); + break; + case PATCH: + path = httpRule.getPatch(); + break; + default: + path = ""; + break; + } + if (!path.isEmpty()) { + methodContext.httpMethod = patternCase.name(); + methodContext.path = path; + methodContext.body = httpRule.getBody(); + methodContext.hasMapping = true; + methodContext.needRequestAnnotation = !methodContext.body.isEmpty() || path.contains("{"); + } + } + Location methodLocation = locations.stream() .filter(location -> location.getPathCount() == METHOD_NUMBER_OF_PATHS && location.getPath(METHOD_NUMBER_OF_PATHS - 1) == methodNumber) @@ -197,6 +251,17 @@ private MethodContext buildMethodContext( return methodContext; } + private HttpRule parseHttpRule(MethodDescriptorProto methodProto) { + HttpRule rule = null; + // check methodProto have options + if (methodProto.hasOptions()) { + if (methodProto.getOptions().hasExtension(AnnotationsProto.http)) { + rule = methodProto.getOptions().getExtension(AnnotationsProto.http); + } + } + return rule; + } + private String lowerCaseFirst(String s) { return Character.toLowerCase(s.charAt(0)) + s.substring(1); } @@ -360,6 +425,26 @@ private static class MethodContext { public String grpcCallsMethodName; public int methodNumber; public String javaDoc; + /** + * The HTTP request method + */ + public String httpMethod; + /** + * The HTTP request path + */ + public String path; + /** + * The message field that the HTTP request body mapping to + */ + public String body; + /** + * Whether the method has HTTP mapping + */ + public boolean hasMapping; + /** + * Whether the request body parameter need @GRequest annotation + */ + public boolean needRequestAnnotation; // This method mimics the upper-casing method ogf gRPC to ensure compatibility // See https://github.com/grpc/grpc-java/blob/v1.8.0/compiler/src/java_plugin/cpp/java_generator.cpp#L58 diff --git a/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleInterfaceStub.mustache b/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleInterfaceStub.mustache index d31fed7efbe..559e2b9e5ce 100644 --- a/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleInterfaceStub.mustache +++ b/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleInterfaceStub.mustache @@ -1,25 +1,28 @@ /* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ {{#packageName}} package {{packageName}}; {{/packageName}} import org.apache.dubbo.common.stream.StreamObserver; +import org.apache.dubbo.remoting.http12.HttpMethods; +import org.apache.dubbo.remoting.http12.rest.Mapping; +import org.apache.dubbo.rpc.stub.annotations.GRequest; import com.google.protobuf.Message; import java.util.HashMap; @@ -28,7 +31,6 @@ import java.util.function.BiConsumer; import java.util.concurrent.CompletableFuture; public interface {{interfaceClassName}} extends org.apache.dubbo.rpc.model.DubboStub { - String JAVA_SERVICE_NAME = "{{packageName}}.{{serviceName}}"; {{#commonPackageName}} String SERVICE_NAME = "{{commonPackageName}}.{{serviceName}}"; @@ -40,34 +42,32 @@ public interface {{interfaceClassName}} extends org.apache.dubbo.rpc.model.Dubbo {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} - {{outputType}} {{methodName}}({{inputType}} request); - - CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request); - - + {{#hasMapping}} + @Mapping(method = HttpMethods.{{httpMethod}}, path = "{{path}}") + {{/hasMapping}} + {{outputType}} {{methodName}}({{#needRequestAnnotation}}@GRequest{{#body}}("{{body}}"){{/body}} {{/needRequestAnnotation}}{{inputType}} request); + CompletableFuture<{{outputType}}> {{methodName}}Async({{#needRequestAnnotation}}@GRequest {{/needRequestAnnotation}}{{inputType}} request); {{/unaryMethods}} - {{#serverStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} - void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver); + {{#hasMapping}} + @Mapping(method = HttpMethods.{{httpMethod}}, path = "{{path}}") + {{/hasMapping}} + void {{methodName}}({{#needRequestAnnotation}}@GRequest{{#body}}("{{body}}"){{/body}} {{/needRequestAnnotation}}{{inputType}} request, StreamObserver<{{outputType}}> responseObserver); {{/serverStreamingMethods}} - {{#biStreamingWithoutClientStreamMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver); {{/biStreamingWithoutClientStreamMethods}} - - {{#clientStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} {{/javaDoc}} StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver); {{/clientStreamingMethods}} - } diff --git a/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache b/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache index 6f19d17fb1c..94572b36e42 100644 --- a/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache +++ b/dubbo-plugin/dubbo-compiler/src/main/resources/Dubbo3TripleStub.mustache @@ -1,19 +1,19 @@ /* -* Licensed to the Apache Software Foundation (ASF) under one or more -* contributor license agreements. See the NOTICE file distributed with -* this work for additional information regarding copyright ownership. -* The ASF licenses this file to You under the Apache License, Version 2.0 -* (the "License"); you may not use this file except in compliance with -* the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ {{#packageName}} package {{packageName}}; @@ -47,7 +47,6 @@ import java.util.function.BiConsumer; import java.util.concurrent.CompletableFuture; public final class {{className}} { - public static final String SERVICE_NAME = {{interfaceClassName}}.SERVICE_NAME; private static final StubServiceDescriptor serviceDescriptor = new StubServiceDescriptor(SERVICE_NAME,{{interfaceClassName}}.class); @@ -64,7 +63,6 @@ public final class {{className}} { public static {{interfaceClassName}} newStub(Invoker invoker) { return new {{interfaceClassName}}Stub((Invoker<{{interfaceClassName}}>)invoker); } - {{#unaryMethods}} {{#javaDoc}} {{{javaDoc}}} @@ -84,7 +82,6 @@ public final class {{className}} { obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/unaryMethods}} - {{#serverStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} @@ -94,7 +91,6 @@ public final class {{className}} { obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/serverStreamingMethods}} - {{#clientStreamingMethods}} {{#javaDoc}} {{{javaDoc}}} @@ -104,7 +100,6 @@ public final class {{className}} { obj -> ((Message) obj).toByteArray(), obj -> ((Message) obj).toByteArray(), {{inputType}}::parseFrom, {{outputType}}::parseFrom); {{/clientStreamingMethods}} - {{#biStreamingWithoutClientStreamMethods}} {{#javaDoc}} {{{javaDoc}}} @@ -142,52 +137,32 @@ public final class {{className}} { public void $destroy() { invoker.destroy(); } - {{#unaryMethods}} - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} @Override public {{outputType}} {{methodName}}({{inputType}} request){ return StubInvocationUtil.unaryCall(invoker, {{methodName}}Method, request); } - public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){ return StubInvocationUtil.unaryCall(invoker, {{methodName}}AsyncMethod, request); } - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ StubInvocationUtil.unaryCall(invoker, {{methodName}}Method , request, responseObserver); } {{/unaryMethods}} - {{#serverStreamingMethods}} - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} @Override public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ StubInvocationUtil.serverStreamCall(invoker, {{methodName}}Method , request, responseObserver); } {{/serverStreamingMethods}} - {{#biStreamingWithoutClientStreamMethods}} - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ return StubInvocationUtil.biOrClientStreamCall(invoker, {{methodName}}Method , responseObserver); } {{/biStreamingWithoutClientStreamMethods}} - {{#clientStreamingMethods}} - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ return StubInvocationUtil.biOrClientStreamCall(invoker, {{methodName}}Method , responseObserver); @@ -196,7 +171,6 @@ public final class {{className}} { } public static abstract class {{interfaceClassName}}ImplBase implements {{interfaceClassName}}, ServerService<{{interfaceClassName}}> { - private BiConsumer> syncToAsync(java.util.function.Function syncFun) { return new BiConsumer>() { @Override @@ -211,7 +185,6 @@ public final class {{className}} { } }; } - {{#unaryMethods}} @Override public CompletableFuture<{{outputType}}> {{methodName}}Async({{inputType}} request){ @@ -219,10 +192,8 @@ public final class {{className}} { } {{/unaryMethods}} - /** - * This server stream type unary method is only used for generated stub to support async unary method. - * It will not be called if you are NOT using Dubbo3 generated triple stub and DO NOT implement this method. - */ + // This server stream type unary method is only used for generated stub to support async unary method. + // It will not be called if you are NOT using Dubbo3 generated triple stub and DO NOT implement this method. {{#unaryMethods}} public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ {{methodName}}Async(request).whenComplete((r, t) -> { @@ -242,68 +213,50 @@ public final class {{className}} { .getExtensionLoader(PathResolver.class) .getDefaultExtension(); Map> handlers = new HashMap<>(); - {{#methods}} pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}"); pathResolver.addNativeStub( "/" + SERVICE_NAME + "/{{originMethodName}}Async"); // for compatibility pathResolver.addNativeStub( "/" + JAVA_SERVICE_NAME + "/{{originMethodName}}"); pathResolver.addNativeStub( "/" + JAVA_SERVICE_NAME + "/{{originMethodName}}Async"); - {{/methods}} - {{#unaryMethods}} BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}Func = this::{{methodName}}; handlers.put({{methodName}}Method.getMethodName(), new UnaryStubMethodHandler<>({{methodName}}Func)); BiConsumer<{{inputType}}, StreamObserver<{{outputType}}>> {{methodName}}AsyncFunc = syncToAsync(this::{{methodName}}); handlers.put({{methodName}}ProxyAsyncMethod.getMethodName(), new UnaryStubMethodHandler<>({{methodName}}AsyncFunc)); {{/unaryMethods}} - {{#serverStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new ServerStreamMethodHandler<>(this::{{methodName}})); {{/serverStreamingMethods}} - {{#clientStreamingMethods}} handlers.put({{methodName}}Method.getMethodName(), new BiStreamMethodHandler<>(this::{{methodName}})); {{/clientStreamingMethods}} - {{#biStreamingWithoutClientStreamMethods}} handlers.put({{methodName}}Method.getMethodName(), new BiStreamMethodHandler<>(this::{{methodName}})); {{/biStreamingWithoutClientStreamMethods}} return new StubInvoker<>(this, url, {{interfaceClassName}}.class, handlers); } - - {{#unaryMethods}} @Override public {{outputType}} {{methodName}}({{inputType}} request){ throw unimplementedMethodException({{methodName}}Method); } - {{/unaryMethods}} - {{#serverStreamingMethods}} - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} @Override public void {{methodName}}({{inputType}} request, StreamObserver<{{outputType}}> responseObserver){ throw unimplementedMethodException({{methodName}}Method); } {{/serverStreamingMethods}} - {{#biStreamingWithoutClientStreamMethods}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ throw unimplementedMethodException({{methodName}}Method); } {{/biStreamingWithoutClientStreamMethods}} - {{#clientStreamingMethods}} - {{#javaDoc}} - {{{javaDoc}}} - {{/javaDoc}} @Override public StreamObserver<{{inputType}}> {{methodName}}(StreamObserver<{{outputType}}> responseObserver){ throw unimplementedMethodException({{methodName}}Method); @@ -319,5 +272,4 @@ public final class {{className}} { "/" + serviceDescriptor.getInterfaceName() + "/" + methodDescriptor.getMethodName())).asException(); } } - } diff --git a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/annotations/GRequest.java b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/annotations/GRequest.java new file mode 100644 index 00000000000..4e6144f908f --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/stub/annotations/GRequest.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.stub.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface GRequest { + String value() default ""; +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/GRequestArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/GRequestArgumentResolver.java new file mode 100644 index 00000000000..7651892c6b5 --- /dev/null +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/GRequestArgumentResolver.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.rpc.protocol.tri.rest.support.basic; + +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.common.utils.JsonUtils; +import org.apache.dubbo.remoting.http12.HttpRequest; +import org.apache.dubbo.remoting.http12.HttpResponse; +import org.apache.dubbo.remoting.http12.exception.DecodeException; +import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder; +import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants; +import org.apache.dubbo.rpc.protocol.tri.rest.argument.AnnotationBaseArgumentResolver; +import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta; +import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta; +import org.apache.dubbo.rpc.stub.annotations.GRequest; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +@Activate +public class GRequestArgumentResolver implements AnnotationBaseArgumentResolver { + + @Override + public Class accept() { + return GRequest.class; + } + + @Override + public Object resolve( + ParameterMeta parameter, AnnotationMeta annotation, HttpRequest request, HttpResponse response) { + HttpMessageDecoder decoder = request.attribute(RestConstants.BODY_DECODER_ATTRIBUTE); + if (decoder == null) { + return null; + } + + Map value = new HashMap<>(); + Map variableMap = request.attribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + if (variableMap != null) { + value.putAll(variableMap); + } + + InputStream is = request.inputStream(); + try { + int available = is.available(); + if (available > 0) { + Object body = decoder.decode(is, Object.class, request.charsetOrDefault()); + if (body instanceof Map) { + @SuppressWarnings("unchecked") + Map bodyMap = (Map) body; + String key = annotation.getValue(); + if ("*".equals(key) || key.isEmpty()) { + value.putAll(bodyMap); + } else { + value.put(key, bodyMap.get(key)); + } + } + } + } catch (IOException e) { + throw new DecodeException("Error reading input", e); + } + + return decoder.decode( + new ByteArrayInputStream(JsonUtils.toJson(value).getBytes(StandardCharsets.UTF_8)), + parameter.getType(), + StandardCharsets.UTF_8); + } +} diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver index 0486f00884a..829c373182f 100644 --- a/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver +++ b/dubbo-rpc/dubbo-rpc-triple/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver @@ -1,3 +1,4 @@ misc=org.apache.dubbo.rpc.protocol.tri.rest.argument.MiscArgumentResolver basic-param=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.ParamArgumentResolver basic-fallback=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.FallbackArgumentResolver +grpc-request=org.apache.dubbo.rpc.protocol.tri.rest.support.basic.GRequestArgumentResolver