背景
- 通常要在拦截器层对请求参数进行打印, 校验等处理, 如果是 JsonBody 请求, 由于请求体只能消费一遍, 所以必须实现重复获取 JsonBody, 如何实现 ?
- 是否可以统一处理表单请求和 JsonBody 请求 ?
注解对比
@RequestParam | @RequestBody | @LokiParam | |
---|---|---|---|
表单请求 | ✔ | ||
✔ | |||
JsonBody 请求 | ✔ | ✔ | |
声明请求参数 | ✔ | ✔ | |
pojo 参数类型 | ✔ | ✔ |
@LokiParam 优点
- 同时支持表单请求与 JsonBody 请求
- 保持与 @RequestParam 一致的使用体验
- 声明接口请求参数
- 自动 Json 类型解析
实现
实现 JsonBody 缓存功能
ContentCachingRequestFilter
过滤器封装 JsonBody 请求
public class ContentCachingRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
// 包装 json body 请求
if (HttpUtil.isJsonBodyRequest((HttpServletRequest) request)) {
request = new JsonBodyCachingRequestWrapper((HttpServletRequest) request);
}
}
chain.doFilter(request, response);
}
}
JsonBodyCachingRequestWrapper
缓存 JsonBody, 实现重复获取
public class JsonBodyCachingRequestWrapper extends ContentCachingRequestWrapper {
private JSONObject jsonBody;
public JsonBodyCachingRequestWrapper(HttpServletRequest request) {
super(request);
}
/**
*
*/
public JSONObject getJsonBody() {
if (jsonBody != null) {
return jsonBody;
}
String body = HttpUtil.getStringBody(this);
if (StrUtil.isNotBlank(body)) {
jsonBody = JSON.parseObject(body);
} else {
jsonBody = new JSONObject();
}
return jsonBody;
}
/**
* 实现重复消费
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if (jsonBody == null) {
return super.getInputStream();
} else {
final InputStream is = new ByteArrayInputStream(jsonBody.toString().getBytes(StandardCharsets.UTF_8));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener listener) {
}
@Override
public int read() throws IOException {
return is.read();
}
};
}
}
}
实现 @LokiParam 功能
定义 @LokiParam
注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LokiParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
实现注解解析
public class LokiParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
public LokiParamMethodArgumentResolver() {
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(LokiParam.class)) {
return true;
}
return false;
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
LokiParam ann = parameter.getParameterAnnotation(LokiParam.class);
return new NamedValueInfo(ann.name(), ann.required(), ann.defaultValue());
}
@Override
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) {
String value = HttpUtil.getParam(request.getNativeRequest(HttpServletRequest.class), name);
if (StrUtil.isBlank(value)) {
return value;
}
Class<?> paramType = parameter.getNestedParameterType();
if (Object.class == paramType || BeanUtils.isSimpleProperty(paramType)) {
return value;
}
try {
return JSON.parseObject(value, paramType);
} catch (Exception e) {
throw new LokiException(ErrorCode.InvalidParameter, String.format("Invalid parameter '%s' of type %s", name, parameter.getNestedParameterType().getSimpleName()));
}
}
@Override
protected void handleMissingValue(String name, MethodParameter parameter) {
throw new LokiException(ErrorCode.InvalidParameter, String.format("Missing parameter '%s' of type %s", name, parameter.getNestedParameterType().getSimpleName()));
}
}