• 解决SpringMVC @RequestBody无法注入基本数据类型


      我们都知道SpringMVC使用 @RequestBody 注解可以接收请求content-type 为 application/json 格式的消息体。但是我们必须使用实体对象,Map或者直接用String类型去接收数据。

      否则SpringMVC会直接把整个json字符串注入到参数中,此时用String类型的参数是可以接收的,但是用Integer,Long等其他类型会报JSON转换异常。

    org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize instance of `java.lang.Integer` 

      所以当我们只需要一个参数的请求时,要么不适用JSON格式,要么写一个实体对象,要么用map。因为项目一般会统一消息体,所以导致接收JSON参数非常麻烦。不过好在SpringMVC提供了HandlerMethodArgumentResolver这个让我们自定义参数解析器的接口。只要我们实现该接口,并添加到spring容器中即可。

      不足的地方:因为 HandlerMethodArgumentResolver 解析的参数是一个一个来的,而且因为httpServletRequest中的流只能读取一次,所以现在只能在一个参数的方法中使用,如果需要支持多个参数,需要对流进行处理。

    准备工作

    定义一个注解,用于标识接口参数和附带信息

    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface JsonBasicParam {
    
        /**
         * 字段名
         */
        String name() default "";
    
        /**
         * 是否必传
         */
        boolean required() default true;
    
        /**
         * 参数为空提示信息
         */
        String message() default "参数不能为空";
    
        /**
         * 默认值
         */
        String defaultValue() default "
    		
    		
    ue000ue001ue002
    				
    ";
    }

    实现逻辑

      定义一个类实现 HandlerMethodArgumentResolver接口

      注意事项:@Slf4j 是lombok的注解,相当于在类里面加入private  final Logger logger = LoggerFactory.getLogger(当前类名.class);个人习惯用这种方法输出日志。使用的是阿里的fastjson解析json字符串,所以需要导入fastjson包,或者自己用其他json框架也行。

    @Slf4j
    public class JsonParamProvider implements HandlerMethodArgumentResolver {
    
        /**
         * 请求body格式
         */
        private static final String CONTENT_TYPE = "application/json";
    
        /**
         * 判断是否是需要我们解析的参数类型
         */
        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            return methodParameter.hasParameterAnnotation(JsonBasicParam.class);
        }
    
        /**
         * 真正解析的方法
         */
        @Override
        public Object resolveArgument(@NonNull MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest,
                                      WebDataBinderFactory webDataBinderFactory) {
            HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
            if (request == null) {
                return null;
            }
            String contentType = request.getContentType();
            if (StringUtils.isEmpty(contentType) || !contentType.toLowerCase().contains(CONTENT_TYPE)) {
                throw new BaseException("只支持content-type为application/json的请求");
            }
            JSONObject jsonObject;
            try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(),
                    StandardCharsets.UTF_8))) {
                StringBuilder responseStrBuilder = new StringBuilder();
                String inputStr;
                while ((inputStr = streamReader.readLine()) != null) {
                    responseStrBuilder.append(inputStr);
                }
                jsonObject = JSON.parseObject(responseStrBuilder.toString());
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new BaseException("json格式异常");
            }
            JsonBasicParam jsonParam = methodParameter.getParameterAnnotation(JsonBasicParam.class);
            if (jsonParam == null) {
                return null;
            }
            //获取参数名
            String paramName = jsonParam.name();
            //注解没有给定参数名字,默认取参数名称
            if (StringUtils.isEmpty(paramName)) {
                paramName = methodParameter.getParameter().getName();
            }
            String paramType = methodParameter.getParameter().getType().getSimpleName();
            if (jsonObject != null && jsonObject.containsKey(paramName)) {
                String data = String.valueOf(jsonObject.get(paramName));
                if (jsonParam.required() && StringUtils.isEmpty(data)) {
                    throw new BaseException(jsonParam.message());
                }
                return initValue(paramType, data);
            }
            return null;
        }
    
        /**
         * 给基本类型参数注入值
         *
         * @param type 类型
         * @param data 值
         * @return java.lang.Object
         */
        private Object initValue(String type, String data) {
            try {
                if ("int".equalsIgnoreCase(type) || "integer".equalsIgnoreCase(type)) {
                    return Integer.valueOf(data);
                } else if ("double".equalsIgnoreCase(type)) {
                    return Double.valueOf(data);
                } else if ("long".equalsIgnoreCase(type)) {
                    return Long.valueOf(data);
                }
            } catch (NumberFormatException e) {
                log.error(e.getMessage(), e);
                throw new BaseException("数据类型错误");
            }
            return data;
        }
    }

    使用

    默认参数名称,必传,不传则提示message里面的内容
    @PostMapping("test1") public String test1(@JsonBasicParam(message = "testId不能为空") Integer testId) { return ""; }
    name指定参数名称
    @PostMapping("test1") public String test1(@JsonBasicParam(name = "testId",message = "testId不能为空") Integer id) { return ""; }
    必传,不传则提示默认的不能为空
    public
    String test1(@JsonBasicParam Integer id) { return ""; }
    可以不传
    @PostMapping("test1") public String test1(@JsonBasicParam(name = "testId",message = "testId不能为空",required = false) Integer testId) { return ""; }
    不传默认值为 1
    public
    String test1(@JsonBasicParam(name = "testId",message = "testId不能为空",required = false,defaultValue = "1") Integer testId) { return ""; }
  • 相关阅读:
    Silverlight 4中把DataGrid数据导出Excel
    C#正则的委托和lambda表达式用法
    C#简单的写日志方法
    GAE上传失败
    asp.net后台进程做定时任务
    ASP.NET页面生命周期描述
    巴士电台新版发布
    jQuery 1.51.7一些值得注意的更新
    wxPython应用心得
    Ajax保留浏览器历史的两种解决方案(Hash&Pjax)[转]
  • 原文地址:https://www.cnblogs.com/lwjQAQ/p/13689587.html
Copyright © 2020-2023  润新知