Springmvc-@RequestBody

news/2025/2/16 5:35:57/

SpringBoot-2.7.12

请求的body参数无法转换,服务端没有报错信息打印,而是响应的状态码是400

@PostMapping("/static/user")
public User userInfo(@RequestBody(required = false) User user){user.setAge(19);return user;
}

image-20240126215324187

@PostMapping("/static/requiredUser")
public User requiredUserInfo(@RequestBody() User user){log.info(gson.toJson(user));user.setAge(19);return user;
}

image-20240126215757844

SpringBoot-2.1.14.RELEASE

对应springframe一系列版本spring-web、spring-webmvc…版本5.1.15.RELEASE

image-20240126220714423

请求体参数无法接受,没有报错

@PostMapping("/static/requiredUser")
public User requiredUserInfo(@RequestBody() User user){log.info(gson.toJson(user));user.setAge(19);return user;
}

image-20240126221057373

无body及日志输出原因

是因为我这里有一段全局异常处理,但是这里的异常处理返回信息的结果和我上面接口的返回结果不一致。将断点打在这里,也会发现异常并没有进来(进来就会有错误日志了)。是因为我的全局异常类继承了ResponseEntityExceptionHandler这个类导致的。

发现去掉这个全局异常处理,响应结果如下:

@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {private final Log log = LogFactory.getLog(this.getClass());@ExceptionHandler(Exception.class)@ResponseBodyReturnInfo handleControllerException(HttpServletRequest request, Throwable ex) {HttpStatus status = getStatus(request);//return new ReturnInfo(status.value(), ex.getMessage());log.error(status.value(),ex);return ReturnInfo.buildErrorInfo(ex.getMessage());}private HttpStatus getStatus(HttpServletRequest request) {Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");if (statusCode == null) {return HttpStatus.INTERNAL_SERVER_ERROR;}return HttpStatus.valueOf(statusCode);}
}

image-20240127093318328

image-20240127093358907

全局异常处理修改

// 只监听这个的下面的异常处理
@ControllerAdvice(assignableTypes = {ExampleController.class})

ResponseEntityExceptionHandler

这个类里面有定义处理HttpMessageNotReadableException异常,这个指定特定异常的优先级可能更高,所以先在这里处理了,但是因为返回的结果类型不匹配,所以最终的响应体是空。

image-20240127100754414

请求体参数属性首字母小写

当出现属性变量名首字母小写时,idea自动生成的get/set格式是set/get[a-z][A-Z].*,首字母会小写,此时你的json的key是属性名,参数是能正常接受的。但是如果将set/get格式换成set/get[A-Z][A-Z].*格式,就会接收不到参数,说明Spring的自动解析参数是set或get方法来的。测试发现,参数接收/响应结果会根据类的get/set中的一个来确定属性的key,例如:

存在一个类中有属性iName,启中只有满足setiName/getiName一项,这个时候使用下面格式就能接收参数

{"iName" : "iName"}

如果都不满从setiName/getiName,而是setIName;getIName,则只能使用下面格式接收参数(并且响应的key和传递一样):

{"iname" : "iname"}

dto1

public class Goods {private String name;private String iName;private String description;private String iDescription;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIName() {return iName;}public void setIName(String iName) {this.iName = iName;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getIDescription() {return iDescription;}public void setIDescription(String iDescription) {this.iDescription = iDescription;}
}

传参格式:

{"name": "name","description": "description","iname": "iname","idescription": "idescription"
}

响应结果

{"name": "name","description": "description","iname": "iname","idescription": "idescription"
}

dto2

public class Goods {private String name;private String iName;private String description;private String iDescription;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getiName() {return iName;}public void setiName(String iName) {this.iName = iName;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getiDescription() {return iDescription;}public void setiDescription(String iDescription) {this.iDescription = iDescription;}
}

接收参数:

{"name": "name","description": "description","iName": "iName","iDescription": "iDescription"
}

响应结果:

{"name": "name","description": "description","iName": "iName","iDescription": "iDescription"
}

dto3

两种方式都存在,没有属性结果都被返回

public class Goods {private String name;private String iName;private String description;private String iDescription;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getIName() {return iName;}public void setIName(String iName) {this.iName = iName;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public String getIDescription() {return iDescription;}public void setIDescription(String iDescription) {this.iDescription = iDescription;}public String getiName() {return iName;}public void setiName(String iName) {this.iName = iName;}public String getiDescription() {return iDescription;}public void setiDescription(String iDescription) {this.iDescription = iDescription;}
}

image-20240127111037541

请求体解析

在AbstractJackson2HttpMessageConverter调用read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)方法解析请求体

  • type:@RequestBody注解的类
  • contextClass:被代理处理的controller接口类
  • inputMessage:HTTP请求体的消息

image-20240128132835031

获取要转换的请求的参数类型Type进行解析

image-20240128133804873

TypeVariable处理逻辑:

  1. 根据这个泛型参数所在的类,new ResolvableType(clazz)对象;
  2. 最终是要获取泛型所代表的具体类型

image-20240128134825773

ParameterizedType支持嵌套处理

image-20240128140440376

根据前面获取到的Type类型,是要ObjectMapper对象构建JavaType对象。

image-20240128140919917

最终使用ObjectMapper结合JavaType和输入流得到对象。默认的解析和转换都是以Jackson的规则来的

Jackson使用测试

public class JacksonUtil {private static final ObjectMapper objectMapper = new ObjectMapper();public static JavaType getJavaType(Type type){return objectMapper.getTypeFactory().constructType(type);}/*** 字符串转Java对象* @param json json字符串* @param javaType 最终解析的Java对象类Type对应的JavaType* @return 泛型对象* @param <T>*/public static <T> T stringToObject(String json, JavaType javaType){try {return objectMapper.readValue(json, javaType);} catch (IOException e) {throw new RuntimeException(e);}}/*** 输入流json转Java对象* @param inputStream json输入流* @param javaType 最终解析的Java对象类Type对应的JavaType* @return*/public static Object inputStreamToObject(InputStream inputStream, JavaType javaType){try {return objectMapper.readValue(inputStream, javaType);} catch (IOException e) {throw new RuntimeException(e);}}/**** @param json json字符串* @param clazz 结果类* @return json解析结果对象* @param <T>*/public static <T> T stringToClass(String json, Class<T> clazz){try {return objectMapper.readValue(json, clazz);} catch (IOException e) {throw new RuntimeException(e);}}/*** 对象转json字符串* @param object 要转成json的对象* @param javaType 对象的Type对应的JavaType* @return*/public static String objectToJson(Object object, JavaType javaType){try {return objectMapper.writerFor(javaType).writeValueAsString(object);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}/*** 普通对象转输出流* @param object 对象* @param out 输出流* @throws IOException*/public static void objectToPrintStream(Object object, OutputStream out) throws IOException {JsonGenerator generator = objectMapper.getFactory().createGenerator(out, JsonEncoding.UTF8);ObjectWriter writer = objectMapper.writer();writer.writeValue(generator, object);generator.flush();}
}
public class JacksonExample {public static void main(String[] args) throws IOException {String json = "{\n" +"    \"name\": \"name\",\n" +"    \"description\": \"description\",\n" +"    \"iName\": \"iName\",\n" +"    \"iDescription\": \"iDescription\"\n" +"}";JavaType javaType = JacksonUtil.getJavaType(Goods.class);Goods goods = JacksonUtil.stringToObject(json, javaType);Goods goods1 = JacksonUtil.stringToClass(json, Goods.class);System.out.println(JacksonUtil.objectToJson(goods, javaType));JacksonUtil.objectToPrintStream(goods1,System.out);}
}

在json的解析和转换成json的过程中,以对象的set/get方法作为属性的重要依据

image-20240128150445199

可以从下面的测试看出,get方法在解析和转换的过程中占主要地位

image-20240128151805876


http://www.ppmy.cn/news/1336055.html

相关文章

redis原理(五)Lua语言

一、介绍&#xff1a; 1、背景&#xff1a; 在 Redis 的 2.6 以上版本中&#xff0c;除了可以使用命令外&#xff0c;还可以使用 Lua 语言操作 Redis。 Redis 命令的计算能力并不算很强大&#xff0c;而使用 Lua 语言则在很大程度上弥补了 Redis 的这个不足。 2、特点&#…

单片机14-17

目录 LCD1602 LCD1602液晶显示屏 直流电机驱动&#xff08;PWM&#xff09; LED呼吸灯 直流电机调速 AD/DA&#xff08;SPI通信&#xff09; AD模数转换 DA数模转换 红外遥控&#xff08;外部中断&#xff09; 红外遥控 红外遥控电机调速 LCD1602 LCD1602液晶显示屏 …

让AI帮你说话--GPT-SoVITS教程

有时候我们在录制视频的时候&#xff0c;由于周边环境嘈杂或者录音设备问题需要后期配音&#xff0c;这样就比较麻烦。一个比较直观的想法就是能不能将写好的视频脚本直接转换成我们的声音&#xff0c;让AI帮我们完成配音呢&#xff1f;在语音合成领域已经有很多这类工作了&…

【干货】一文掌握JavaScript检查对象空值的N种技巧!

在开发 JavaScript 应用程序时&#xff0c;经常需要检查对象是否为空。这是因为在处理和操作对象数据时&#xff0c;我们需要确保对象包含有效的值或属性。 在本文中&#xff0c;我们将讨论如何检查对象是否为空&#xff0c;其中包括 JavaScript 中检查对象是否为空的不同方法…

【GAMES101】Lecture 09 纹理贴图 点查询与范围查询 Mipmap

目录 纹理贴图 纹理放大-双线性插值 点采样纹理所带来的问题 Mipmap 各向异性过滤 纹理贴图 我们在之前的着色里面说过如何给物体上纹理&#xff0c;就是对于已经光栅化的屏幕点&#xff0c;就是每个像素的中心&#xff0c;去寻找对应纹理的映射位置的纹理颜色&#xff0…

GD32移植FreeRTOS+CLI过程记录

背景 之前我只在STM32F0上基于HAL库和CubeMX移植FreeRTOS&#xff0c;但最近发现国产化替代热潮正盛&#xff0c;许多项目都有国产化器件指标&#xff0c;而且国产单片机确实比意法的便宜&#xff0c;所以也买了块兆易创新的GD32F303开发板&#xff0c;试一试它的优劣。虽然GD…

升级anaconda中python到3.10版本

需要使用函数pairwise&#xff0c;发现python版本偏低&#xff0c;尝试了把anaconda中jupyter notebook中的python环境升级到3.10。 步骤如下&#xff1a; 在Anaconda Prompt中依次执行以下命令&#xff1a; # 更新conda环境 conda update conda # 更新anaconda环境 conda u…

Leetcode刷题笔记题解(C++):1117. H2O 生成(多线程)

思路&#xff1a; 解法二&#xff1a;生产者-消费者解法 1.把 hydrogen 线程看作生产者&#xff0c;oxygen 线程看作消费者&#xff0c;缓冲队列大小为2。 2.hydrogen 把生成的氢放入队列&#xff1b;oxygen 线程每次从队列里消费两个氢元素。 3.生产者生产两个氢元素后会因为…