Feature/dubbo rest no annotation support (#12700)

This commit is contained in:
suncairong163 2023-07-11 13:38:49 +08:00 committed by GitHub
parent 8bb17b85f2
commit c7d411c3cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 599 additions and 35 deletions

View File

@ -362,9 +362,9 @@ public abstract class AbstractServiceRestMetadataResolver implements ServiceRest
}
}
private void processAnnotatedMethodParameter(Parameter parameter, int parameterIndex, Method serviceMethod,
Class<?> serviceType, Class<?> serviceInterfaceClass,
RestMethodMetadata metadata) {
protected void processAnnotatedMethodParameter(Parameter parameter, int parameterIndex, Method serviceMethod,
Class<?> serviceType, Class<?> serviceInterfaceClass,
RestMethodMetadata metadata) {
Annotation[] annotations = parameter.getAnnotations();
if (annotations == null || annotations.length == 0) {

View File

@ -20,7 +20,7 @@ package org.apache.dubbo.metadata.rest;
import java.lang.reflect.Parameter;
/**
* description of service method args info
* description of service method args info
*/
public class ArgInfo {
/**

View File

@ -17,6 +17,7 @@
package org.apache.dubbo.metadata.rest;
import org.apache.dubbo.metadata.rest.tag.BodyTag;
import org.apache.dubbo.metadata.rest.tag.NoAnnotationTag;
import org.apache.dubbo.metadata.rest.tag.ParamTag;
import java.util.ArrayList;
@ -40,9 +41,11 @@ public enum ParamType {
SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS)),
PROVIDER_BODY(addSupportTypes(
JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS,JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS,
JAXRSClassConstants.REST_EASY_BODY_ANNOTATION_CLASS, JAXRSClassConstants.FORM_PARAM_ANNOTATION_CLASS,
SpringMvcClassConstants.REQUEST_BODY_ANNOTATION_CLASS, BodyTag.class)),
PROVIDER_NO_ANNOTATION(addSupportTypes(NoAnnotationTag.class)),
EMPTY(addSupportTypes());
private List<Class> annotationClasses;

View File

@ -17,6 +17,8 @@
package org.apache.dubbo.metadata.rest;
import org.apache.dubbo.metadata.definition.model.MethodDefinition;
import org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver;
import org.apache.dubbo.metadata.rest.tag.NoAnnotationTag;
import java.io.Serializable;
import java.lang.reflect.Method;
@ -62,7 +64,7 @@ public class RestMethodMetadata implements Serializable {
private Method reflectMethod;
/**
* make a distinction between mvc & resteasy
* make a distinction between mvc & resteasy
*/
private Class codeStyle;
@ -175,6 +177,9 @@ public class RestMethodMetadata implements Serializable {
}
public void addArgInfo(ArgInfo argInfo) {
if (currentCodeStyleIsNoAnnotationMode()) {
argInfo.setParamAnnotationType(NoAnnotationTag.class);
}
getArgInfos().add(argInfo);
}
@ -195,6 +200,10 @@ public class RestMethodMetadata implements Serializable {
this.codeStyle = codeStyle;
}
public boolean currentCodeStyleIsNoAnnotationMode() {
return NoAnnotationServiceRestMetadataResolver.class.equals(getCodeStyle());
}
@Override
public boolean equals(Object o) {
if (this == o) {

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.metadata.rest.jaxrs;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metadata.rest.AbstractServiceRestMetadataResolver;
import org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver;
import org.apache.dubbo.rpc.model.ApplicationModel;
@ -40,6 +41,7 @@ import static org.apache.dubbo.metadata.rest.RestMetadataConstants.JAX_RS.PRODUC
*
* @since 2.7.6
*/
@Activate(order = 100)
public class JAXRSServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
public JAXRSServiceRestMetadataResolver(ApplicationModel applicationModel) {
super(applicationModel);

View File

@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.metadata.rest.noannotaion;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metadata.rest.AbstractServiceRestMetadataResolver;
import org.apache.dubbo.metadata.rest.ArgInfo;
import org.apache.dubbo.metadata.rest.RestMethodMetadata;
import org.apache.dubbo.metadata.rest.media.MediaType;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Set;
import static org.apache.dubbo.common.utils.PathUtils.buildPath;
/**
* NoAnnotationServiceRestMetadataResolver
*
* @since 3.3
*/
@Activate(order = Integer.MAX_VALUE)
public class NoAnnotationServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
private static final String CONTENT_TYPE = MediaType.APPLICATION_JSON_VALUE.value;
private static final String REQUEST_METHOD = "POST";
public NoAnnotationServiceRestMetadataResolver(ApplicationModel applicationModel) {
super(applicationModel);
}
@Override
protected boolean supports0(Class<?> serviceType) {
// class @Controller or @RequestMapping
return true;
}
@Override
protected boolean isRestCapableMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
// method only match @RequestMapping
return true;
}
@Override
protected String resolveRequestMethod(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
return REQUEST_METHOD;
}
@Override
protected String resolveRequestPath(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass) {
// use serviceInterfaceClass class name
return buildPath(serviceInterfaceClass.getName(), serviceMethod.getName());
}
@Override
protected void processProduces(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> produces) {
produces.add(CONTENT_TYPE);
}
@Override
protected void processConsumes(Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, Set<String> consumes) {
consumes.add(CONTENT_TYPE);
}
@Override
protected void processAnnotatedMethodParameter(Parameter parameter, int parameterIndex, Method serviceMethod, Class<?> serviceType, Class<?> serviceInterfaceClass, RestMethodMetadata metadata) {
ArgInfo argInfo = ArgInfo.build(parameterIndex, parameter);
metadata.addArgInfo(argInfo);
}
}

View File

@ -16,6 +16,7 @@
*/
package org.apache.dubbo.metadata.rest.springmvc;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metadata.rest.AbstractServiceRestMetadataResolver;
import org.apache.dubbo.metadata.rest.ServiceRestMetadataResolver;
import org.apache.dubbo.rpc.model.ApplicationModel;
@ -45,6 +46,7 @@ import static org.apache.dubbo.metadata.rest.RestMetadataConstants.SPRING_MVC.RE
*
* @since 2.7.6
*/
@Activate(order = 100)
public class SpringMvcServiceRestMetadataResolver extends AbstractServiceRestMetadataResolver {
private static final int FIRST_ELEMENT_INDEX = 0;

View File

@ -0,0 +1,23 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.metadata.rest.tag;
/**
* for no annotation mode param
*/
public interface NoAnnotationTag {
}

View File

@ -1,3 +1,4 @@
default =
jax-rs = org.apache.dubbo.metadata.rest.jaxrs.JAXRSServiceRestMetadataResolver
spring-webmvc = org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver
spring-webmvc = org.apache.dubbo.metadata.rest.springmvc.SpringMvcServiceRestMetadataResolver
no-annotation = org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver

View File

@ -0,0 +1,92 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.metadata.rest.noannotation;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.metadata.rest.DefaultRestService;
import org.apache.dubbo.metadata.rest.RestMethodMetadata;
import org.apache.dubbo.metadata.rest.ServiceRestMetadata;
import org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class NoAnnotationServiceRestMetadataResolverTest {
private NoAnnotationServiceRestMetadataResolver instance = new NoAnnotationServiceRestMetadataResolver(ApplicationModel.defaultModel());
@Test
void testResolve() {
List<String> jsons = Arrays.asList(
"{\"argInfos\":[{\"annotationNameAttribute\":\"form\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"form\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"form\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/form\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"header\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"header\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0},{\"annotationNameAttribute\":\"header2\",\"formContentType\":false,\"index\":1,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"header2\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0},{\"annotationNameAttribute\":\"param\",\"formContentType\":false,\"index\":2,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"param\",\"paramType\":\"java.lang.Integer\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"header\"],1:[\"header2\"],2:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/headers\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/noAnnotationFormBody\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/noAnnotationJsonBody\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"text\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"text\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"text\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/noAnnotationParam\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"param\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/param\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"a\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"a\",\"paramType\":\"int\",\"urlSplitIndex\":0},{\"annotationNameAttribute\":\"b\",\"formContentType\":false,\"index\":1,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"b\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"a\"],1:[\"b\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/params\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"path1\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"path1\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0},{\"annotationNameAttribute\":\"path2\",\"formContentType\":false,\"index\":1,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"path2\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0},{\"annotationNameAttribute\":\"param\",\"formContentType\":false,\"index\":2,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"path1\"],1:[\"path2\"],2:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/pathVariables\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"data\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"data\",\"paramType\":\"java.util.Map\",\"urlSplitIndex\":0},{\"annotationNameAttribute\":\"param\",\"formContentType\":false,\"index\":1,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"param\",\"paramType\":\"java.lang.String\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"data\"],1:[\"param\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/requestBodyMap\",\"produces\":[\"application/json\"]}}",
"{\"argInfos\":[{\"annotationNameAttribute\":\"user\",\"formContentType\":false,\"index\":0,\"paramAnnotationType\":\"org.apache.dubbo.metadata.rest.tag.NoAnnotationTag\",\"paramName\":\"user\",\"paramType\":\"org.apache.dubbo.metadata.rest.User\",\"urlSplitIndex\":0}],\"codeStyle\":\"org.apache.dubbo.metadata.rest.noannotaion.NoAnnotationServiceRestMetadataResolver\",\"indexToName\":{0:[\"user\"]},\"method\":{\"annotations\":[],\"parameters\":[]},\"request\":{\"consumes\":[\"application/json\"],\"headerNames\":[],\"headers\":{},\"method\":\"POST\",\"paramNames\":[],\"params\":{},\"path\":\"/org.apache.dubbo.metadata.rest.RestService/requestBodyUser\",\"produces\":[\"application/json\"]}}"
);
boolean supports = instance.supports(DefaultRestService.class);
Assertions.assertEquals(true, supports);
ServiceRestMetadata serviceRestMetadata = instance.resolve(DefaultRestService.class);
List<String> jsonsTmp = new ArrayList<>();
for (RestMethodMetadata restMethodMetadata : serviceRestMetadata.getMeta()) {
restMethodMetadata.setReflectMethod(null);
restMethodMetadata.setMethod(null);
jsonsTmp.add(JsonUtils.toJson(restMethodMetadata));
}
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
};
jsons.sort(comparator);
jsonsTmp.sort(comparator);
for (int i = 0; i < jsons.size(); i++) {
assertEquals(jsons.get(i), jsonsTmp.get(i));
}
}
}

View File

@ -32,6 +32,7 @@ import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
import org.apache.dubbo.rpc.protocol.rest.pair.InvokerAndRestMethodMetadataPair;
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
import org.apache.dubbo.rpc.protocol.rest.util.HttpHeaderUtil;
import org.apache.dubbo.rpc.protocol.rest.util.NoAnnotationBodyParseUtil;
import java.lang.reflect.Method;
@ -91,11 +92,16 @@ public class RestRPCInvocationUtil {
ProviderParseContext parseContext = new ProviderParseContext(request);
parseContext.setResponse(originResponse);
parseContext.setRequest(originRequest);
parseContext.setNoAnnotationMode(restMethodMetadata.currentCodeStyleIsNoAnnotationMode());
Object[] objects = new Object[restMethodMetadata.getArgInfos().size()];
parseContext.setArgs(Arrays.asList(objects));
parseContext.setArgInfos(restMethodMetadata.getArgInfos());
// parse object arrays body
if (restMethodMetadata.currentCodeStyleIsNoAnnotationMode()) {
parseContext.setArrayArgs(NoAnnotationBodyParseUtil.doParse(parseContext));
}
return parseContext;
}

View File

@ -28,6 +28,8 @@ public class BaseParseContext {
protected List<ArgInfo> argInfos;
boolean isNoAnnotationMode;
public List<Object> getArgs() {
return args;
@ -48,4 +50,12 @@ public class BaseParseContext {
public ArgInfo getArgInfoByIndex(int index) {
return getArgInfos().get(index);
}
public boolean isNoAnnotationMode() {
return isNoAnnotationMode;
}
public void setNoAnnotationMode(boolean noAnnotationMode) {
isNoAnnotationMode = noAnnotationMode;
}
}

View File

@ -51,7 +51,11 @@ public class ParamParserManager {
List<ArgInfo> args = parseContext.getArgInfos();
for (int i = 0; i < args.size(); i++) {
for (ParamParser paramParser : providerParamParsers) {
for (BaseProviderParamParser paramParser : providerParamParsers) {
if (!paramParser.matchParseType(args.get(i).getParamAnnotationType())) {
continue;
}
paramParser.parse(parseContext, args.get(i));
}

View File

@ -74,4 +74,8 @@ public class HttpConnectionCreateContext {
public void setServiceRestMetadata(ServiceRestMetadata serviceRestMetadata) {
this.serviceRestMetadata = serviceRestMetadata;
}
public boolean isNoAnnotationMode() {
return restMethodMetadata.currentCodeStyleIsNoAnnotationMode();
}
}

View File

@ -17,6 +17,7 @@
package org.apache.dubbo.rpc.protocol.rest.annotation.consumer.inercept;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.remoting.http.RequestTemplate;
import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParserManager;
import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionCreateContext;
import org.apache.dubbo.rpc.protocol.rest.annotation.consumer.HttpConnectionPreBuildIntercept;
@ -25,17 +26,27 @@ import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.consumer.Consum
import java.util.Arrays;
/**
* resolve method args by args info
* resolve method args by args info
*/
@Activate(value = "paramparse",order = 5)
@Activate(value = "paramparse", order = 5)
public class ParamParseIntercept implements HttpConnectionPreBuildIntercept {
@Override
public void intercept(HttpConnectionCreateContext connectionCreateContext) {
ConsumerParseContext consumerParseContext = new ConsumerParseContext(connectionCreateContext.getRequestTemplate());
consumerParseContext.setArgInfos(connectionCreateContext.getRestMethodMetadata().getArgInfos());
consumerParseContext.setArgs(Arrays.asList(connectionCreateContext.getInvocation().getArguments()));
ParamParserManager.consumerParamParse(consumerParseContext);
RequestTemplate requestTemplate = connectionCreateContext.getRequestTemplate();
Object[] arguments = connectionCreateContext.getInvocation().getArguments();
// no annotation mode set array body
if (connectionCreateContext.isNoAnnotationMode()) {
requestTemplate.body(arguments, Object[].class);
} else {
ConsumerParseContext consumerParseContext = new ConsumerParseContext(requestTemplate);
consumerParseContext.setArgInfos(connectionCreateContext.getRestMethodMetadata().getArgInfos());
consumerParseContext.setArgs(Arrays.asList(arguments));
ParamParserManager.consumerParamParse(consumerParseContext);
}
}
}

View File

@ -38,7 +38,7 @@ public class MetadataResolver {
public static ServiceRestMetadata resolveConsumerServiceMetadata(Class<?> targetClass, URL url, String contextPathFromUrl) {
ExtensionLoader<ServiceRestMetadataResolver> extensionLoader = url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class);
for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getSupportedExtensionInstances()) {
for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getActivateExtensions()) {
if (serviceRestMetadataResolver.supports(targetClass, true)) {
ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), true);
serviceRestMetadata.setContextPathFromUrl(contextPathFromUrl);
@ -55,7 +55,7 @@ public class MetadataResolver {
public static ServiceRestMetadata resolveProviderServiceMetadata(Class serviceImpl, URL url, String contextPathFromUrl) {
ExtensionLoader<ServiceRestMetadataResolver> extensionLoader = url.getOrDefaultApplicationModel().getExtensionLoader(ServiceRestMetadataResolver.class);
for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getSupportedExtensionInstances()) {
for (ServiceRestMetadataResolver serviceRestMetadataResolver : extensionLoader.getActivateExtensions()) {
boolean supports = serviceRestMetadataResolver.supports(serviceImpl);
if (supports) {
ServiceRestMetadata serviceRestMetadata = new ServiceRestMetadata(url.getServiceInterface(), url.getVersion(), url.getGroup(), false);

View File

@ -19,9 +19,19 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
import org.apache.dubbo.common.extension.ExtensionScope;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.metadata.rest.ParamType;
import org.apache.dubbo.rpc.protocol.rest.annotation.ParamParser;
@SPI(scope = ExtensionScope.FRAMEWORK)
public interface BaseProviderParamParser extends ParamParser<ProviderParseContext> {
default boolean matchParseType(Class paramAnno) {
ParamType paramAnnotType = getParamType();
return paramAnnotType.supportAnno(paramAnno);
}
ParamType getParamType();
}

View File

@ -51,7 +51,7 @@ public class BodyProviderParamParser extends ProviderParamParser {
}
@Override
protected ParamType getParamType() {
public ParamType getParamType() {
return ParamType.PROVIDER_BODY;
}
}

View File

@ -59,7 +59,7 @@ public class HeaderProviderParamParser extends ProviderParamParser {
}
@Override
protected ParamType getParamType() {
public ParamType getParamType() {
return ParamType.HEADER;
}
}

View File

@ -0,0 +1,59 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.JsonUtils;
import org.apache.dubbo.metadata.rest.ArgInfo;
import org.apache.dubbo.metadata.rest.ParamType;
import org.apache.dubbo.rpc.protocol.rest.constans.RestConstant;
import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
/**
* body param parse
*/
@Activate(value = RestConstant.PROVIDER_NO_ANNOTATION)
public class NoAnnotationParamProviderParamParser extends ProviderParamParser {
@Override
protected void doParse(ProviderParseContext parseContext, ArgInfo argInfo) {
Object[] arrayArgs = parseContext.getArrayArgs();
int index = argInfo.getIndex();
Object arg = arrayArgs[index];
Object convertArg = null;
if (DataParseUtils.isTextType(argInfo.getParamType())) {
convertArg = DataParseUtils.stringTypeConvert(argInfo.getParamType(), String.valueOf(arg));
} else {
convertArg = JsonUtils.toJavaObject(arg.toString(), argInfo.getParamType());
}
parseContext.setValueByIndex(index, convertArg);
}
@Override
public ParamType getParamType() {
return ParamType.PROVIDER_NO_ANNOTATION;
}
}

View File

@ -59,7 +59,7 @@ public class ParamProviderParamParser extends ProviderParamParser {
}
@Override
protected ParamType getParamType() {
public ParamType getParamType() {
return ParamType.PARAM;
}
}

View File

@ -40,7 +40,7 @@ public class PathProviderParamParser extends ProviderParamParser {
}
@Override
protected ParamType getParamType() {
public ParamType getParamType() {
return ParamType.PATH;
}
}

View File

@ -18,33 +18,19 @@ package org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider;
import org.apache.dubbo.metadata.rest.ArgInfo;
import org.apache.dubbo.metadata.rest.ParamType;
import org.apache.dubbo.rpc.protocol.rest.util.DataParseUtils;
public abstract class ProviderParamParser implements BaseProviderParamParser {
public void parse(ProviderParseContext parseContext, ArgInfo argInfo) {
if (!matchParseType(argInfo.getParamAnnotationType())) {
return;
}
doParse(parseContext, argInfo);
}
protected abstract void doParse(ProviderParseContext parseContext, ArgInfo argInfo);
public boolean matchParseType(Class paramAnno) {
ParamType paramAnnotType = getParamType();
return paramAnnotType.supportAnno(paramAnno);
}
protected abstract ParamType getParamType();
protected Object paramTypeConvert(Class targetType, String value) {
return DataParseUtils.stringTypeConvert(targetType, value);
}

View File

@ -27,6 +27,7 @@ public class ProviderParseContext extends BaseParseContext {
private RequestFacade requestFacade;
private Object response;
private Object request;
private Object[] arrayArgs;
public ProviderParseContext(RequestFacade request) {
@ -66,5 +67,12 @@ public class ProviderParseContext extends BaseParseContext {
}
public Object[] getArrayArgs() {
return arrayArgs;
}
public void setArrayArgs(Object[] objects) {
this.arrayArgs = objects;
}
}

View File

@ -32,6 +32,7 @@ public interface RestConstant {
String PROVIDER_PARAM_PARSE = "param";
String PROVIDER_HEADER_PARSE = "header";
String PROVIDER_PATH_PARSE = "path";
String PROVIDER_NO_ANNOTATION = "no-annotation";
String ADD_MUST_ATTTACHMENT = "must-intercept";
String RPCCONTEXT_INTERCEPT = "rpc-context";

View File

@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.protocol.rest.util;
import org.apache.dubbo.metadata.rest.media.MediaType;
import org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ProviderParseContext;
import org.apache.dubbo.rpc.protocol.rest.exception.ParamParseException;
import org.apache.dubbo.rpc.protocol.rest.message.HttpMessageCodecManager;
import org.apache.dubbo.rpc.protocol.rest.request.RequestFacade;
public class NoAnnotationBodyParseUtil {
public static Object[] doParse(ProviderParseContext parseContext) {
RequestFacade request = parseContext.getRequestFacade();
try {
Class<?> objectArraysType = Object[].class;
MediaType mediaType = MediaTypeUtil.convertMediaType(objectArraysType, MediaType.APPLICATION_JSON_VALUE.value);
Object[] params = (Object[]) HttpMessageCodecManager.httpMessageDecode(request.getInputStream(), objectArraysType, mediaType);
return params;
} catch (Throwable e) {
throw new ParamParseException("dubbo rest protocol provider body param parser error: " + e.getMessage());
}
}
}

View File

@ -2,3 +2,4 @@ body=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.BodyProv
header=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.HeaderProviderParamParser
path=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.PathProviderParamParser
param=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.ParamProviderParamParser
no-annotation=org.apache.dubbo.rpc.protocol.rest.annotation.param.parse.provider.NoAnnotationParamProviderParamParser

View File

@ -0,0 +1,127 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.protocol.rest;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.rpc.Exporter;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Protocol;
import org.apache.dubbo.rpc.ProxyFactory;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.apache.dubbo.rpc.model.ModuleServiceRepository;
import org.apache.dubbo.rpc.model.ProviderModel;
import org.apache.dubbo.rpc.model.ServiceDescriptor;
import org.apache.dubbo.rpc.protocol.rest.noannotation.NoAnnotationDemoService;
import org.apache.dubbo.rpc.protocol.rest.noannotation.NoAnnotationDemoServiceImpl;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
class NoAnnotationRestProtocolTest {
private final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension("rest");
private final ProxyFactory proxy = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private final int availablePort = NetUtils.getAvailablePort();
private final URL exportUrl = URL.valueOf("rest://127.0.0.1:" + availablePort + "/rest?interface=org.apache.dubbo.rpc.protocol.rest.noannotation.NoAnnotationDemoService");
private final ModuleServiceRepository repository = ApplicationModel.defaultModel().getDefaultModule().getServiceRepository();
private static final String SERVER = "netty4";
@AfterEach
public void tearDown() {
protocol.destroy();
FrameworkModel.destroyAll();
}
@Test
void testRestProtocol() {
URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort() + "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.noannotation.NoAnnotationDemoService");
NoAnnotationDemoServiceImpl server = new NoAnnotationDemoServiceImpl();
url = this.registerProvider(url, server, DemoService.class);
Exporter<NoAnnotationDemoService> exporter = protocol.export(proxy.getInvoker(server, NoAnnotationDemoService.class, url));
Invoker<NoAnnotationDemoService> invoker = protocol.refer(NoAnnotationDemoService.class, url);
NoAnnotationDemoService client = proxy.getProxy(invoker);
Object result = client.sayHello("haha");
Assertions.assertEquals("Hello, haha", result);
result = client.hello(1, 2);
Assertions.assertEquals(3, result);
User user = client.noBodyArg(User.getInstance());
Assertions.assertEquals("invoked", user.getName());
invoker.destroy();
exporter.unexport();
}
@Test
void testAnotherUserRestProtocolByDifferentRestClient() {
testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.OK_HTTP);
testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.APACHE_HTTP_CLIENT);
testAnotherUserRestProtocol(org.apache.dubbo.remoting.Constants.URL_CONNECTION);
}
void testAnotherUserRestProtocol(String restClient) {
URL url = URL.valueOf("rest://127.0.0.1:" + NetUtils.getAvailablePort()
+ "/?version=1.0.0&interface=org.apache.dubbo.rpc.protocol.rest.noannotation.NoAnnotationDemoService&"
+ org.apache.dubbo.remoting.Constants.CLIENT_KEY + "=" + restClient);
NoAnnotationDemoService server = new NoAnnotationDemoServiceImpl();
url = this.registerProvider(url, server, DemoService.class);
Exporter<NoAnnotationDemoService> exporter = protocol.export(proxy.getInvoker(server, NoAnnotationDemoService.class, url));
Invoker<NoAnnotationDemoService> invoker = protocol.refer(NoAnnotationDemoService.class, url);
NoAnnotationDemoService client = proxy.getProxy(invoker);
String name = "no-annotation";
String result = client.sayHello(name);
Assertions.assertEquals("Hello, " + name, result);
invoker.destroy();
exporter.unexport();
}
private URL registerProvider(URL url, Object impl, Class<?> interfaceClass) {
ServiceDescriptor serviceDescriptor = repository.registerService(interfaceClass);
ProviderModel providerModel = new ProviderModel(
url.getServiceKey(),
impl,
serviceDescriptor,
null,
null);
repository.registerProvider(providerModel);
return url.setServiceModel(providerModel);
}
}

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.protocol.rest.noannotation;
import org.apache.dubbo.rpc.protocol.rest.User;
public interface NoAnnotationDemoService {
Integer hello(Integer a, Integer b);
String error();
String sayHello(String name);
User noBodyArg(User user);
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.rpc.protocol.rest.noannotation;
import org.apache.dubbo.rpc.protocol.rest.User;
public class NoAnnotationDemoServiceImpl implements NoAnnotationDemoService {
@Override
public Integer hello(Integer a, Integer b) {
return a + b;
}
@Override
public String error() {
return null;
}
@Override
public String sayHello(String name) {
return "Hello, "+name;
}
@Override
public User noBodyArg(User user) {
user.setName("invoked");
return user;
}
}