Springmvc-@RequestBody

SpringBoot-2.7.12

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

java 复制代码
@PostMapping("/static/user")
public User userInfo(@RequestBody(required = false) User user){
	user.setAge(19);
	return user;
}
java 复制代码
@PostMapping("/static/requiredUser")
public User requiredUserInfo(@RequestBody() User user){
	log.info(gson.toJson(user));
	user.setAge(19);
	return user;
}

SpringBoot-2.1.14.RELEASE

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

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

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

无body及日志输出原因

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

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

java 复制代码
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    private final Log log = LogFactory.getLog(this.getClass());

    @ExceptionHandler(Exception.class)
    @ResponseBody
    ReturnInfo 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);
    }
}

全局异常处理修改

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

ResponseEntityExceptionHandler

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

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

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

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

json 复制代码
{"iName" : "iName"}

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

json 复制代码
{"iname" : "iname"}

dto1

java 复制代码
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;
    }
}

传参格式:

json 复制代码
{
    "name": "name",
    "description": "description",
    "iname": "iname",
    "idescription": "idescription"
}

响应结果

json 复制代码
{
    "name": "name",
    "description": "description",
    "iname": "iname",
    "idescription": "idescription"
}

dto2

java 复制代码
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;
    }
}

接收参数:

json 复制代码
{
    "name": "name",
    "description": "description",
    "iName": "iName",
    "iDescription": "iDescription"
}

响应结果:

json 复制代码
{
    "name": "name",
    "description": "description",
    "iName": "iName",
    "iDescription": "iDescription"
}

dto3

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

java 复制代码
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;
    }
}

请求体解析

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

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

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

TypeVariable处理逻辑:

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

ParameterizedType支持嵌套处理

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

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

Jackson使用测试

java 复制代码
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();
    }
}
java 复制代码
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方法作为属性的重要依据

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

相关推荐
编程的一拳超人1 天前
Maven 国内高速镜像推荐(按速度排序)
java·maven
云烟成雨TD1 天前
Spring AI 1.x 系列【61】Spring AI 2.0 升级指南
java·人工智能·spring
lulu12165440781 天前
OpenRouter Fusion 多模型融合架构深度拆解:预算级模型组团打平 Fable 5,多模型协作才是 AGI 的正确打开方式?
java·人工智能·架构·ai编程·agi
雨辰AI1 天前
生产级实测:SpringBoot3 + 达梦数据库接口从 200ms 优化至 20ms 完整调优指南
java·数据库·spring boot·后端·政务
(Charon)1 天前
【C++ 面试高频:内存管理、RAII 和智能指针详解】
java·开发语言·word
凡人叶枫1 天前
Effective C++ 条款39:明智而审慎地使用 private 继承
java·数据库·c++·嵌入式开发
轻刀快马1 天前
跨越软硬件的共鸣(二):从 Cache 写策略看 Redis 与 DB 的一致性博弈
java·开发语言·redis·计算机组成原理
折哥的程序人生 · 物流技术专研1 天前
Java 23 种设计模式:从踩坑到精通 | 装饰器模式 —— 比继承更灵活的扩展方式,你用过吗?
java·装饰器模式·java面试·结构型模式·java设计模式·javaio·从踩坑到精通
lili00121 天前
2026 企业 AI 选型新范式:OpenRouter Fusion 证明多模型融合性价比远超单模型,企业该如何重构技术栈? - 微元算力(weytoken)
java·人工智能·python·重构·ai编程