我们都知道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. Swift Lazy计算属性的线程安全分析

    一.代码示例 // // ViewController.swift // LazyTest // // Created by lilun.ios on 2021/7/30. // import UIK ...

  2. 关于 Linux 中模拟鼠标

    问题的背景是我想用自动化脚本来玩 Stardew Valley 的小游戏,刷钱,但是遇到了一系列问题,这里记录我的一些历程. pyautogui/pydirectinput pyautogui 是我第 ...

  3. kettle从入门到精通 第六十四课 ETL之kettle kettle中执行SQL脚本步骤,使用需当心

    1.群里有不定时会有同学反馈执行SQL脚本步骤使用有问题,那么咱们今天一起来学习下该步骤.trans中的执行SQL脚本有两方面功能,使用时需小心,不然很容易踩坑. 官方定义: 翻译: 您可以使用此步骤 ...

  4. Math Record

    T1.P3327 知识点:莫比乌斯反演,数论分块 我们知道 \(d(ij) = \sum_{x | i}\sum_{y | j}[\gcd(x,y) == 1]\). 所以我们就要求 \(\sum^n ...

  5. 华擎B365 BIOS 设置来电启动,来电自启,来电后开机

    开机 DEL 进入BIOS. F6 进入高级模式. 在Advanced \ Chipset Configuration ,拉到底,找到 Restore on AC/Power Loss,设置为 Pow ...

  6. 【译】Visual Studio 17.10 发布了新版扩展管理器

    我们将更新的扩展管理器带给所有用户!在过去的一年里,我们已经将更新后的扩展管理器作为可选的预览功能提供,并一直期待您的反馈.基于您令人难以置信的反馈,我们现在准备从 Visual Studio 17. ...

  7. ES进阶

    https://www.elastic.co/guide/en/elasticsearch/reference/current/cat.html 1.监控接口 访问es的_cat接口,获取不同的属性 ...

  8. 关于Android开机动画调试的一点小小的经验

    格式要求: 开机动画图片既可以是jpg文件,也可以是png文件,只是一定要按顺序命名.文件名命名的国际惯例是五位数,即:00000.00001. 00002--也可以是文件夹名称+下划线+序数,即:p ...

  9. Linux创建新用户时遇到的问题记录

    创建新用户命令: useradd -d "/home/guest" -m -s "/bin/bash" guest 报错: useradd: cannot op ...

  10. Linux使用Tomcat常用命令

    切换到tomcat主目录下 cd usr/local/tomcat # 1.启动tomcat服务 # 方式一:直接启动 bin/startup.sh # 方式二:作为服务启动(推荐) nohup bi ...