解决SpringMVC/SpringBoot @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 "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}

实现逻辑

  定义一个类实现 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 ""; }

 

posted @ 2020-09-18 09:54  房东的Tom  阅读(3547)  评论(0编辑  收藏  举报