自定义注解RequestBodyParam实现接收json数据

一、添加注解

@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";
   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值