pull/1/head
tanghc 5 years ago
parent aaa5a86977
commit 045e8cdaba
  1. 16
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/bean/OpenContextImpl.java
  2. 156
      sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/param/ApiArgumentResolver.java
  3. 12
      sop-example/sop-story/sop-story-web/src/main/java/com/gitee/sop/storyweb/controller/AlipayController.java
  4. 8
      sop-test/src/main/java/com/gitee/sop/test/Client.java
  5. 68
      sop-test/src/test/java/com/gitee/sop/test/AllInOneTest.java

@ -31,13 +31,15 @@ public class OpenContextImpl<T> implements OpenContext<T> {
} }
public OpenContextImpl(JSONObject rootJsonObject, Class<?> bizClass) { public OpenContextImpl(JSONObject rootJsonObject, Class<?> bizClass) {
if (rootJsonObject == null) {
throw new IllegalArgumentException("rootJsonObject can not be null");
}
this.rootJsonObject = rootJsonObject; this.rootJsonObject = rootJsonObject;
if (bizClass != null) { if (bizClass != null) {
JSONObject bizJsonObj = this.rootJsonObject.getJSONObject(BIZ_CONTENT_NAME); String bizContent = getBizContent();
if (bizJsonObj == null) { if (bizContent != null) {
bizJsonObj = rootJsonObject; bizObject = (T) JSON.parseObject(bizContent, bizClass);
} }
bizObject = (T) bizJsonObj.toJavaObject(bizClass);
} }
} }
@ -106,10 +108,10 @@ public class OpenContextImpl<T> implements OpenContext<T> {
if (bizObject != null && bizObject.getClass() == clazz) { if (bizObject != null && bizObject.getClass() == clazz) {
return (E) bizObject; return (E) bizObject;
} }
String bizJsonObj = this.rootJsonObject.getString(BIZ_CONTENT_NAME); String bizContent = getBizContent();
if (bizJsonObj == null) { if (bizContent == null) {
return null; return null;
} }
return JSON.parseObject(bizJsonObj, clazz); return JSON.parseObject(bizContent, clazz);
} }
} }

@ -9,6 +9,9 @@ import com.gitee.sop.servercommon.bean.ServiceContext;
import com.gitee.sop.servercommon.util.OpenUtil; import com.gitee.sop.servercommon.util.OpenUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
@ -20,17 +23,25 @@ import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartRequest; import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor;
import org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.security.Principal;
import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap; import java.util.TimeZone;
/** /**
* 解析request参数中的业务参数绑定到方法参数上 * 解析request参数中的业务参数绑定到方法参数上
@ -40,15 +51,23 @@ import java.util.concurrent.ConcurrentHashMap;
@Slf4j @Slf4j
public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver { public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = new ConcurrentHashMap<>(256);
private ParamValidator paramValidator = new ServiceParamValidator(); private ParamValidator paramValidator = new ServiceParamValidator();
private RequestMappingHandlerAdapter requestMappingHandlerAdapter; @Nullable
private static Class<?> pushBuilder;
static {
try {
pushBuilder = ClassUtils.forName("javax.servlet.http.PushBuilder",
ServletRequestMethodArgumentResolver.class.getClassLoader());
} catch (ClassNotFoundException ex) {
// Servlet 4.0 PushBuilder not found - not supported for injection
pushBuilder = null;
}
}
@Override @Override
public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) { public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
List<HandlerMethodArgumentResolver> argumentResolversNew = new ArrayList<>(64); List<HandlerMethodArgumentResolver> argumentResolversNew = new ArrayList<>(64);
// 先加自己 // 先加自己
argumentResolversNew.add(this); argumentResolversNew.add(this);
@ -64,14 +83,42 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
if (lastOne != null) { if (lastOne != null) {
argumentResolversNew.add(lastOne); argumentResolversNew.add(lastOne);
} }
this.requestMappingHandlerAdapter.setArgumentResolvers(argumentResolversNew); requestMappingHandlerAdapter.setArgumentResolvers(argumentResolversNew);
} }
@Override @Override
public boolean supportsParameter(MethodParameter methodParameter) { public boolean supportsParameter(MethodParameter methodParameter) {
// 有注解 // 是否有注解
return methodParameter.getMethodAnnotation(ApiMapping.class) != null boolean hasAnnotation = methodParameter.hasMethodAnnotation(ApiMapping.class)
|| methodParameter.getMethodAnnotation(ApiAbility.class) != null; || methodParameter.hasMethodAnnotation(ApiAbility.class);
if (!hasAnnotation) {
return false;
}
Class<?> paramType = methodParameter.getParameterType();
if (paramType == OpenContext.class) {
return true;
}
// 排除的
boolean exclude = (
paramType.isInterface() ||
WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
Principal.class.isAssignableFrom(paramType) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType ||
ServletResponse.class.isAssignableFrom(paramType) ||
OutputStream.class.isAssignableFrom(paramType) ||
Writer.class.isAssignableFrom(paramType)
);
// 除此之外都匹配
return !exclude;
} }
@Override @Override
@ -81,14 +128,9 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
// JSR-303验证 // JSR-303验证
paramValidator.validateBizParam(paramObj); paramValidator.validateBizParam(paramObj);
return paramObj; return paramObj;
} else {
HandlerMethodArgumentResolver resolver = getOtherArgumentResolver(methodParameter);
if (resolver != null) {
return resolver.resolveArgument(methodParameter, modelAndViewContainer, nativeWebRequest, webDataBinderFactory);
} }
return null; return null;
} }
}
/** /**
@ -99,62 +141,26 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
* @return 返回参数绑定的对象没有返回null * @return 返回参数绑定的对象没有返回null
*/ */
protected Object getParamObject(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) { protected Object getParamObject(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) {
Class<?> parameterType = methodParameter.getParameterType();
// WebRequest / NativeWebRequest / ServletWebRequest
if (WebRequest.class.isAssignableFrom(parameterType)) {
if (!parameterType.isInstance(nativeWebRequest)) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + nativeWebRequest);
}
return nativeWebRequest;
}
// ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest
if (ServletRequest.class.isAssignableFrom(parameterType) || MultipartRequest.class.isAssignableFrom(parameterType)) {
return resolveNativeRequest(nativeWebRequest, parameterType);
}
// ServletResponse, HttpServletResponse
if (ServletResponse.class.isAssignableFrom(parameterType)) {
return resolveNativeResponse(nativeWebRequest, parameterType);
}
HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest(); HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
JSONObject requestParams = OpenUtil.getRequestParams(request); JSONObject requestParams = OpenUtil.getRequestParams(request);
// 方法参数类型 // 方法参数类型
Class<?> parameterType = methodParameter.getParameterType();
boolean isOpenContextParam = parameterType == OpenContext.class;
Class<?> bizObjClass = parameterType; Class<?> bizObjClass = parameterType;
boolean isOpenRequestParam = parameterType == OpenContext.class;
// 参数是OpenRequest,则取OpenRequest的泛型参数类型 // 参数是OpenRequest,则取OpenRequest的泛型参数类型
if (isOpenRequestParam) { if (isOpenContextParam) {
bizObjClass = this.getOpenRequestGenericParameterClass(methodParameter); bizObjClass = this.getOpenRequestGenericParameterClass(methodParameter);
} }
OpenContext openContext = new OpenContextImpl(requestParams, bizObjClass); OpenContext openContext = new OpenContextImpl(requestParams, bizObjClass);
ServiceContext.getCurrentContext().setOpenContext(openContext); ServiceContext.getCurrentContext().setOpenContext(openContext);
Object bizObj = openContext.getBizObject(); Object bizObj = openContext.getBizObject();
this.bindUploadFile(bizObj, nativeWebRequest); this.bindUploadFile(bizObj, request);
return isOpenRequestParam ? openContext : bizObj; return isOpenContextParam ? openContext : bizObj;
}
private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeRequest = webRequest.getNativeRequest(requiredType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeRequest;
}
private <T> T resolveNativeResponse(NativeWebRequest webRequest, Class<T> requiredType) {
T nativeResponse = webRequest.getNativeResponse(requiredType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + requiredType.getName() + "]: " + webRequest);
}
return nativeResponse;
} }
/** /**
* 获取泛型参数类型 * 获取泛型参数类型
*
* @param methodParameter 参数 * @param methodParameter 参数
* @return 返回泛型参数class * @return 返回泛型参数class
*/ */
@ -174,15 +180,14 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
* 将上传文件对象绑定到属性中 * 将上传文件对象绑定到属性中
* *
* @param bizObj 业务参数 * @param bizObj 业务参数
* @param nativeWebRequest * @param httpServletRequest
*/ */
protected void bindUploadFile(Object bizObj, NativeWebRequest nativeWebRequest) { protected void bindUploadFile(Object bizObj, HttpServletRequest httpServletRequest) {
if (bizObj == null) { if (bizObj == null) {
return; return;
} }
if (this.isMultipartRequest(nativeWebRequest)) { if (this.isMultipartRequest(httpServletRequest)) {
Object nativeRequest = nativeWebRequest.getNativeRequest(); MultipartHttpServletRequest request = (MultipartHttpServletRequest) httpServletRequest;
MultipartHttpServletRequest request = (MultipartHttpServletRequest) nativeRequest;
Class<?> bizClass = bizObj.getClass(); Class<?> bizClass = bizObj.getClass();
ReflectionUtils.doWithFields(bizClass, field -> { ReflectionUtils.doWithFields(bizClass, field -> {
ReflectionUtils.makeAccessible(field); ReflectionUtils.makeAccessible(field);
@ -193,33 +198,10 @@ public class ApiArgumentResolver implements SopHandlerMethodArgumentResolver {
} }
} }
protected boolean isMultipartRequest(NativeWebRequest nativeWebRequest) { protected boolean isMultipartRequest(HttpServletRequest request) {
return nativeWebRequest.getNativeRequest() instanceof MultipartHttpServletRequest; return request instanceof MultipartRequest;
} }
/**
* 获取其它的参数解析器
*
* @param parameter 业务参数
* @return 返回合适参数解析器没有返回null
*/
protected HandlerMethodArgumentResolver getOtherArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
List<HandlerMethodArgumentResolver> argumentResolvers = this.requestMappingHandlerAdapter.getArgumentResolvers();
for (HandlerMethodArgumentResolver methodArgumentResolver : argumentResolvers) {
if (methodArgumentResolver instanceof SopHandlerMethodArgumentResolver) {
continue;
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
public void setParamValidator(ParamValidator paramValidator) { public void setParamValidator(ParamValidator paramValidator) {
this.paramValidator = paramValidator; this.paramValidator = paramValidator;

@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -102,10 +103,19 @@ public class AlipayController {
Story bizObject = openContext.getBizObject(); Story bizObject = openContext.getBizObject();
// 获取appid,更多方法查看OpenContext类 // 获取appid,更多方法查看OpenContext类
String appId = openContext.getAppId(); String appId = openContext.getAppId();
System.out.println(appId); bizObject.setName("appId:" + appId + ", " + bizObject.getName());
return bizObject; return bizObject;
} }
@ApiMapping(value = "story.get", version = "2.3")
public Story getStory23(Story story, HttpServletRequest request) {
OpenContext openContext = ServiceContext.getCurrentContext().getOpenContext();
String appId = openContext.getAppId();
System.out.println(appId);
story.setName("appId:" + appId + ", " + story.getName() + ",ip:" + request.getLocalAddr());
return story;
}
// http://localhost:2222/getStory2 // http://localhost:2222/getStory2
// 遗留接口具备开放平台能力 // 遗留接口具备开放平台能力
@ApiAbility @ApiAbility

@ -344,6 +344,14 @@ public class Client {
private Map<String, ?> form; private Map<String, ?> form;
private Map<String, String> header; private Map<String, String> header;
private HttpTool.HTTPMethod httpMethod; private HttpTool.HTTPMethod httpMethod;
/**
* 返回json跟节点名称
* @return 返回json跟节点名称
*/
public String getDataNode() {
return method == null ? null : method.replace('.', '_') + "_response";
}
} }
} }

@ -105,6 +105,39 @@ public class AllInOneTest extends TestBase {
client.execute(requestBuilder); client.execute(requestBuilder);
} }
/**
* OpenContext参数绑定
*/
public void testOpenContext() {
Client client = new Client(url, appId, privateKey);
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get")
.version("2.2")
.bizContent(new BizContent().add("id", "222").add("name", "openContext"))
.httpMethod(HttpTool.HTTPMethod.GET)
.callback((requestInfo, responseData) -> {
System.out.println(responseData);
JSONObject jsonObject = JSON.parseObject(responseData);
String name = jsonObject.getJSONObject(requestInfo.getDataNode()).getString("name");
Assert.assertEquals(name, "appId:" + appId + ", openContext");
});
client.execute(requestBuilder);
}
/**
* 其它参数绑定
*/
public void testOtherParam() {
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("story.get")
.version("2.3")
.bizContent(new BizContent().add("id", "222").add("name", "request param"))
.httpMethod(HttpTool.HTTPMethod.GET);
client.execute(requestBuilder);
}
/** /**
* JSR-303参数校验 * JSR-303参数校验
*/ */
@ -118,7 +151,7 @@ public class AllInOneTest extends TestBase {
.callback((requestInfo, responseData) -> { .callback((requestInfo, responseData) -> {
System.out.println(responseData); System.out.println(responseData);
JSONObject jsonObject = JSON.parseObject(responseData); JSONObject jsonObject = JSON.parseObject(responseData);
String sub_msg = jsonObject.getJSONObject("goods_add_response").getString("sub_msg"); String sub_msg = jsonObject.getJSONObject(requestInfo.getDataNode()).getString("sub_msg");
Assert.assertEquals(sub_msg, "商品评论长度必须在3和20之间"); Assert.assertEquals(sub_msg, "商品评论长度必须在3和20之间");
}); });
@ -154,6 +187,7 @@ public class AllInOneTest extends TestBase {
* 演示文件上传 * 演示文件上传
*/ */
public void testFile() { public void testFile() {
Client client = new Client(url, appId, privateKey);
String root = System.getProperty("user.dir"); String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder() Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("demo.file.upload") .method("demo.file.upload")
@ -162,6 +196,36 @@ public class AllInOneTest extends TestBase {
// 添加文件 // 添加文件
.addFile("file1", new File(root + "/src/main/resources/file1.txt")) .addFile("file1", new File(root + "/src/main/resources/file1.txt"))
.addFile("file2", new File(root + "/src/main/resources/file2.txt")) .addFile("file2", new File(root + "/src/main/resources/file2.txt"))
.callback((requestInfo, responseData) -> {
JSONObject jsonObject = JSON.parseObject(responseData);
JSONObject data = jsonObject.getJSONObject(requestInfo.getDataNode());
Assert.assertEquals(data.getString("code"), "10000");
Assert.assertEquals(data.getJSONArray("files").size(), 2);
})
;
client.execute(requestBuilder);
}
/**
* 演示文件上传2
*/
public void testFile2() {
Client client = new Client(url, appId, privateKey);
String root = System.getProperty("user.dir");
Client.RequestBuilder requestBuilder = new Client.RequestBuilder()
.method("demo.file.upload2")
.version("1.0")
.bizContent(new BizContent().add("remark", "test file upload"))
// 添加文件
.addFile("file1", new File(root + "/src/main/resources/file1.txt"))
.addFile("file2", new File(root + "/src/main/resources/file2.txt"))
.callback((requestInfo, responseData) -> {
JSONObject jsonObject = JSON.parseObject(responseData);
JSONObject data = jsonObject.getJSONObject(requestInfo.getDataNode());
Assert.assertEquals(data.getString("code"), "10000");
Assert.assertEquals(data.getJSONArray("files").size(), 2);
})
; ;
client.execute(requestBuilder); client.execute(requestBuilder);
@ -235,7 +299,7 @@ public class AllInOneTest extends TestBase {
if (method == null) { if (method == null) {
return; return;
} }
String node = method.replace('.', '_') + "_response"; String node = requestInfo.getDataNode();
JSONObject jsonObject = JSON.parseObject(responseData).getJSONObject(node); JSONObject jsonObject = JSON.parseObject(responseData).getJSONObject(node);
String code = Optional.ofNullable(jsonObject).map(json -> json.getString("code")).orElse("20000"); String code = Optional.ofNullable(jsonObject).map(json -> json.getString("code")).orElse("20000");
Assert.assertEquals("10000", code); Assert.assertEquals("10000", code);

Loading…
Cancel
Save