You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2024/07/29 02:54:09 UTC
(dubbo) branch 3.3 updated: Generic param support (#14471)
This is an automated email from the ASF dual-hosted git repository.
albumenj pushed a commit to branch 3.3
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.3 by this push:
new e286565597 Generic param support (#14471)
e286565597 is described below
commit e2865655971690d6c0a74da9b5d8c3e9336ecca5
Author: Sean Yang <ox...@gmail.com>
AuthorDate: Mon Jul 29 10:54:03 2024 +0800
Generic param support (#14471)
* Generic param support
* OPTIONS request support
* Fix yml constructor
---
.../org/apache/dubbo/common/utils/MethodUtils.java | 15 +++
.../rest/support/jaxrs/BodyArgumentResolver.java | 2 +-
.../support/jaxrs/FallbackArgumentResolver.java | 6 +-
.../support/jaxrs/JaxrsRequestMappingResolver.java | 3 +-
.../tri/rest/support/jaxrs/JaxrsRestToolKit.java | 2 +-
.../jaxrs/filter/InterceptorContextImpl.java | 2 +-
.../spring/MatrixVariableArgumentResolver.java | 2 +-
.../spring/RequestBodyArgumentResolver.java | 6 +-
.../spring/SpringMvcRequestMappingResolver.java | 19 ++-
.../tri/rest/support/spring/SpringRestToolKit.java | 47 +++++--
.../rest/support/spring/RestProtocolTest.groovy | 142 ++++++++++++++++-----
.../support/spring/service/SpringDemoService.java | 64 +++++++++-
.../spring/service/SpringDemoServiceImpl.java | 10 +-
.../src/test/resources/log4j2-test.xml | 1 +
.../apache/dubbo/remoting/http12/HttpMethods.java | 4 +-
.../http12/message/HttpMessageDecoder.java | 16 +++
.../remoting/http12/message/codec/JsonCodec.java | 12 ++
.../remoting/http12/message/codec/JsonPbCodec.java | 8 ++
.../remoting/http12/message/codec/YamlCodec.java | 19 ++-
.../apache/dubbo/remoting/http12/rest/Mapping.java | 2 +
.../tri/h12/HttpMessageDecoderWrapper.java | 11 ++
.../dubbo/rpc/protocol/tri/rest/Messages.java | 4 +-
.../protocol/tri/rest/RestHttpMessageCodec.java | 17 ++-
.../tri/rest/argument/GeneralTypeConverter.java | 49 ++++++-
.../NamedValueArgumentResolverSupport.java | 17 +--
.../mapping/DefaultRequestMappingRegistry.java | 30 ++++-
.../protocol/tri/rest/mapping/RequestMapping.java | 12 ++
.../rest/mapping/RestRequestHandlerMapping.java | 40 +++++-
.../rest/mapping/condition/MethodsCondition.java | 15 ++-
.../mapping/condition/ServiceVersionCondition.java | 2 +-
.../tri/rest/mapping/meta/AnnotationSupport.java | 2 +-
.../protocol/tri/rest/mapping/meta/BeanMeta.java | 2 +-
.../protocol/tri/rest/mapping/meta/MethodMeta.java | 61 ++++++++-
.../tri/rest/mapping/meta/MethodParameterMeta.java | 9 --
.../tri/rest/mapping/meta/NamedValueMeta.java | 13 ++
.../tri/rest/mapping/meta/ParameterMeta.java | 26 ++--
.../support/basic/BasicRequestMappingResolver.java | 17 ++-
.../support/basic/FallbackArgumentResolver.java | 66 ++++++++--
.../rest/support/basic/ParamArgumentResolver.java | 8 +-
.../tri/rest/util/AbstractRestToolKit.java | 13 +-
.../rpc/protocol/tri/rest/util/MethodWalker.java | 3 -
.../rpc/protocol/tri/rest/util/RequestUtils.java | 110 ++++++----------
.../tri/rest/support/basic/RestProtocolTest.groovy | 41 +++++-
.../rpc/protocol/tri/rest/service/DemoService.java | 21 +++
.../protocol/tri/rest/service/DemoServiceImpl.java | 43 +++++++
45 files changed, 784 insertions(+), 230 deletions(-)
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java
index a4b829ce45..a826cd1df5 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java
@@ -462,4 +462,19 @@ public interface MethodUtils {
return -1;
}
}
+
+ static String toShortString(Method method) {
+ StringBuilder sb = new StringBuilder(64);
+ sb.append(method.getDeclaringClass().getName());
+ sb.append('.').append(method.getName()).append('(');
+ Class<?>[] parameterTypes = method.getParameterTypes();
+ for (int i = 0, len = parameterTypes.length; i < len; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(parameterTypes[i].getSimpleName());
+ }
+ sb.append(')');
+ return sb.toString();
+ }
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BodyArgumentResolver.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BodyArgumentResolver.java
index 5d46134103..fc3640aa10 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BodyArgumentResolver.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/BodyArgumentResolver.java
@@ -51,6 +51,6 @@ public class BodyArgumentResolver implements AnnotationBaseArgumentResolver<Anno
throw new RestException(e);
}
}
- return RequestUtils.decodeBody(request, type);
+ return RequestUtils.decodeBody(request, parameter.getActualGenericType());
}
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FallbackArgumentResolver.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FallbackArgumentResolver.java
index 0ecc924df7..07076ba08f 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FallbackArgumentResolver.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/FallbackArgumentResolver.java
@@ -45,7 +45,7 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
@Override
protected Object resolveValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
- Object value = RequestUtils.decodeBody(request, meta.type());
+ Object value = RequestUtils.decodeBody(request, meta.genericType());
if (value != null) {
return value;
}
@@ -65,7 +65,7 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
throw new RestException(e);
}
}
- Object value = RequestUtils.decodeBody(request, meta.type());
+ Object value = RequestUtils.decodeBody(request, meta.genericType());
if (value != null) {
return value;
}
@@ -77,7 +77,7 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
@Override
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
- Object value = RequestUtils.decodeBody(request, meta.type());
+ Object value = RequestUtils.decodeBody(request, meta.genericType());
if (value != null) {
return value;
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRequestMappingResolver.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRequestMappingResolver.java
index 72cb325351..f9fc66768e 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRequestMappingResolver.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRequestMappingResolver.java
@@ -22,7 +22,6 @@ import org.apache.dubbo.rpc.protocol.tri.rest.cors.CorsUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver;
-import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ServiceVersionCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationSupport;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta;
@@ -76,7 +75,7 @@ public class JaxrsRequestMappingResolver implements RequestMappingResolver {
return builder(methodMeta, path, httpMethod)
.name(methodMeta.getMethod().getName())
.contextPath(methodMeta.getServiceMeta().getContextPath())
- .custom(new ServiceVersionCondition(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()))
+ .service(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion())
.cors(globalCorsMeta)
.build();
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java
index 56c3365e53..4ce80939a8 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/JaxrsRestToolKit.java
@@ -54,7 +54,7 @@ final class JaxrsRestToolKit extends AbstractRestToolKit {
}
ParamConverter converter = paramConverterFactory.getParamConverter(
- parameter.getType(), parameter.getGenericType(), parameter.getRealAnnotations());
+ parameter.getType(), parameter.getGenericType(), parameter.getRawAnnotations());
if (converter != null) {
return value instanceof String ? converter.fromString((String) value) : converter.toString(value);
}
diff --git a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/InterceptorContextImpl.java b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/InterceptorContextImpl.java
index ee67447bb5..1f64c21f9e 100644
--- a/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/InterceptorContextImpl.java
+++ b/dubbo-plugin/dubbo-rest-jaxrs/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/jaxrs/filter/InterceptorContextImpl.java
@@ -56,7 +56,7 @@ public abstract class InterceptorContextImpl implements InterceptorContext {
@Override
public Annotation[] getAnnotations() {
- return getHandler().getMethod().getRealAnnotations();
+ return getHandler().getMethod().getRawAnnotations();
}
@Override
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/MatrixVariableArgumentResolver.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/MatrixVariableArgumentResolver.java
index 1ca99a5cb0..f68ff9cacf 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/MatrixVariableArgumentResolver.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/MatrixVariableArgumentResolver.java
@@ -117,7 +117,7 @@ public class MatrixVariableArgumentResolver extends AbstractSpringArgumentResolv
private final String pathVar;
- public MatrixNamedValueMeta(String name, boolean required, String defaultValue, String pathVar) {
+ MatrixNamedValueMeta(String name, boolean required, String defaultValue, String pathVar) {
super(name, required, defaultValue);
this.pathVar = pathVar;
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestBodyArgumentResolver.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestBodyArgumentResolver.java
index 4bc2bf1401..dcf3173f35 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestBodyArgumentResolver.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RequestBodyArgumentResolver.java
@@ -50,7 +50,7 @@ public class RequestBodyArgumentResolver extends AbstractSpringArgumentResolver
}
return meta.parameterMeta().bind(request, response);
}
- return RequestUtils.decodeBody(request, meta.type());
+ return RequestUtils.decodeBody(request, meta.genericType());
}
@Override
@@ -66,7 +66,7 @@ public class RequestBodyArgumentResolver extends AbstractSpringArgumentResolver
if (RequestUtils.isFormOrMultiPart(request)) {
return request.formParameterValues(meta.name());
}
- return RequestUtils.decodeBody(request, meta.type());
+ return RequestUtils.decodeBody(request, meta.genericType());
}
@Override
@@ -74,6 +74,6 @@ public class RequestBodyArgumentResolver extends AbstractSpringArgumentResolver
if (RequestUtils.isFormOrMultiPart(request)) {
return RequestUtils.getFormParametersMap(request);
}
- return RequestUtils.decodeBody(request, meta.type());
+ return RequestUtils.decodeBody(request, meta.genericType());
}
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java
index 728156a98d..bdb3aff019 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringMvcRequestMappingResolver.java
@@ -24,7 +24,6 @@ import org.apache.dubbo.rpc.protocol.tri.rest.cors.CorsUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver;
-import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ServiceVersionCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
@@ -70,9 +69,11 @@ public class SpringMvcRequestMappingResolver implements RequestMappingResolver {
String[] methods = requestMapping == null
? httpExchange.getStringArray("method")
: requestMapping.getStringArray("method");
+ String[] paths = requestMapping == null ? httpExchange.getValueArray() : requestMapping.getValueArray();
return builder(requestMapping, httpExchange, serviceMeta.findMergedAnnotation(Annotations.ResponseStatus))
.method(methods)
.name(serviceMeta.getType().getSimpleName())
+ .path(paths)
.contextPath(serviceMeta.getContextPath())
.cors(buildCorsMeta(serviceMeta.findMergedAnnotation(Annotations.CrossOrigin), methods))
.build();
@@ -92,14 +93,20 @@ public class SpringMvcRequestMappingResolver implements RequestMappingResolver {
}
ServiceMeta serviceMeta = methodMeta.getServiceMeta();
+ String name = methodMeta.getMethod().getName();
String[] methods = requestMapping == null
? httpExchange.getStringArray("method")
: requestMapping.getStringArray("method");
+ String[] paths = requestMapping == null ? httpExchange.getValueArray() : requestMapping.getValueArray();
+ if (paths.length == 0) {
+ paths = new String[] {'/' + name};
+ }
return builder(requestMapping, httpExchange, methodMeta.findMergedAnnotation(Annotations.ResponseStatus))
.method(methods)
- .name(methodMeta.getMethod().getName())
+ .name(name)
+ .path(paths)
.contextPath(serviceMeta.getContextPath())
- .custom(new ServiceVersionCondition(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()))
+ .service(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion())
.cors(buildCorsMeta(methodMeta.findMergedAnnotation(Annotations.CrossOrigin), methods))
.build();
}
@@ -116,12 +123,10 @@ public class SpringMvcRequestMappingResolver implements RequestMappingResolver {
}
}
if (requestMapping == null) {
- return builder.path(httpExchange.getValueArray())
- .consume(httpExchange.getStringArray("contentType"))
+ return builder.consume(httpExchange.getStringArray("contentType"))
.produce(httpExchange.getStringArray("accept"));
}
- return builder.path(requestMapping.getValueArray())
- .param(requestMapping.getStringArray("params"))
+ return builder.param(requestMapping.getStringArray("params"))
.header(requestMapping.getStringArray("headers"))
.consume(requestMapping.getStringArray("consumes"))
.produce(requestMapping.getStringArray("produces"));
diff --git a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
index a2638c7748..e8c75ae6c8 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/SpringRestToolKit.java
@@ -16,12 +16,17 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.support.spring;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.spring.extension.SpringExtensionInjector;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodParameterMeta;
@@ -49,6 +54,9 @@ import org.springframework.util.PropertyPlaceholderHelper;
final class SpringRestToolKit implements RestToolKit {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SpringRestToolKit.class);
+
+ private final Map<MethodParameterMeta, TypeDescriptor> cache = CollectionUtils.newConcurrentHashMap();
private final ConfigurableBeanFactory beanFactory;
private final PropertyPlaceholderHelper placeholderHelper;
private final ConfigurationWrapper configuration;
@@ -98,23 +106,42 @@ final class SpringRestToolKit implements RestToolKit {
@Override
public Object convert(Object value, ParameterMeta parameter) {
- if (value instanceof Collection) {
+ boolean tried = false;
+ if (value instanceof Collection || value instanceof Map) {
+ tried = true;
Object target = typeConverter.convert(value, parameter.getGenericType());
if (target != null) {
return target;
}
}
- TypeDescriptor targetType = (TypeDescriptor) parameter.getTypeDescriptor();
- if (targetType == null) {
- MethodParameterMeta meta = (MethodParameterMeta) parameter;
- targetType = new TypeDescriptor(new MethodParameter(meta.getMethod(), meta.getIndex()));
- parameter.setTypeDescriptor(targetType);
+ if (parameter instanceof MethodParameterMeta) {
+ TypeDescriptor targetType = cache.computeIfAbsent(
+ (MethodParameterMeta) parameter,
+ k -> new TypeDescriptor(new MethodParameter(k.getMethod(), k.getIndex())));
+ TypeDescriptor sourceType = TypeDescriptor.forObject(value);
+ if (conversionService.canConvert(sourceType, targetType)) {
+ try {
+ return conversionService.convert(value, sourceType, targetType);
+ } catch (Throwable t) {
+ LOGGER.debug(
+ "Spring convert value '{}' from type [{}] to type [{}] failed",
+ value,
+ value.getClass(),
+ parameter.getGenericType(),
+ t);
+ }
+ }
}
- TypeDescriptor sourceType = TypeDescriptor.forObject(value);
- if (conversionService.canConvert(sourceType, targetType)) {
- return conversionService.convert(value, sourceType, targetType);
+ Object target = tried ? null : typeConverter.convert(value, parameter.getGenericType());
+ if (target == null && value != null) {
+ throw new RestException(
+ Messages.ARGUMENT_CONVERT_ERROR,
+ parameter.getName(),
+ value,
+ value.getClass(),
+ parameter.getGenericType());
}
- return typeConverter.convert(value, parameter.getGenericType());
+ return target;
}
@Override
diff --git a/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy b/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy
index ec1935b122..88b222a337 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/RestProtocolTest.groovy
@@ -17,56 +17,138 @@
package org.apache.dubbo.rpc.protocol.tri.rest.support.spring
-import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl
+import org.apache.dubbo.remoting.http12.message.MediaType
+import org.apache.dubbo.rpc.protocol.tri.rest.service.Book
import org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service.SpringDemoServiceImpl
+import org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service.SpringDemoService
import org.apache.dubbo.rpc.protocol.tri.rest.test.BaseServiceTest
+import org.apache.dubbo.rpc.protocol.tri.test.TestRequest
import org.apache.dubbo.rpc.protocol.tri.test.TestRunnerBuilder
class RestProtocolTest extends BaseServiceTest {
@Override
void setupService(TestRunnerBuilder builder) {
- builder.provider(new DemoServiceImpl())
- builder.provider(new SpringDemoServiceImpl())
+ builder.provider(SpringDemoService.class, new SpringDemoServiceImpl())
}
def "hello world"() {
expect:
runner.get(path) == output
where:
- path | output
- '/hello?name=world' | 'hello world'
- '/hello/?name=world' | 'hello world'
- '/hello.yml?name=world' | 'hello world'
+ path | output
+ '/spring/hello?name=world' | 'hello world'
+ '/spring/hello/?name=world' | 'hello world'
+ '/spring/hello.yml?name=world' | 'hello world'
+ }
+
+ def "list argument body test"() {
+ expect:
+ runner.post(path, body) contains output
+ where:
+ path | body | output
+ '/spring/listArgBodyTest?age=2' | '[1,2]' | '[1,2]'
+ }
+
+ def "map argument body test"() {
+ expect:
+ runner.post(path, body) contains output
+ where:
+ path | body | output
+ '/spring/mapArgBodyTest?age=2' | '{"1":["2",3],"4":[5,"6"]}' | '{4:[5,6],1:[2,3]}'
+ }
+
+ def "bean argument get test"() {
+ expect:
+ runner.get(path, Book.class).price == output
+ where:
+ path | output
+ '/spring/beanArgTest' | 0
+ '/spring/beanArgTest?quote=5' | 5
+ }
+
+ def "bean argument post test"() {
+ expect:
+ runner.post(path, body, Book.class).price == output
+ where:
+ path | body | output
+ '/spring/beanArgTest' | [:] | 0
+ '/spring/beanArgTest' | [price: 6] | 6
+ '/spring/beanArgTest2' | '{"price": 5}' | 5
}
def "spring bean argument test"() {
expect:
runner.get(path) contains output
where:
- path | output
- '/springBean?id=1&name=sam' | '"id":1,"name":"sam"'
- '/springBean?name=sam&phone=123&email=a@b.com' | '"email":"a@b.com","name":"sam","phone":"123"'
- '/springBean?group.name=g1&group.owner.name=jack' | '"group":{"id":0,"name":"g1","owner":{"name":"jack"'
- '/springBean?group.parent.parent.children[0].name=xx' | '"group":{"id":0,"parent":{"id":0,"parent":{"children":[{"id":0,"name":"xx"}],"id":0}}}'
- '/springBean?ids=3&ids=4' | '"ids":[3,4]'
- '/springBean?ids[]=3&ids[]=4' | '"ids":[3,4]'
- '/springBean?ids[1]=3&ids[2]=4' | '"ids":[0,3,4]'
- '/springBean?scores=3&scores=4' | '"scores":[3,4]'
- '/springBean?scores[]=3&scores[]=4' | '"scores":[3,4]'
- '/springBean?scores[1]=3&scores[2]=4' | '"scores":[null,3,4]'
- '/springBean?tags[0].name=a&tags[0].value=b&tags[1].name=c&tags[1].value=d' | '"tags":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
- '/springBean?tagsA[0].name=a&tagsA[0].value=b&tagsA[1].name=c&tagsA[1].value=d' | '"tagsA":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
- '/springBean?tagsB[0].name=e&tagsB[1].name=c&tagsB[1].value=d' | '"tagsB":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
- '/springBean?tagsC[0].name=e&tagsC[1].name=c&tagsC[1].value=d' | '"tagsC":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
- '/springBean?groupMaps[0].one.name=a&groupMaps[1].two.name=b' | '"groupMaps":[{},{}]' //x
- '/springBean?id=1&features.a=xx&features.b=2' | '"features":{},"id":1' //x
- '/springBean?id=1&features[a]=xx&features[b]=2' | '"features":{"a":"xx","b":"2"}'
- '/springBean?group.id=2&group.features.a=1&group.features.b=xx' | '"group":{"features":{},"id":2}' //x
- '/springBean?tagMap.a.name=a&tagMap.a.value=b&tagMap.b.name=c&tagMap.b.value=d' | '"tagMap":{}' //x
- '/springBean?tagMapA.a.name=e&tagMapA.b.name=c&tagMapA.b.value=d' | '"tagMapA":{"a":{"name":"a","value":"b"}}' //x
- '/springBean?tagMapB[2].name=a&tagMapB[2].value=b&tagMapB[3].name=c' | '"tagMapB":{2:{"name":"a","value":"b"},3:{"name":"c"}}'
- '/springBean?groupsMap.one[0].name=a&groupsMap.one[1].name=b&groupsMap.two[1].name=c' | '"groupsMap":{}' //x
+ path | output
+ '/spring/bean?id=1&name=sam' | '"id":1,"name":"sam"'
+ '/spring/bean?name=sam&phone=123&email=a@b.com' | '"email":"a@b.com","name":"sam","phone":"123"'
+ '/spring/bean?group.name=g1&group.owner.name=jack' | '"group":{"id":0,"name":"g1","owner":{"name":"jack"'
+ '/spring/bean?group.parent.parent.children[0].name=xx' | '"group":{"id":0,"parent":{"id":0,"parent":{"children":[{"id":0,"name":"xx"}],"id":0}}}'
+ '/spring/bean?ids=3&ids=4' | '"ids":[3,4]'
+ '/spring/bean?ids[]=3&ids[]=4' | '"ids":[3,4]'
+ '/spring/bean?ids[1]=3&ids[2]=4' | '"ids":[0,3,4]'
+ '/spring/bean?scores=3&scores=4' | '"scores":[3,4]'
+ '/spring/bean?scores[]=3&scores[]=4' | '"scores":[3,4]'
+ '/spring/bean?scores[1]=3&scores[2]=4' | '"scores":[null,3,4]'
+ '/spring/bean?tags[0].name=a&tags[0].value=b&tags[1].name=c&tags[1].value=d' | '"tags":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
+ '/spring/bean?tagsA[0].name=a&tagsA[0].value=b&tagsA[1].name=c&tagsA[1].value=d' | '"tagsA":[{"name":"a","value":"b"},{"name":"c","value":"d"}]'
+ '/spring/bean?tagsB[0].name=e&tagsB[1].name=c&tagsB[1].value=d' | '"tagsB":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
+ '/spring/bean?tagsC[0].name=e&tagsC[1].name=c&tagsC[1].value=d' | '"tagsC":[{"name":"e","value":"b"},{"name":"c","value":"d"}]'
+ '/spring/bean?id=1&features[a]=xx&features[b]=2' | '"features":{"a":"xx","b":"2"}'
+ '/spring/bean?tagMapB[2].name=a&tagMapB[2].value=b&tagMapB[3].name=c' | '"tagMapB":{2:{"name":"a","value":"b"},3:{"name":"c"}}'
+ }
+
+ def "bean body test"() {
+ expect:
+ runner.post(path, body) contains output
+ where:
+ path | body | output
+ '/spring/beanBodyTest?age=2' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]'
+ }
+
+ def "urlEncodeForm test"() {
+ given:
+ def request = new TestRequest(
+ path: path,
+ contentType: MediaType.APPLICATION_FROM_URLENCODED,
+ params: [
+ 'name': 'Sam',
+ 'age' : 8
+ ]
+ )
+ expect:
+ runner.post(request) == output
+ where:
+ path | output
+ '/spring/argTest' | 'Sam is 8 years old'
+ }
+
+ def "no interface method test"() {
+ expect:
+ runner.get(path) contains output
+ where:
+ path | output
+ '/spring/noInterface' | 'ok'
+ '/spring/noInterfaceAndMapping' | '404'
+ }
+
+ def "use interface argument name test"() {
+ expect:
+ runner.get(path) contains output
+ where:
+ path | output
+ '/spring/argNameTest?name=Sam' | 'Sam'
+ }
+
+ def "pb server stream test"() {
+ expect:
+ runner.posts(path, body).size() == output
+ where:
+ path | body | output
+ '/spring/pbServerStream' | '{"service": "3"}' | 3
+ '/spring/pbServerStream' | '{}' | 0
}
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java
index bdf66f566a..4d64b125ab 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoService.java
@@ -16,12 +16,70 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service;
+import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.Book;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.Group;
import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
-import org.springframework.web.bind.annotation.GetMapping;
+import java.util.List;
+import java.util.Map;
+import io.grpc.health.v1.HealthCheckRequest;
+import io.grpc.health.v1.HealthCheckResponse;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+@RequestMapping("/spring")
public interface SpringDemoService {
- @GetMapping("/springBean")
- UserEx beanArgTest(UserEx user);
+ @RequestMapping
+ String hello(String name);
+
+ @RequestMapping
+ String argTest(String name, int age);
+
+ @RequestMapping
+ Book beanArgTest(@RequestBody(required = false) Book book, Integer quote);
+
+ @RequestMapping
+ Book beanArgTest2(@RequestBody Book book);
+
+ @RequestMapping("/bean")
+ UserEx advanceBeanArgTest(UserEx user);
+
+ @RequestMapping
+ List<Integer> listArgBodyTest(@RequestBody List<Integer> list, int age);
+
+ @RequestMapping
+ List<Integer> listArgBodyTest2(List<Integer> list, int age);
+
+ @RequestMapping
+ Map<Integer, List<Long>> mapArgBodyTest(@RequestBody Map<Integer, List<Long>> map, int age);
+
+ @RequestMapping
+ Map<Integer, List<Long>> mapArgBodyTest2(Map<Integer, List<Long>> map, int age);
+
+ @RequestMapping
+ List<Group> beanBodyTest(@RequestBody List<Group> groups, int age);
+
+ @RequestMapping
+ List<Group> beanBodyTest2(List<Group> groups, int age);
+
+ @RequestMapping
+ Book buy(Book book);
+
+ @RequestMapping("/buy2")
+ Book buy(Book book, int count);
+
+ @RequestMapping
+ String say(String name, Long count);
+
+ @RequestMapping
+ String say(String name);
+
+ @RequestMapping
+ String argNameTest(String name);
+
+ @RequestMapping
+ void pbServerStream(@RequestBody HealthCheckRequest request, StreamObserver<HealthCheckResponse> responseObserver);
}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java
index 97ef3cfe71..e2954d27e5 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/support/spring/service/SpringDemoServiceImpl.java
@@ -16,12 +16,6 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.support.spring.service;
-import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.DemoServiceImpl;
-public class SpringDemoServiceImpl implements SpringDemoService {
-
- @Override
- public UserEx beanArgTest(UserEx user) {
- return user;
- }
-}
+public class SpringDemoServiceImpl extends DemoServiceImpl implements SpringDemoService {}
diff --git a/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml b/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml
index 91337f333b..8fff1d4ace 100644
--- a/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml
+++ b/dubbo-plugin/dubbo-rest-spring/src/test/resources/log4j2-test.xml
@@ -24,6 +24,7 @@
<Loggers>
<Logger name="org.apache.dubbo.rpc.protocol.tri.stream.StreamUtils" level="error"/>
<Logger name="org.apache.dubbo.rpc.protocol.tri.h12" level="debug"/>
+ <Logger name="org.apache.dubbo.rpc.protocol.tri.rest" level="debug"/>
<Root level="info">
<AppenderRef ref="Console"/>
</Root>
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java
index 8cdf0e9f73..4792ccd9bd 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/HttpMethods.java
@@ -20,12 +20,12 @@ import java.nio.charset.StandardCharsets;
public enum HttpMethods {
GET,
+ HEAD,
POST,
PUT,
+ PATCH,
DELETE,
- HEAD,
OPTIONS,
- PATCH,
TRACE;
public static final byte[][] HTTP_METHODS_BYTES;
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java
index 448646bb36..53c4e84779 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/HttpMessageDecoder.java
@@ -20,6 +20,8 @@ import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.remoting.http12.exception.DecodeException;
import java.io.InputStream;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
import java.nio.charset.Charset;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -28,6 +30,16 @@ public interface HttpMessageDecoder extends CodecMediaType {
Object decode(InputStream inputStream, Class<?> targetType, Charset charset) throws DecodeException;
+ default Object decode(InputStream inputStream, Type targetType, Charset charset) throws DecodeException {
+ if (targetType instanceof Class) {
+ return decode(inputStream, (Class<?>) targetType, charset);
+ }
+ if (targetType instanceof ParameterizedType) {
+ return decode(inputStream, (Class<?>) ((ParameterizedType) targetType).getRawType(), charset);
+ }
+ throw new DecodeException("targetType " + targetType + " is not a class");
+ }
+
default Object[] decode(InputStream inputStream, Class<?>[] targetTypes, Charset charset) throws DecodeException {
return new Object[] {decode(inputStream, ArrayUtils.isEmpty(targetTypes) ? null : targetTypes[0], charset)};
}
@@ -36,6 +48,10 @@ public interface HttpMessageDecoder extends CodecMediaType {
return decode(inputStream, targetType, UTF_8);
}
+ default Object decode(InputStream inputStream, Type targetType) throws DecodeException {
+ return decode(inputStream, targetType, UTF_8);
+ }
+
default Object[] decode(InputStream inputStream, Class<?>[] targetTypes) throws DecodeException {
return decode(inputStream, targetTypes, UTF_8);
}
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java
index edeee5e01b..32281439cc 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonCodec.java
@@ -26,6 +26,7 @@ import org.apache.dubbo.remoting.http12.message.MediaType;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.List;
@@ -64,6 +65,17 @@ public class JsonCodec implements HttpMessageCodec {
}
}
+ @Override
+ public Object decode(InputStream is, Type targetType, Charset charset) throws DecodeException {
+ try {
+ return JsonUtils.toJavaObject(StreamUtils.toString(is, charset), targetType);
+ } catch (HttpStatusException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new DecodeException("Error decoding json", t);
+ }
+ }
+
@Override
public Object[] decode(InputStream is, Class<?>[] targetTypes, Charset charset) throws DecodeException {
try {
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java
index a3b2dbdea4..bb87ba882e 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/JsonPbCodec.java
@@ -25,6 +25,7 @@ import org.apache.dubbo.remoting.http12.exception.HttpStatusException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.reflect.Type;
import java.nio.charset.Charset;
import com.google.protobuf.Message;
@@ -63,6 +64,13 @@ public final class JsonPbCodec extends JsonCodec {
return super.decode(is, targetType, charset);
}
+ @Override
+ public Object decode(InputStream is, Type targetType, Charset charset) throws DecodeException {
+ return targetType instanceof Class
+ ? decode(is, (Class<?>) targetType, charset)
+ : super.decode(is, targetType, charset);
+ }
+
@Override
public Object[] decode(InputStream is, Class<?>[] targetTypes, Charset charset) throws DecodeException {
try {
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/YamlCodec.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/YamlCodec.java
index 9362bcaf8b..01cce80e43 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/YamlCodec.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/message/codec/YamlCodec.java
@@ -18,6 +18,7 @@ package org.apache.dubbo.remoting.http12.message.codec;
import org.apache.dubbo.common.utils.ClassUtils;
import org.apache.dubbo.common.utils.DefaultSerializeClassChecker;
+import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.remoting.http12.exception.DecodeException;
import org.apache.dubbo.remoting.http12.exception.EncodeException;
import org.apache.dubbo.remoting.http12.exception.HttpStatusException;
@@ -28,6 +29,7 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
+import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.Iterator;
@@ -51,10 +53,25 @@ public class YamlCodec implements HttpMessageCodec {
}
}
+ @Override
+ public Object decode(InputStream is, Type targetType, Charset charset) throws DecodeException {
+ if (targetType instanceof Class) {
+ return decode(is, (Class<?>) targetType, charset);
+ }
+
+ try (InputStreamReader reader = new InputStreamReader(is, charset)) {
+ return JsonUtils.convertObject(createYaml().load(reader), targetType);
+ } catch (HttpStatusException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new DecodeException("Error decoding yaml", t);
+ }
+ }
+
@Override
public Object[] decode(InputStream is, Class<?>[] targetTypes, Charset charset) throws DecodeException {
try (InputStreamReader reader = new InputStreamReader(is, charset)) {
- Yaml yaml = new Yaml();
+ Yaml yaml = createYaml();
Iterator<Object> iterator = yaml.loadAll(reader).iterator();
int len = targetTypes.length;
Object[] results = new Object[len];
diff --git a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
index a05e479306..0aea2292ed 100644
--- a/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
+++ b/dubbo-remoting/dubbo-remoting-http12/src/main/java/org/apache/dubbo/remoting/http12/rest/Mapping.java
@@ -42,4 +42,6 @@ public @interface Mapping {
String[] consumes() default {};
String[] produces() default {};
+
+ boolean disabled() default false;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageDecoderWrapper.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageDecoderWrapper.java
index b50a7c3d98..22bf82a8a9 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageDecoderWrapper.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/h12/HttpMessageDecoderWrapper.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder;
import org.apache.dubbo.remoting.http12.message.MediaType;
import java.io.InputStream;
+import java.lang.reflect.Type;
import java.nio.charset.Charset;
public final class HttpMessageDecoderWrapper implements HttpMessageDecoder {
@@ -43,6 +44,11 @@ public final class HttpMessageDecoderWrapper implements HttpMessageDecoder {
return httpMessageDecoder.decode(inputStream, targetType, this.charset);
}
+ @Override
+ public Object decode(InputStream inputStream, Type targetType, Charset charset) throws DecodeException {
+ return httpMessageDecoder.decode(inputStream, targetType, this.charset);
+ }
+
@Override
public Object[] decode(InputStream inputStream, Class<?>[] targetTypes, Charset charset) throws DecodeException {
return httpMessageDecoder.decode(inputStream, targetTypes, this.charset);
@@ -53,6 +59,11 @@ public final class HttpMessageDecoderWrapper implements HttpMessageDecoder {
return httpMessageDecoder.decode(inputStream, targetType, charset);
}
+ @Override
+ public Object decode(InputStream inputStream, Type targetType) throws DecodeException {
+ return httpMessageDecoder.decode(inputStream, targetType, charset);
+ }
+
@Override
public Object[] decode(InputStream inputStream, Class<?>[] targetTypes) throws DecodeException {
return httpMessageDecoder.decode(inputStream, targetTypes, charset);
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java
index 2abcff1220..3ded19a441 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/Messages.java
@@ -34,9 +34,9 @@ public enum Messages {
AMBIGUOUS_MAPPING("Ambiguous mapping for ''{0}'': [{1}, {2}]"),
EXTENSION_INIT_FAILED("Rest extension: ''{0}'' initialization failed for invoker: ''{1}''"),
ARGUMENT_NAME_MISSING("Name for argument of type [{0}] not specified, and parameter name information not "
- + "available via reflection. Ensure that the compiler uses the '-parameters' flag."),
+ + "available via reflection. Ensure that the compiler uses the '-parameters' flag"),
ARGUMENT_VALUE_MISSING("Missing argument ''{0}'' for method parameter of type [{1}]", 412),
- ARGUMENT_CONVERT_ERROR("Convert argument ''{0}'' value [{1}] from type [{2}] to type [{3}] error", 412),
+ ARGUMENT_CONVERT_ERROR("Could not convert argument ''{0}'' value ''{1}'' from type [{2}] to type [{3}]", 412),
ARGUMENT_COULD_NOT_RESOLVED("Could not resolve ''{0}'', no suitable resolver", 400),
ARGUMENT_BIND_ERROR("Bind argument ''{0}'' of type [{1}] error", 400),
INTERNAL_ERROR("Rest internal error");
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java
index 1451c8d2d1..cd6df6f4d6 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/RestHttpMessageCodec.java
@@ -29,7 +29,9 @@ import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
@@ -73,7 +75,7 @@ public final class RestHttpMessageCodec implements HttpMessageDecoder, HttpMessa
@Override
public Object[] decode(InputStream inputStream, Class<?>[] targetTypes, Charset charset) throws DecodeException {
- request.setInputStream(inputStream);
+ request.setInputStream(decodeInputStream(inputStream));
ParameterMeta[] parameters = this.parameters;
int len = parameters.length;
if (len == 0) {
@@ -91,6 +93,19 @@ public final class RestHttpMessageCodec implements HttpMessageDecoder, HttpMessa
encode(os, data);
}
+ private InputStream decodeInputStream(InputStream is) {
+ if (is.getClass() == ByteArrayInputStream.class) {
+ return is;
+ }
+ try {
+ byte[] bytes = new byte[is.available()];
+ is.read(bytes);
+ return new ByteArrayInputStream(bytes);
+ } catch (IOException e) {
+ throw new DecodeException(e);
+ }
+ }
+
@Override
public void encode(OutputStream os, Object data) throws EncodeException {
if (data != null) {
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java
index 9caef42987..a985704823 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/GeneralTypeConverter.java
@@ -615,8 +615,9 @@ public class GeneralTypeConverter implements TypeConverter {
try {
return JsonUtils.convertObject(source, targetClass);
- } catch (Exception e) {
- LOGGER.debug("JSON convert from [{}] to [{}] failed", sourceClass.getName(), targetClass.getName(), e);
+ } catch (Throwable t) {
+ String msg = "JSON convert value '{}' from type [{}] to type [{}] failed";
+ LOGGER.debug(msg, source, sourceClass, targetClass, t);
}
return null;
@@ -660,6 +661,20 @@ public class GeneralTypeConverter implements TypeConverter {
if (Collection.class.isAssignableFrom(targetClass)) {
Type itemType = getActualGenericType(argTypes[0]);
+ if (itemType instanceof Class && targetClass.isInstance(source)) {
+ boolean same = true;
+ Class<?> itemClass = (Class<?>) itemType;
+ for (Object item : (Collection) source) {
+ if (item != null && !itemClass.isInstance(item)) {
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ return source;
+ }
+ }
+
Collection items = toCollection(source);
Collection targetItems = createCollection(targetClass, items.size());
for (Object item : items) {
@@ -671,6 +686,28 @@ public class GeneralTypeConverter implements TypeConverter {
if (Map.class.isAssignableFrom(targetClass)) {
Type keyType = argTypes[0];
Type valueType = argTypes[1];
+
+ if (keyType instanceof Class && valueType instanceof Class && targetClass.isInstance(source)) {
+ boolean same = true;
+ Class<?> keyClass = (Class<?>) keyType;
+ Class<?> valueClass = (Class<?>) valueType;
+ for (Map.Entry entry : ((Map<Object, Object>) source).entrySet()) {
+ Object key = entry.getKey();
+ if (key != null && !keyClass.isInstance(key)) {
+ same = false;
+ break;
+ }
+ Object value = entry.getValue();
+ if (value != null && !valueClass.isInstance(value)) {
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ return source;
+ }
+ }
+
Class<?> mapValueClass = TypeUtils.getMapValueType(targetClass);
boolean multiValue = mapValueClass != null && Collection.class.isAssignableFrom(mapValueClass);
@@ -725,9 +762,9 @@ public class GeneralTypeConverter implements TypeConverter {
try {
return JsonUtils.convertObject(source, targetType);
- } catch (Exception e) {
- String name = source.getClass().getName();
- LOGGER.debug("JSON convert from [{}] to [{}] failed", name, targetType.getTypeName(), e);
+ } catch (Throwable t) {
+ String msg = "JSON convert value '{}' from type [{}] to type [{}] failed";
+ LOGGER.debug(msg, source, source.getClass(), targetType, t);
}
return null;
@@ -1099,7 +1136,7 @@ public class GeneralTypeConverter implements TypeConverter {
.determineHttpMessageDecoder(MediaType.APPLICATION_JSON.getName())
.decode(new ByteArrayInputStream(value.getBytes(UTF_8)), (Class<?>) targetType);
} catch (Throwable t) {
- LOGGER.debug("Failed to parse [{}] from json string [{}]", targetType, value, t);
+ LOGGER.debug("Failed to parse value '{}' from json string '{}'", targetType, value, t);
}
}
return null;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java
index d7401c57cf..80deb647b1 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/argument/NamedValueArgumentResolverSupport.java
@@ -26,11 +26,9 @@ import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
-import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
-import java.util.Optional;
public abstract class NamedValueArgumentResolverSupport {
@@ -64,18 +62,13 @@ public abstract class NamedValueArgumentResolverSupport {
meta.setName(parameterMeta.getRequiredName());
}
- Class<?> type = parameterMeta.getType();
- Type genericType = parameterMeta.getGenericType();
- if (type == Optional.class) {
- Class<?> actualType = TypeUtils.getNestedActualType(genericType, 0);
- meta.setType(actualType == null ? Object.class : actualType);
- } else {
- meta.setType(type);
- }
+ Class<?> type = parameterMeta.getActualType();
+ meta.setType(type);
+ meta.setGenericType(parameterMeta.getActualGenericType());
if (type.isArray()) {
- meta.setNestedTypes(new Class<?>[] {type.getComponentType()});
+ meta.setNestedTypes(new Class<?>[] {type});
} else {
- meta.setNestedTypes(TypeUtils.getNestedActualTypes(genericType));
+ meta.setNestedTypes(TypeUtils.getNestedActualTypes(meta.genericType()));
}
meta.setParameterMeta(parameterMeta);
return meta;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
index 17edec2101..8647c09e78 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/DefaultRequestMappingRegistry.java
@@ -19,6 +19,10 @@ package org.apache.dubbo.rpc.protocol.tri.rest.mapping;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.config.Configuration;
import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.constants.LoggerCodeConstants;
+import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.nested.RestConfig;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.message.MethodMetadata;
@@ -29,6 +33,7 @@ import org.apache.dubbo.rpc.model.ReflectionMethodDescriptor;
import org.apache.dubbo.rpc.model.ReflectionServiceDescriptor;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.protocol.tri.DescriptorUtils;
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.RestInitializeException;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RadixTree.Match;
@@ -53,6 +58,9 @@ import static org.apache.dubbo.rpc.protocol.tri.rest.Messages.AMBIGUOUS_MAPPING;
public final class DefaultRequestMappingRegistry implements RequestMappingRegistry {
+ private static final ErrorTypeAwareLogger LOGGER =
+ LoggerFactory.getErrorTypeAwareLogger(DefaultRequestMappingRegistry.class);
+
private final FrameworkModel frameworkModel;
private final ContentNegotiator contentNegotiator;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
@@ -100,6 +108,10 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
new MethodWalker().walk(service.getClass(), (classes, consumer) -> {
for (int i = 0, size = resolvers.size(); i < size; i++) {
RequestMappingResolver resolver = resolvers.get(i);
+ if (LOGGER.isInfoEnabled()) {
+ String name = resolver.getClass().getSimpleName();
+ LOGGER.info("{} resolve rest mappings from [{}]", name, CollectionUtils.first(classes));
+ }
ServiceMeta serviceMeta = new ServiceMeta(classes, sd, service, url, resolver.getRestToolKit());
if (!resolver.accept(serviceMeta)) {
continue;
@@ -142,7 +154,13 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
registration.mapping = mapping;
registration.meta = handler;
for (PathExpression path : mapping.getPathCondition().getExpressions()) {
- tree.addPath(path, registration);
+ Registration exists = tree.addPath(path, registration);
+ if (exists == null) {
+ LOGGER.debug("Register rest mapping path: '{}' -> {}", path, mapping);
+ } else if (LOGGER.isWarnEnabled()) {
+ String msg = Messages.DUPLICATE_MAPPING.format(path, mapping, exists.mapping);
+ LOGGER.warn(LoggerCodeConstants.INTERNAL_ERROR, "", "", msg);
+ }
}
} finally {
lock.writeLock().unlock();
@@ -232,6 +250,9 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
}
return c1.variableMap.size() - c2.variableMap.size();
});
+
+ LOGGER.debug("Candidate rest mappings: {}", candidates);
+
Candidate first = candidates.get(0);
Candidate second = candidates.get(1);
if (first.mapping.compareTo(second.mapping, request) == 0) {
@@ -246,6 +267,8 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
request.setAttribute(RestConstants.MAPPING_ATTRIBUTE, mapping);
request.setAttribute(RestConstants.HANDLER_ATTRIBUTE, handler);
+ LOGGER.debug("Matched rest mapping={}, method={}", mapping, handler.getMethod());
+
if (!winner.variableMap.isEmpty()) {
request.setAttribute(RestConstants.URI_TEMPLATE_VARIABLES_ATTRIBUTE, winner.variableMap);
}
@@ -368,5 +391,10 @@ public final class DefaultRequestMappingRegistry implements RequestMappingRegist
HandlerMeta meta;
PathExpression expression;
Map<String, String> variableMap;
+
+ @Override
+ public String toString() {
+ return "Candidate{mapping=" + mapping + ", method=" + meta.getMethod() + '}';
+ }
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
index bd84d96f3e..99abe12fc0 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RequestMapping.java
@@ -29,6 +29,7 @@ import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ParamsCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.PathExpression;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ProducesCondition;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ServiceVersionCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ResponseMeta;
@@ -203,6 +204,10 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
return pathCondition;
}
+ public MethodsCondition getMethodsCondition() {
+ return methodsCondition;
+ }
+
public ProducesCondition getProducesCondition() {
return producesCondition;
}
@@ -420,6 +425,13 @@ public final class RequestMapping implements Condition<RequestMapping, HttpReque
return this;
}
+ public Builder service(String ServiceGroup, String ServiceVersion) {
+ if (ServiceGroup != null || ServiceVersion != null) {
+ customCondition = new ServiceVersionCondition(ServiceGroup, ServiceVersion);
+ }
+ return this;
+ }
+
public Builder cors(CorsMeta cors) {
this.cors = cors;
return this;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RestRequestHandlerMapping.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RestRequestHandlerMapping.java
index e04b9794de..1fe540af6d 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RestRequestHandlerMapping.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/RestRequestHandlerMapping.java
@@ -19,10 +19,15 @@ package org.apache.dubbo.rpc.protocol.tri.rest.mapping;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.beans.factory.ScopeBeanFactory;
import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.HttpMethods;
import org.apache.dubbo.remoting.http12.HttpRequest;
import org.apache.dubbo.remoting.http12.HttpResponse;
+import org.apache.dubbo.remoting.http12.HttpResult;
+import org.apache.dubbo.remoting.http12.HttpStatus;
+import org.apache.dubbo.remoting.http12.exception.HttpResultPayloadException;
import org.apache.dubbo.remoting.http12.message.MediaType;
import org.apache.dubbo.remoting.http12.message.codec.CodecUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
@@ -33,14 +38,19 @@ import org.apache.dubbo.rpc.protocol.tri.rest.argument.ArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.CompositeArgumentResolver;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.MethodsCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.HandlerMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
import org.apache.dubbo.rpc.protocol.tri.route.RequestHandler;
import org.apache.dubbo.rpc.protocol.tri.route.RequestHandlerMapping;
+import java.util.Set;
+
@Activate(order = -2000)
public final class RestRequestHandlerMapping implements RequestHandlerMapping {
+ private static final Logger LOGGER = LoggerFactory.getLogger(RestRequestHandlerMapping.class);
+
private final FrameworkModel frameworkModel;
private final RequestMappingRegistry requestMappingRegistry;
private final ArgumentResolver argumentResolver;
@@ -60,11 +70,18 @@ public final class RestRequestHandlerMapping implements RequestHandlerMapping {
@Override
public RequestHandler getRequestHandler(URL url, HttpRequest request, HttpResponse response) {
+ LOGGER.debug("Received http request: {}", request);
+
HandlerMeta meta = requestMappingRegistry.lookup(request);
if (meta == null) {
return null;
}
+ String method = request.method();
+ if (HttpMethods.OPTIONS.name().equals(method)) {
+ handleOptionsRequest(request);
+ }
+
String requestMediaType = request.mediaType();
String responseMediaType = contentNegotiator.negotiate(request);
if (responseMediaType != null) {
@@ -84,7 +101,7 @@ public final class RestRequestHandlerMapping implements RequestHandlerMapping {
typeConverter,
codecUtils.determineHttpMessageEncoder(url, frameworkModel, responseMediaType));
- if (HttpMethods.supportBody(request.method()) && !RequestUtils.isFormOrMultiPart(request)) {
+ if (HttpMethods.supportBody(method) && !RequestUtils.isFormOrMultiPart(request)) {
if (StringUtils.isEmpty(requestMediaType)) {
requestMediaType = responseMediaType;
}
@@ -93,6 +110,8 @@ public final class RestRequestHandlerMapping implements RequestHandlerMapping {
codecUtils.determineHttpMessageDecoder(url, frameworkModel, requestMediaType));
}
+ LOGGER.debug("Content-type negotiate result: request='{}', response='{}'", requestMediaType, responseMediaType);
+
RequestHandler handler = new RequestHandler(meta.getInvoker());
handler.setHasStub(false);
handler.setMethodDescriptor(meta.getMethodDescriptor());
@@ -103,6 +122,25 @@ public final class RestRequestHandlerMapping implements RequestHandlerMapping {
return handler;
}
+ private static void handleOptionsRequest(HttpRequest request) {
+ RequestMapping mapping = request.attribute(RestConstants.MAPPING_ATTRIBUTE);
+ MethodsCondition condition = mapping.getMethodsCondition();
+ if (condition == null) {
+ throw new HttpResultPayloadException(HttpResult.builder()
+ .status(HttpStatus.NO_CONTENT)
+ .header("allow", "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS")
+ .build());
+ }
+ Set<String> methods = condition.getMethods();
+ if (methods.size() == 1 && methods.contains(HttpMethods.OPTIONS.name())) {
+ return;
+ }
+ throw new HttpResultPayloadException(HttpResult.builder()
+ .status(HttpStatus.NO_CONTENT)
+ .header("allow", StringUtils.join(methods, ","))
+ .build());
+ }
+
@Override
public String getType() {
return TripleConstant.TRIPLE_HANDLER_TYPE_REST;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java
index 96b8eec3c4..1790d50f61 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/MethodsCondition.java
@@ -52,17 +52,22 @@ public final class MethodsCondition implements Condition<MethodsCondition, HttpR
@Override
public MethodsCondition match(HttpRequest request) {
String method = request.method();
+
+ if (OPTIONS.name().equals(method)) {
+ if (request.hasHeader("origin") && request.hasHeader("access-control-request-method")) {
+ return new MethodsCondition(OPTIONS.name());
+ } else {
+ return this;
+ }
+ }
+
if (methods.contains(method)) {
return new MethodsCondition(method);
}
+
if (HEAD.name().equals(method) && methods.contains(GET.name())) {
return new MethodsCondition(GET.name());
}
- if (OPTIONS.name().equals(method)
- && request.hasHeader("origin")
- && request.hasHeader("access-control-request-method")) {
- return new MethodsCondition(OPTIONS.name());
- }
return null;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ServiceVersionCondition.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ServiceVersionCondition.java
index f00a26d57d..f1675c0a58 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ServiceVersionCondition.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/condition/ServiceVersionCondition.java
@@ -51,10 +51,10 @@ public final class ServiceVersionCondition implements Condition<ServiceVersionCo
if (version == null) {
version = request.header(RestConstants.HEADER_SERVICE_VERSION);
}
-
if (version != null && !version.equals(this.version)) {
return null;
}
+
return this;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java
index 35592ce137..c93a164ed8 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/AnnotationSupport.java
@@ -61,7 +61,7 @@ public abstract class AnnotationSupport {
});
}
- public final Annotation[] getRealAnnotations() {
+ public final Annotation[] getRawAnnotations() {
AnnotationMeta[] annotations = getAnnotations();
int len = annotations.length;
Annotation[] result = new Annotation[len];
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java
index e56d6e9243..5c827876d1 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/BeanMeta.java
@@ -352,7 +352,7 @@ public final class BeanMeta {
}
}
- public static final class NestedMeta extends NestableParameterMeta {
+ private static final class NestedMeta extends NestableParameterMeta {
private final Class<?> type;
private final Type genericType;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java
index ce3e2350d6..6e8fe11427 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodMeta.java
@@ -16,7 +16,11 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
+import org.apache.dubbo.common.utils.MethodUtils;
import org.apache.dubbo.rpc.model.MethodDescriptor;
+import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.RestToolKit;
+import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
@@ -50,7 +54,14 @@ public final class MethodMeta extends AnnotationSupport {
}
public void initParameters() {
- int count = method.getParameterCount();
+ RpcType rpcType = methodDescriptor.getRpcType();
+ if (rpcType == RpcType.CLIENT_STREAM || rpcType == RpcType.BI_STREAM) {
+ Type genericType = TypeUtils.getNestedGenericType(method.getGenericReturnType(), 0);
+ parameters = new ParameterMeta[] {new StreamParameterMeta(getToolKit(), genericType, method, hierarchy)};
+ return;
+ }
+
+ int count = rpcType == RpcType.SERVER_STREAM ? 1 : method.getParameterCount();
List<List<Parameter>> parameterHierarchies = new ArrayList<>(count);
for (int i = 0, len = hierarchy.size(); i < len; i++) {
Method m = hierarchy.get(i);
@@ -66,7 +77,6 @@ public final class MethodMeta extends AnnotationSupport {
parameterHierarchy.add(mps[j]);
}
}
-
String[] parameterNames = getToolKit().getParameterNames(method);
ParameterMeta[] parameters = new ParameterMeta[count];
for (int i = 0; i < count; i++) {
@@ -136,6 +146,51 @@ public final class MethodMeta extends AnnotationSupport {
@Override
public String toString() {
- return "MethodMeta{method=" + method + '}';
+ return "MethodMeta{" + MethodUtils.toShortString(method) + '}';
+ }
+
+ private static final class StreamParameterMeta extends ParameterMeta {
+
+ private final Class<?> type;
+ private final Type genericType;
+ private final AnnotatedElement element;
+ private final List<? extends AnnotatedElement> elements;
+
+ StreamParameterMeta(
+ RestToolKit toolKit,
+ Type genericType,
+ AnnotatedElement element,
+ List<? extends AnnotatedElement> elements) {
+ super(toolKit, "value");
+ type = TypeUtils.getActualType(genericType);
+ this.genericType = genericType;
+ this.element = element;
+ this.elements = elements;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Stream parameter [" + element + "]";
+ }
+
+ @Override
+ public Class<?> getType() {
+ return type;
+ }
+
+ @Override
+ public Type getGenericType() {
+ return genericType;
+ }
+
+ @Override
+ protected AnnotatedElement getAnnotatedElement() {
+ return element;
+ }
+
+ @Override
+ protected List<? extends AnnotatedElement> getAnnotatedElements() {
+ return elements;
+ }
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java
index 845acf7d60..e9b82cf482 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/MethodParameterMeta.java
@@ -16,9 +16,6 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
-import org.apache.dubbo.rpc.model.MethodDescriptor;
-import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType;
-
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@@ -48,12 +45,6 @@ public final class MethodParameterMeta extends ParameterMeta {
return parameter;
}
- @Override
- public boolean isSingle() {
- MethodDescriptor methodDescriptor = methodMeta.getMethodDescriptor();
- return methodDescriptor.getRpcType() != RpcType.UNARY || methodDescriptor.getParameterClasses().length == 1;
- }
-
@Override
public int getIndex() {
return index;
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/NamedValueMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/NamedValueMeta.java
index 04c77875b8..1713668acc 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/NamedValueMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/NamedValueMeta.java
@@ -16,6 +16,7 @@
*/
package org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta;
+import java.lang.reflect.Type;
import java.util.Arrays;
public class NamedValueMeta {
@@ -24,6 +25,7 @@ public class NamedValueMeta {
private final boolean required;
private final String defaultValue;
private Class<?> type;
+ private Type genericType;
private Class<?>[] nestedTypes;
private ParameterMeta parameterMeta;
@@ -63,6 +65,14 @@ public class NamedValueMeta {
this.type = type;
}
+ public Type genericType() {
+ return genericType;
+ }
+
+ public void setGenericType(Type genericType) {
+ this.genericType = genericType;
+ }
+
public Class<?> nestedType() {
return nestedTypes == null ? null : nestedTypes[0];
}
@@ -99,6 +109,9 @@ public class NamedValueMeta {
}
if (type != null) {
sb.append(", type=").append(type);
+ if (genericType != type) {
+ sb.append(", genericType=").append(genericType);
+ }
}
if (nestedTypes != null) {
sb.append(", nestedTypes=").append(Arrays.toString(nestedTypes));
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java
index 2def707122..33f02fdb31 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/mapping/meta/ParameterMeta.java
@@ -25,6 +25,7 @@ import org.apache.dubbo.rpc.protocol.tri.rest.util.TypeUtils;
import javax.annotation.Nullable;
+import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Optional;
@@ -35,7 +36,7 @@ public abstract class ParameterMeta extends AnnotationSupport {
private final String name;
private Boolean simple;
private Class<?> actualType;
- private Object typeDescriptor;
+ private Type actualGenericType;
protected ParameterMeta(RestToolKit toolKit, String prefix, String name) {
super(toolKit);
@@ -93,22 +94,25 @@ public abstract class ParameterMeta extends AnnotationSupport {
return type;
}
- public final Object getTypeDescriptor() {
- return typeDescriptor;
- }
-
- public final void setTypeDescriptor(Object typeDescriptor) {
- this.typeDescriptor = typeDescriptor;
+ public final Type getActualGenericType() {
+ Type type = actualGenericType;
+ if (type == null) {
+ type = getGenericType();
+ if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() == Optional.class) {
+ type = TypeUtils.getNestedGenericType(getGenericType(), 0);
+ if (type == null) {
+ type = Object.class;
+ }
+ }
+ actualGenericType = type;
+ }
+ return type;
}
public final Object bind(HttpRequest request, HttpResponse response) {
return getToolKit().bind(this, request, response);
}
- public boolean isSingle() {
- return false;
- }
-
public int getIndex() {
return -1;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java
index db39ea43f1..7c6377f873 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/BasicRequestMappingResolver.java
@@ -18,12 +18,12 @@ package org.apache.dubbo.rpc.protocol.tri.rest.support.basic;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.remoting.http12.rest.Mapping;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.protocol.tri.rest.cors.CorsUtils;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMapping.Builder;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.RequestMappingResolver;
-import org.apache.dubbo.rpc.protocol.tri.rest.mapping.condition.ServiceVersionCondition;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.CorsMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
@@ -52,12 +52,13 @@ public class BasicRequestMappingResolver implements RequestMappingResolver {
@Override
public boolean accept(MethodMeta methodMeta) {
- return methodMeta.getMethodDescriptor() != null || methodMeta.findAnnotation(Annotations.Mapping) != null;
+ AnnotationMeta<Mapping> mapping = methodMeta.findAnnotation(Mapping.class);
+ return mapping != null ? !mapping.getAnnotation().disabled() : methodMeta.getMethodDescriptor() != null;
}
@Override
public RequestMapping resolve(ServiceMeta serviceMeta) {
- AnnotationMeta<?> mapping = serviceMeta.findAnnotation(Annotations.Mapping);
+ AnnotationMeta<Mapping> mapping = serviceMeta.findAnnotation(Mapping.class);
Builder builder = builder(mapping);
String[] paths = getPaths(mapping);
@@ -75,12 +76,16 @@ public class BasicRequestMappingResolver implements RequestMappingResolver {
@Override
public RequestMapping resolve(MethodMeta methodMeta) {
Method method = methodMeta.getMethod();
- AnnotationMeta<?> mapping = methodMeta.findAnnotation(Annotations.Mapping);
+ AnnotationMeta<Mapping> mapping = methodMeta.findAnnotation(Mapping.class);
+ if (mapping != null && mapping.getAnnotation().disabled()) {
+ return null;
+ }
+
Builder builder = builder(mapping);
String[] paths = getPaths(mapping);
if (paths.length == 0) {
- builder.path(method.getName()).sig(TypeUtils.buildSig(method));
+ builder.path('/' + method.getName()).sig(TypeUtils.buildSig(method));
} else {
builder.path(paths);
}
@@ -90,7 +95,7 @@ public class BasicRequestMappingResolver implements RequestMappingResolver {
globalCorsMeta = CorsUtils.getGlobalCorsMeta(frameworkModel);
}
return builder.name(method.getName())
- .custom(new ServiceVersionCondition(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion()))
+ .service(serviceMeta.getServiceGroup(), serviceMeta.getServiceVersion())
.cors(globalCorsMeta)
.build();
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
index ab4cbd97a6..ef788d6084 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/FallbackArgumentResolver.java
@@ -20,8 +20,15 @@ import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http12.HttpMethods;
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.rest.Param;
+import org.apache.dubbo.remoting.http12.rest.ParamType;
+import org.apache.dubbo.rpc.model.MethodDescriptor.RpcType;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.AbstractArgumentResolver;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.AnnotationMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodMeta;
+import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.MethodParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.NamedValueMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
import org.apache.dubbo.rpc.protocol.tri.rest.util.RequestUtils;
@@ -40,7 +47,21 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
@Override
protected NamedValueMeta createNamedValueMeta(ParameterMeta param) {
- return new NamedValueMeta(param.isAnnotated(Annotations.Nonnull), null);
+ boolean noBodyParam = true;
+ int paramCount = -1;
+ if (param instanceof MethodParameterMeta) {
+ MethodMeta methodMeta = ((MethodParameterMeta) param).getMethodMeta();
+ ParameterMeta[] paramMetas = methodMeta.getParameters();
+ for (ParameterMeta paramMeta : paramMetas) {
+ AnnotationMeta<Param> anno = paramMeta.findAnnotation(Param.class);
+ if (anno != null && anno.getAnnotation().type() == ParamType.Body) {
+ noBodyParam = false;
+ break;
+ }
+ }
+ paramCount = methodMeta.getMethodDescriptor().getRpcType() != RpcType.UNARY ? 1 : paramMetas.length;
+ }
+ return new FallbackNamedValueMeta(param.isAnnotated(Annotations.Nonnull), noBodyParam, paramCount);
}
@Override
@@ -54,17 +75,24 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
}
protected Object doResolveValue(NamedValueMeta meta, boolean single, HttpRequest request, HttpResponse response) {
- ParameterMeta parameter = meta.parameterMeta();
+ FallbackNamedValueMeta fm = (FallbackNamedValueMeta) meta;
+
if (HttpMethods.supportBody(request.method())) {
- Object body = RequestUtils.decodeBody(request, parameter.getType(), parameter.isSingle());
- if (body != null) {
- if (parameter.getType().isInstance(body)) {
- return body;
- } else if (body instanceof List) {
+ if (fm.paramCount == 1) {
+ try {
+ Object body = RequestUtils.decodeBody(request, meta.genericType());
+ if (body != null) {
+ return body;
+ }
+ } catch (DecodeException ignored) {
+ }
+ }
+ if (fm.noBodyParam) {
+ Object body = RequestUtils.decodeBodyAsObject(request);
+ if (body instanceof List) {
List<?> list = (List<?>) body;
- int index = parameter.getIndex();
- if (index >= 0 && list.size() > index) {
- return list.get(index);
+ if (list.size() == fm.paramCount) {
+ return list.get(meta.parameterMeta().getIndex());
}
} else if (body instanceof Map) {
Object value = ((Map<?, ?>) body).get(meta.name());
@@ -74,13 +102,15 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
}
}
}
+
if (single) {
String value = request.parameter(meta.name());
- if (parameter.isSimple() || RestUtils.isMaybeJSONObject(value)) {
+ if (meta.parameterMeta().isSimple() || RestUtils.isMaybeJSONObject(value)) {
return value;
}
- return parameter.bind(request, response);
+ return meta.parameterMeta().bind(request, response);
}
+
return request.parameterValues(meta.name());
}
@@ -88,4 +118,16 @@ public class FallbackArgumentResolver extends AbstractArgumentResolver {
protected Object resolveMapValue(NamedValueMeta meta, HttpRequest request, HttpResponse response) {
return resolveValue(meta, request, response);
}
+
+ private static class FallbackNamedValueMeta extends NamedValueMeta {
+
+ private final boolean noBodyParam;
+ private final int paramCount;
+
+ FallbackNamedValueMeta(boolean required, boolean noBodyParam, int paramCount) {
+ super(required, null);
+ this.noBodyParam = noBodyParam;
+ this.paramCount = paramCount;
+ }
+ }
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java
index 52456c898c..264df7d334 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/ParamArgumentResolver.java
@@ -84,7 +84,7 @@ public class ParamArgumentResolver extends AbstractAnnotationBaseArgumentResolve
if (RequestUtils.isFormOrMultiPart(request)) {
return request.formParameter(meta.name());
}
- return RequestUtils.decodeBody(request, meta.type());
+ return RequestUtils.decodeBody(request, meta.genericType());
}
return null;
}
@@ -135,7 +135,7 @@ public class ParamArgumentResolver extends AbstractAnnotationBaseArgumentResolve
if (RequestUtils.isFormOrMultiPart(request)) {
return request.formParameterValues(meta.name());
}
- return RequestUtils.decodeBody(request, meta.type());
+ return RequestUtils.decodeBody(request, meta.genericType());
}
return null;
}
@@ -179,7 +179,7 @@ public class ParamArgumentResolver extends AbstractAnnotationBaseArgumentResolve
if (RequestUtils.isFormOrMultiPart(request)) {
return RequestUtils.getFormParametersMap(request);
}
- return RequestUtils.decodeBody(request, meta.type());
+ return RequestUtils.decodeBody(request, meta.genericType());
}
return null;
}
@@ -209,7 +209,7 @@ public class ParamArgumentResolver extends AbstractAnnotationBaseArgumentResolve
private final ParamType paramType;
- public ParamNamedValueMeta(String name, boolean required, String defaultValue, ParamType paramType) {
+ ParamNamedValueMeta(String name, boolean required, String defaultValue, ParamType paramType) {
super(name, required, defaultValue);
this.paramType = paramType;
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java
index c35cd414d5..7ddb719c58 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/AbstractRestToolKit.java
@@ -19,6 +19,8 @@ package org.apache.dubbo.rpc.protocol.tri.rest.util;
import org.apache.dubbo.common.config.Environment;
import org.apache.dubbo.common.utils.AnnotationUtils;
import org.apache.dubbo.rpc.model.FrameworkModel;
+import org.apache.dubbo.rpc.protocol.tri.rest.Messages;
+import org.apache.dubbo.rpc.protocol.tri.rest.RestException;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.GeneralTypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.argument.TypeConverter;
import org.apache.dubbo.rpc.protocol.tri.rest.mapping.meta.ParameterMeta;
@@ -50,7 +52,16 @@ public abstract class AbstractRestToolKit implements RestToolKit {
@Override
public Object convert(Object value, ParameterMeta parameter) {
- return typeConverter.convert(value, parameter.getGenericType());
+ Object target = typeConverter.convert(value, parameter.getGenericType());
+ if (target == null && value != null) {
+ throw new RestException(
+ Messages.ARGUMENT_CONVERT_ERROR,
+ parameter.getName(),
+ value,
+ value.getClass(),
+ parameter.getGenericType());
+ }
+ return target;
}
@Override
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/MethodWalker.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/MethodWalker.java
index 61413c0633..ccb7e95fdc 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/MethodWalker.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/MethodWalker.java
@@ -54,9 +54,6 @@ public final class MethodWalker {
for (Method method : clazz.getDeclaredMethods()) {
int modifiers = method.getModifiers();
if ((modifiers & (Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) {
- if (Modifier.isAbstract(modifiers) && method.getDeclaredAnnotations().length == 0) {
- continue;
- }
methodsMap
.computeIfAbsent(Key.of(method), k -> new ArrayList<>())
.add(method);
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
index b75561a0ee..45d4cde2f0 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/main/java/org/apache/dubbo/rpc/protocol/tri/rest/util/RequestUtils.java
@@ -25,10 +25,10 @@ import org.apache.dubbo.remoting.http12.message.HttpMessageDecoder;
import org.apache.dubbo.remoting.http12.message.MediaType;
import org.apache.dubbo.rpc.protocol.tri.rest.RestConstants;
-import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
@@ -169,92 +169,60 @@ public final class RequestUtils {
return result == null ? Collections.emptyList() : result;
}
- public static Object decodeBody(HttpRequest request, Class<?> type) {
+ public static Object decodeBody(HttpRequest request, Type type) {
HttpMessageDecoder decoder = request.attribute(RestConstants.BODY_DECODER_ATTRIBUTE);
if (decoder == null) {
return null;
}
- Object value = request.attribute(RestConstants.BODY_ATTRIBUTE);
- if (value == null) {
- if (decoder.mediaType().isPureText()) {
- type = String.class;
- }
- InputStream input = request.inputStream();
- try {
- int available = input.available();
- if (available == 0) {
- return emptyDefault(type);
- }
- } catch (IOException e) {
- throw new DecodeException("Error reading input", e);
- }
-
- value = decoder.decode(input, type, request.charsetOrDefault());
- request.setAttribute(RestConstants.BODY_ATTRIBUTE, value);
- }
- return value;
- }
-
- public static Object decodeBody(HttpRequest request, Class<?> type, boolean single) {
- HttpMessageDecoder decoder = request.attribute(RestConstants.BODY_DECODER_ATTRIBUTE);
- if (decoder == null) {
- return null;
- }
- MediaType mediaType = decoder.mediaType();
- if (mediaType == MediaType.APPLICATION_FROM_URLENCODED || mediaType == MediaType.MULTIPART_FORM_DATA) {
- return null;
+ if (decoder.mediaType().isPureText()) {
+ type = String.class;
}
+ InputStream is = request.inputStream();
try {
- InputStream input = request.inputStream();
- if (single) {
- if (mediaType.isPureText()) {
- type = String.class;
- }
- int available = input.available();
- if (available == 0) {
- return emptyDefault(type);
- }
- if (!input.markSupported()) {
- byte[] bytes = new byte[available];
- input.read(bytes);
- input = new ByteArrayInputStream(bytes);
- }
- try {
- return decoder.decode(input, type, request.charsetOrDefault());
- } catch (Throwable t) {
- input.reset();
+ int available = is.available();
+ if (available == 0) {
+ if (type instanceof Class) {
+ Class<?> clazz = (Class<?>) type;
+ if (clazz == String.class) {
+ return StringUtils.EMPTY_STRING;
+ }
+ if (clazz == byte[].class) {
+ return new byte[0];
+ }
}
+ return null;
}
+ } catch (IOException e) {
+ throw new DecodeException("Error reading is", e);
+ }
- Object value = request.attribute(RestConstants.BODY_ATTRIBUTE);
- if (value == null) {
- if (mediaType == MediaType.APPLICATION_JSON || mediaType == MediaType.APPLICATION_YAML) {
- type = Object.class;
- } else if (mediaType.isPureText()) {
- type = String.class;
+ boolean canMark = is.markSupported();
+ try {
+ if (canMark) {
+ is.mark(Integer.MAX_VALUE);
+ }
+ return decoder.decode(is, type, request.charsetOrDefault());
+ } finally {
+ try {
+ if (canMark) {
+ is.reset();
} else {
- return null;
- }
- int available = input.available();
- if (available != 0) {
- value = decoder.decode(input, type, request.charsetOrDefault());
+ is.close();
}
- request.setAttribute(RestConstants.BODY_ATTRIBUTE, value);
+ } catch (IOException ignored) {
}
- return value;
- } catch (IOException e) {
- throw new DecodeException("Error reading input", e);
}
}
- private static Object emptyDefault(Class<?> type) {
- if (type == String.class) {
- return StringUtils.EMPTY_STRING;
- }
- if (type == byte[].class) {
- return new byte[0];
+ public static Object decodeBodyAsObject(HttpRequest request) {
+ Object value = request.attribute(RestConstants.BODY_ATTRIBUTE);
+ if (value == null) {
+ value = decodeBody(request, Object.class);
+ if (value != null) {
+ request.setAttribute(RestConstants.BODY_ATTRIBUTE, value);
+ }
}
- return null;
+ return value;
}
}
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
index 23692bc020..572adc6f09 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/groovy/org/apache/dubbo/rpc/protocol/tri/rest/support/basic/RestProtocolTest.groovy
@@ -51,6 +51,24 @@ class RestProtocolTest extends BaseServiceTest {
'/argTest?name=Sam' | '{"age": 8}' | 'Sam is 8 years old'
}
+ def "list argument body test"() {
+ expect:
+ runner.post(path, body) contains output
+ where:
+ path | body | output
+ '/listArgBodyTest?age=2' | '[1,2]' | '[1,2]'
+ '/listArgBodyTest2?age=1' | '[[1,2],2]' | '[1,2]'
+ }
+
+ def "map argument body test"() {
+ expect:
+ runner.post(path, body) contains output
+ where:
+ path | body | output
+ '/mapArgBodyTest?age=2' | '{"1":["2",3],"4":[5,"6"]}' | '{4:[5,6],1:[2,3]}'
+ '/mapArgBodyTest2?age=1' | '[{"1":[2,3],"4":[5,6]},2]' | '{4:[5,6],1:[2,3]}'
+ }
+
def "bean argument test"() {
expect:
runner.post(path, body, Book.class).name == output
@@ -77,12 +95,14 @@ class RestProtocolTest extends BaseServiceTest {
expect:
runner.post(path, body, Book.class).price == output
where:
- path | body | output
- '/beanArgTest' | [] | 0
- '/beanArgTest' | [quote: 5] | 5
- '/beanArgTest' | [book: new Book(price: 6)] | 6
- '/beanArgTest' | [book: new Book(price: 6), quote: 5] | 5
- '/beanArgTest' | [price: 6, quote: 5] | 5
+ path | body | output
+ '/beanArgTest' | [] | 0
+ '/beanArgTest' | [quote: 5] | 5
+ '/beanArgTest' | [book: new Book(price: 6)] | 6
+ '/beanArgTest' | [book: new Book(price: 6), quote: 5] | 5
+ '/beanArgTest' | [price: 6, quote: 5] | 5
+ '/beanArgTest2' | '{"price": 5}' | 5
+ '/beanArgTest2' | '[{"price": 5}]' | 5
}
def "advance bean argument get test"() {
@@ -116,6 +136,15 @@ class RestProtocolTest extends BaseServiceTest {
'/bean?groupsMap.one[0].name=a&groupsMap.one[1].name=b&groupsMap.two[1].name=c' | '"groupsMap":{"one":[{"id":0,"name":"a"},{"id":0,"name":"b"}],"two":[null,{"id":0,"name":"c"}]}'
}
+ def "bean body test"() {
+ expect:
+ runner.post(path, body) contains output
+ where:
+ path | body | output
+ '/beanBodyTest?age=2' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]'
+ '/beanBodyTest2?age=1' | '[[{"id":1,"name":"g1"},{"id":2,"name":"g2"}],2]' | '[{"id":1,"name":"g1"},{"id":2,"name":"g2"}]'
+ }
+
def "urlEncodeForm test"() {
given:
def request = new TestRequest(
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java
index cb3cc8f1da..732fcde91e 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoService.java
@@ -18,11 +18,18 @@ package org.apache.dubbo.rpc.protocol.tri.rest.service;
import org.apache.dubbo.common.stream.StreamObserver;
import org.apache.dubbo.remoting.http12.rest.Mapping;
+import org.apache.dubbo.remoting.http12.rest.Param;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.Group;
import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
+import java.util.List;
+import java.util.Map;
+
import io.grpc.health.v1.HealthCheckRequest;
import io.grpc.health.v1.HealthCheckResponse;
+import static org.apache.dubbo.remoting.http12.rest.ParamType.Body;
+
@Mapping("/")
public interface DemoService {
@@ -32,9 +39,23 @@ public interface DemoService {
Book beanArgTest(Book book, Integer quote);
+ Book beanArgTest2(Book book);
+
@Mapping("/bean")
UserEx advanceBeanArgTest(UserEx user);
+ List<Integer> listArgBodyTest(@Param(type = Body) List<Integer> list, int age);
+
+ List<Integer> listArgBodyTest2(List<Integer> list, int age);
+
+ Map<Integer, List<Long>> mapArgBodyTest(@Param(type = Body) Map<Integer, List<Long>> map, int age);
+
+ Map<Integer, List<Long>> mapArgBodyTest2(Map<Integer, List<Long>> map, int age);
+
+ List<Group> beanBodyTest(@Param(type = Body) List<Group> groups, int age);
+
+ List<Group> beanBodyTest2(List<Group> groups, int age);
+
Book buy(Book book);
@Mapping("/buy2")
diff --git a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java
index d17e71a00d..39c2f9795a 100644
--- a/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java
+++ b/dubbo-rpc/dubbo-rpc-triple/src/test/java/org/apache/dubbo/rpc/protocol/tri/rest/service/DemoServiceImpl.java
@@ -17,10 +17,15 @@
package org.apache.dubbo.rpc.protocol.tri.rest.service;
import org.apache.dubbo.common.stream.StreamObserver;
+import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.remoting.http12.rest.Mapping;
+import org.apache.dubbo.rpc.protocol.tri.rest.service.User.Group;
import org.apache.dubbo.rpc.protocol.tri.rest.service.User.UserEx;
+import java.util.List;
+import java.util.Map;
+
import io.grpc.health.v1.HealthCheckRequest;
import io.grpc.health.v1.HealthCheckResponse;
import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
@@ -48,11 +53,49 @@ public class DemoServiceImpl implements DemoService {
return book;
}
+ @Override
+ public Book beanArgTest2(Book book) {
+ return beanArgTest(book, null);
+ }
+
@Override
public UserEx advanceBeanArgTest(UserEx user) {
return user;
}
+ @Override
+ public List<Integer> listArgBodyTest(List<Integer> list, int age) {
+ Assert.assertTrue(age == 2, "age must be 2");
+ return list;
+ }
+
+ @Override
+ public List<Integer> listArgBodyTest2(List<Integer> list, int age) {
+ return listArgBodyTest(list, age);
+ }
+
+ @Override
+ public Map<Integer, List<Long>> mapArgBodyTest(Map<Integer, List<Long>> map, int age) {
+ Assert.assertTrue(age == 2, "age must be 2");
+ return map;
+ }
+
+ @Override
+ public Map<Integer, List<Long>> mapArgBodyTest2(Map<Integer, List<Long>> map, int age) {
+ return mapArgBodyTest(map, age);
+ }
+
+ @Override
+ public List<Group> beanBodyTest(List<Group> groups, int age) {
+ Assert.assertTrue(age == 2, "age must be 2");
+ return groups;
+ }
+
+ @Override
+ public List<Group> beanBodyTest2(List<Group> groups, int age) {
+ return beanBodyTest(groups, age);
+ }
+
@Override
public Book buy(Book book) {
return book;