一、添加注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBodyParam {
/**
* 请求参数的名称 {@link #name}
*/
@AliasFor("name")
String value() default "";
/**
* 请求参数的名称 {@link #value}
*/
@AliasFor("value")
String name() default "";
/**
* 是否必填
* 默认值为 {@code true}, 如果请求中缺少参数, 将引发异常
* 如果请求中允许参数缺省, 或可以接收 {@code null}, 则需要设置为 {@code false}
* 或者提供一个默认值 {@link #defaultValue}, 它将隐式地将此标志设置为 {@code false}
*/
boolean required() default true;
/**
* 当未提供请求参数或值为空时的默认值
* 提供默认值会隐式地将 {@link #required} 设置为 {@code false}
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
二、编写注解的方法参数处理
public class RequestBodyParamMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final String POST = "post";
private static final String PUT = "put";
private static final String APPLICATION_JSON = "application/json";
/**
* 判断是否需要处理此参数
* @param parameter 方法的参数
* @return 如果此参数带有 @RequestSingleBody 注解则返回 {@code true}, 否则返回 {@code false}
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBodyParam.class);
}
/**
* 解析参数
* @param parameter 方法的参数
* @param mavContainer 上下文容器, 主要是承担着整个请求过程中数据的传递工作
* @param webRequest WebRequest 接口的扩展, 用于获取本机请求和响应对象
* @param binderFactory 字符串类型转换为字节类型的转换工具
* @return 请求参数
*/
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
String contentType = Objects.requireNonNull(servletRequest).getContentType();
String requestMethod = servletRequest.getMethod();
if (contentType == null || !contentType.contains(APPLICATION_JSON)) {
log.error("[RequestSingleBody] contentType must be " + APPLICATION_JSON);
throw new RuntimeException("[RequestSingleBody] contentType must be " + APPLICATION_JSON);
}
if (!POST.equalsIgnoreCase(requestMethod) && !PUT.equalsIgnoreCase(requestMethod)) {
log.error("[RequestSingleBody] requestMethod must be " + POST + "or " + PUT);
throw new RuntimeException("[RequestSingleBody] requestMethod must be " + POST + "or " + PUT);
}
return this.bindRequestParams(parameter, servletRequest);
}
/**
* 绑定请求参数
* @param parameter 方法的参数
* @param servletRequest 客户端的请求
* @return 请求参数
*/
private Object bindRequestParams(MethodParameter parameter, HttpServletRequest servletRequest) {
RequestBodyParam requestSingleBody = parameter.getParameterAnnotation(RequestBodyParam.class);
Class<?> parameterType = parameter.getParameterType();
String requestBody = this.getRequestBody(servletRequest);
log.info(requestBody);
Map paramObj = JSONObject.parseObject(requestBody, Map.class);
if (paramObj == null) {
paramObj = new JSONObject();
}
String parameterName = StringUtils.isBlank(requestSingleBody.value()) ? parameter.getParameterName() : requestSingleBody.value();
Object value = paramObj.get(parameterName);
// 方便測試
// log.info("parameterName:{}, value:{}", parameterName, value);
if (requestSingleBody.required()) {
if (value == null) {
log.error("[RequestSingleBody] " + parameterName + " is required");
throw new RuntimeException("[RequestSingleBody] " + parameterName + " is required");
}
}
// System.out.println("bindRequestParams convert value: " + value);
// System.out.println("bindRequestParams convert parameterType: " + parameterType);
// System.out.println("bindRequestParams convert: " + ConvertUtils.convert(value, parameterType));
return ConvertUtils.convert(value, parameterType);
}
/**
* 获取请求体
* @param servletRequest 请求
* @return 请求体字符串
*/
private String getRequestBody(HttpServletRequest servletRequest) {
StringBuilder stringBuilder = new StringBuilder();
try {
BufferedReader reader = servletRequest.getReader();
char[] buf = new char[1024];
int length;
while ((length = reader.read(buf)) != -1) {
stringBuilder.append(buf, 0, length);
}
log.info("看這裡:{}",stringBuilder);
} catch (IOException e) {
log.error("[RequestSingleBody] read request body error", e);
throw new RuntimeException("[RequestSingleBody] read request body error");
}
// System.out.println("getRequestBody stringBuilder: " + stringBuilder.toString());
return stringBuilder.toString();
}
}
三、重写HttpServletRequestWrapper
如果没有重写HttpServletRequestWrapper,会导致注解只能读到第一个,后面读不到流
public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
String sessionStream = getBodyString(request);
body = sessionStream.getBytes(Charset.forName("UTF-8"));
}
private String getBodyString(ServletRequest request) throws IOException {
StringBuilder sb = new StringBuilder();
InputStream ins = request.getInputStream();
try (BufferedReader isr = new BufferedReader(new InputStreamReader(ins, Charset.forName("UTF-8")));) {
String line = "";
while ((line = isr.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw e;
}
return sb.toString();
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() throws IOException {
return bais.read();
}
@Override
public boolean isFinished() {
return bais.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
};
}
}
四、添加过滤器
@WebFilter(filterName = "bodyReaderFilter", urlPatterns = "/*")
@Configuration
public class BodyReaderFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
ServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(httpServletRequest);
filterChain.doFilter(requestWrapper, servletResponse);
}
@Override
public void destroy() {
}
}
五、配置解析器
@Configuration
public class RequestBodyParamResolverConfig implements WebMvcConfigurer {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new RequestBodyParamMethodArgumentResolver());
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
}
}
六、使用
@PostMapping("/hello")
public String hello(@RequestBodyParam(required = false) String id, @RequestBodyParam(required = false) String name, @RequestBodyParam(required = false) String age) {
System.out.println(id);
System.out.println(name);
System.out.println(age);
return "hello";
}