Springmvc-@RequestBody

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)
    @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);
    }
}

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

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值