我们都知道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 "";
}

解决SpringMVC/SpringBoot @RequestBody无法注入基本数据类型的更多相关文章

  1. 在Springmvc普通类@Autowired注入request为null解决方法

    在Springmvc普通类@Autowired注入request为null解决方法   在类中加入以下注入request对象的代码,运行时发现request为null,注入失败.在@Controlle ...

  2. Spring SpringMVC SpringBoot SpringCloud 注解整理大全

    Spring SpringMVC SpringBoot SpringCloud 注解整理 才开的博客所以放了一篇以前整理的文档,如果有需要添加修改的地方欢迎指正,我会修改的φ(๑˃∀˂๑)♪ Spri ...

  3. PHP开发中常见的安全问题详解和解决方法(如Sql注入、CSRF、Xss、CC等

    页面导航: 首页 → 网络编程 → PHP编程 → php技巧 → 正文内容 PHP安全 PHP开发中常见的安全问题详解和解决方法(如Sql注入.CSRF.Xss.CC等) 作者: 字体:[增加 减小 ...

  4. 解决SpringMVC拦截器中Request数据只能读取一次的问题

    解决SpringMVC拦截器中Request数据只能读取一次的问题 开发项目中,经常会直接在request中取数据,如Json数据,也经常用到@RequestBody注解,也可以直接通过request ...

  5. spring springMvc spring-boot spring-cloud分别是什么

    本文来源于:克己习礼成仁   的<spring springMvc spring-boot spring-cloud分别是什么> 什么是spring 关于spring的定义无论是从官方还是 ...

  6. Ajax json交互和SpringMVC中@RequestBody

    Ajax json交互和SpringMVC中@RequestBody 标签: 背景 自己提供出去得接口中参数设置为@RequestBody VipPromotionLog vipPromotionLo ...

  7. 开发者说:Sentinel 流控功能在 SpringMVC/SpringBoot 上的实践

    从用户的视角来感受一个开源项目的成长,是我们推出「开发者说」专栏的初衷,即在开发者进行开源项目选型时,提供更为立体的项目信息.专栏所有内容均来自作者原创/投稿,本文是「开发者说」的第6篇,作者 Jas ...

  8. SpringMVC处理脚本,SQL注入问题

    SpringMVC处理脚本,SQL注入问题(写的不好勿喷,互相学习) 使用 Filter 来过滤浏览器发出的请求,对每个URI参数请求过滤些关键字,替换成安全的字符.所有请求的 getParamete ...

  9. 正确理解springboot的常用注入方式

    springboot的属性注入 以注入dataSource为例1.springboot默认读取的文件是放在resources目录下的名为application.properties或applicati ...

  10. 解决SpringMVC put,patch,delete请求数据拿不到的问题

    解决SpringMVC put,patch,delete请求参数拿不到的问题 废话不多说,核心代码如下: 在web.xml中添加如下代码 <!-- 解决web端不能put,delete等请求的问 ...

随机推荐

  1. Leetcode数组-二分法

    Leetcode数组-二分法 二分法学习地址 二分法 704. 二分查找 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 targe ...

  2. C# 利用Autofac批量接口注入依赖【学习记录】

    背景: 本人在一位大佬的Colder框架中看到了这个接口注入,然后呢就想学习一下ioc思想与di设计模式.此写法给我的感觉就是 非常的 优雅 ,优雅永不过时.关于接口注入的概念和ioc和di具体是什么 ...

  3. 在 TypeScript 中,extends

    extends 是一个关键字,用于指定类型参数的约束.它在类型参数的声明中使用,以确保类型参数满足特定的条件. 具体来说,extends 后面可以跟随一个类型,表示类型参数必须是该类型的子类型.在泛型 ...

  4. DBEAVER 23.0.2 调整SQL编辑器字体大小 ver:20240112

    DBEAVER 23.0.2 调整SQL编辑器字体大小 ver:20240112 版本是:23.0.2. 菜单-窗口-首选项.用户界面-外观-颜色和字体.展开 DBeaver Font."M ...

  5. (五)基于selenium实现12306模拟登陆

    这里介绍一款强大验证码识别平台:超级鹰 - 超级鹰:http://www.chaojiying.com/about.html - 注册:普通用户 - 登录:普通用户 - 题分查询:充值 - 创建一个软 ...

  6. work12

    day12   一.请简述集合框架. 集合是单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.List和java.util.Set.其中,List的 ...

  7. 行为型模式(Behavioer Pattern)

    行为型设计模式 行为型模式定义了系统中对象之间的交互与通信,研究系统在运行时对象之间的相互通信与协作,进一步明确对象的职责,包括对系统中较为复杂的流程的控制. 在软件系统运行时对象并不是孤立存在的,它 ...

  8. Android 7.0 开机时间优化

    原文参考(有删改):https://www.jianshu.com/p/6dba42c022a9 问题描述 开机时间相对参考机过慢,大约慢15s左右.Android 系统7.0. 问题分析 开机问题涉 ...

  9. 记一次难忘的json反序列化问题排查经历

    前言 最近我在做知识星球中的商品秒杀系统,昨天遇到了一个诡异的json反序列化问题,感觉挺有意思的,现在拿出来跟大家一起分享一下,希望对你会有所帮助. 案发现场 我最近在做知识星球中的商品秒杀系统,写 ...

  10. python基础-入门必备知识

    1 标识符 标识符是编程时使用的名字,用于给变量.函数.语句块等命名,Python 中标识符由字母.数字.下划线组成,不能以数字开头,区分大小写. 以下划线开头的标识符有特殊含义,单下划线开头的标识符 ...