zoukankan      html  css  js  c++  java
  • SpringBoot:自定义注解实现后台接收Json参数

    0.需求

    在实际的开发过程中,服务间调用一般使用Json传参的模式,SpringBoot项目无法使用@RequestParam接收Json传参

    只有@RequestBody支持Json,但是每次为了一个接口就封装一次实体类比较麻烦

    如果使用Map来进行参数接收,则会导致参数不可控,会在接口中新增较多判断进行入参控制

    其次,在实际的开发过程中,我们偶尔会传入两个实体类,如果使用@RequestBody也会出错

    因为传入的参数只能够读取一次,一般这里也会封装一次实体类,不够方便

    也有重写HttpServletRequestWrapper的处理办法,但不能解决上一个问题

    1.思路

    因为一个注解只能读取一次,按照重写HttpServletRequestWrapper的思路,将请求中的Json参数进行缓存

    另外自定义一个注解,来把参数进行注入。

    1.1.自定义@JsonFmt注解

    import java.lang.annotation.*;
    
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface JsonFmt {
        /**
         * 值
         */
        String value() default "";
    
        /**
         * 是否必须
         */
        boolean require() default true;
    }

    这里的值,不是给参数的默认值(defaultValue),而是类似于@RequestParam注解中的value、name,是用来指定入参的key

    1.2.自定义注解的实现类

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.core.MethodParameter;
    import org.springframework.util.StringUtils;
    import org.springframework.web.bind.support.WebDataBinderFactory;
    import org.springframework.web.context.request.NativeWebRequest;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.method.support.ModelAndViewContainer;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.BufferedReader;
    import java.util.HashMap;
    import java.util.Map;
    
    @Slf4j
    public class JsonFmtHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
    
        //自定义key
        private static final String KEY = "TEST_JSON_BODY_KEY";
        private static ObjectMapper objectMapper = new ObjectMapper();
    
    
        @Override
        public boolean supportsParameter(MethodParameter parameter) {
            return parameter.hasParameterAnnotation(JsonFmt.class);
        }
    
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            JsonFmt jsonFmt = parameter.getParameterAnnotation(JsonFmt.class);
            JSONObject jsonObject = getJsonObject(webRequest);
    
            String value = getParamName(parameter,jsonFmt);
            boolean require = jsonFmt.require();
            Object paramValue = getParamValue(jsonObject,value);
    
            if (paramValue == null && require) {
                throw new Exception("parameter[" + value + "]不能为空。");
            }
            if (paramValue == null) {
                return null;
            }
    
            Class<?> classType = parameter.getParameterType();
    
            if (paramValue.getClass().equals(JSONObject.class)){
                paramValue = objectMapper.readValue(paramValue.toString(),classType);
            }
    
            return paramValue;
        }
    
        private String getParamName(MethodParameter parameter, JsonFmt jsonFmt) {
            String value = jsonFmt.value();
            if (StringUtils.isEmpty(value)) {
                value = parameter.getParameterName();
            }
            return value;
        }
    
        private Object getParamValue(JSONObject jsonObject,String value) {
            for (String key: jsonObject.keySet()) {
                if(key.equalsIgnoreCase(value)){
                    return jsonObject.get(key);
                }
            }
            return null;
        }
    
        private JSONObject getJsonObject(NativeWebRequest webRequest) throws Exception {
            String jsonBody = (String) webRequest.getAttribute(KEY, NativeWebRequest.SCOPE_REQUEST);
            if(StringUtils.isEmpty(jsonBody)){
                HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
                BufferedReader reader = request.getReader();
                StringBuilder sb = new StringBuilder();
                char[] buf = new char[1024];
                int rd;
                while ((rd = reader.read(buf)) != -1) {
                    sb.append(buf, 0, rd);
                }
    
                jsonBody = sb.toString();
    
                if(StringUtils.isEmpty(jsonBody)){
                    Map<String,String[]> params = request.getParameterMap();
    
                    Map tmp = new HashMap();
                    for (Map.Entry<String,String[]> param:params.entrySet()) {
                        if(param.getValue().length == 1){
                            tmp.put(param.getKey(),param.getValue()[0]);
                        }else{
                            tmp.put(param.getKey(),param.getValue());
                        }
    
                    }
                    jsonBody = JSON.toJSONString(tmp);
                }
    
                webRequest.setAttribute(KEY, jsonBody, NativeWebRequest.SCOPE_REQUEST);
            }
    
            return JSONObject.parseObject(jsonBody);
        }
    }

    方法说明:

    supportsParameter:说明支持的注解,只要方法参数有@JsonFmt就启用该实现类

    resolveArgument:解决方法,注解的具体实现

    getJsonObject:获取请求体,这里的实现逻辑就是从请求中获取Json体,如果没有获取到,则从请求参数中获取(兼容From模式),将请求体封装为JsonObject

    getParamName:获取注解参数的key,先获取注解的value,如果为空,则使用方法参数的名称

    getParamValue:这个可以不加,我这里是为了让key不区分大小写,如果需要区分,直接使用jsonObject.get(key)即可

    1.3.加入自定义注解

    import com.example.demo.jsonfmt.JsonFmtHandlerMethodArgumentResolver;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.method.support.HandlerMethodArgumentResolver;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import java.util.List;
    
    @Configuration
    public class AppConfig implements WebMvcConfigurer {
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
            resolvers.add(new JsonFmtHandlerMethodArgumentResolver());
        }
    
    }

    2.使用

    到这里我们就能愉快的使用我们的自定义注解@JsonFmt来进行参数接收了

    目前在Json传参中,能完美的接收实体类、List、Map以及其他基础类型

    在Form传参中,能够支持List、Map以及其他基础类型,对于实体类暂时还不能兼容

    因为后台接收到的是Map,不容易区分哪些是实体类的字段,无法进行填充,这种建议使用@RequestBody

  • 相关阅读:
    MySQL 对于千万级的大表要怎么优化?
    mysql数据库优化总结
    php 正则表达式怎么匹配标签里面的style?
    php一行代码获取本周一,本周日,上周一,上周日,本月一日,本月最后一日,上月一日,上月最后一日日期
    PHP过滤常用标签的正则表达式
    php冒泡排序
    PHP中__FUNCTION__与__METHOD__的区别
    关于PHP程序员技术职业生涯规划
    初识DSP
    MATLAB图像处理基础
  • 原文地址:https://www.cnblogs.com/fdzang/p/12857015.html
Copyright © 2011-2022 走看看