当前位置: 首页 > article >正文

诡异的Spring @RequestBody驼峰命名字段映射失败为null问题记录

问题

一个非常常规的Spring Controller,代码如下:

import lombok.RequiredArgsConstructor;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/config")
public class ConfigController {
    private final ConfigService configService;

    @ApiOperation("新增配置")
    @PostMapping("")
    public R<ConfigVo> createConfig( /*@Valid*/ @RequestBody ConfigDto dto) {
        return R.success(configService.createConfig(dto));
    }
}

DTO定义如下:

import io.swagger.annotations.ApiModel;
import lombok.*;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("配置")
public class ConfigDto {
    @NotBlank
    private String appId;
    private String json;
    private String mark;
}

Postman模拟一次POST请求:

{
    "mark": "verifyUserByIdAndName",
    "json": "{}",
    "appId": "a_15c140390de54948af88ad9b39d1fd2f"
}

代码逻辑不对劲,然后通过断点调试,发现appId字段为null:
在这里插入图片描述
WTF???有毒吧。

现象就是驼峰命名的字段解析不出来,映射不上。

@Valid

咨询DeepSeek,一大堆废话。提取到关键词@Valid。

给这个Controller加个@Valid注解,也就是上面的代码片段里被注释的/*@Valid*/,Postman请求报错:
在这里插入图片描述
这个报错是框架类的封装然后返回的。说明Spring(Jackson)确实没有从Postman的request里解析映射出appId字段。

框架类代码片段:

private static final MediaType MEDIA_TYPE = new MediaType("application", "json", StandardCharsets.UTF_8);

/**
 * JSR303 表单参数校验失败
 * 在Controller层使用@Valid注解
 */
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class})
public ResponseEntity<R<?>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e,
                                                                  HttpServletRequest request) {
    this.logWarn(e, request, "不合法的参数异常");
    InvalidField invalidField = getInvalidField(e.getBindingResult());
    R<InvalidField> invalidFieldR = new R<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "不合法的参数异常", invalidField);
    return createResponseEntity(HttpStatus.BAD_REQUEST, invalidFieldR);
}

private void logWarn(Exception e, HttpServletRequest request, String... msg) {
    String template = "[Web][有Warn被抛出] >> Warn类=[%s], URI=[%s], 消息=[%s], Warn=[%s]";
    log.warn(String.format(template, e.getClass().getName(), request.getRequestURI(),
            ArrayUtil.isEmpty(msg) ? e.getMessage() : ArrayUtil.join(msg, ","), ExceptionUtil.stacktraceToString(e)));
}

private InvalidField getInvalidField(BindingResult bindingResult) {
    if (bindingResult == null) {
        return null;
    }
    FieldError fieldError = bindingResult.getFieldError();
    if (fieldError == null) {
        return null;
    }
    return InvalidField.builder()
            .fieldName(fieldError.getField())
            .message(fieldError.getDefaultMessage())
            .build();
}

protected static ResponseEntity<R<?>> createResponseEntity(HttpStatus httpStatus, R<?> body) {
    return ResponseEntity.status(httpStatus.value()).contentType(MEDIA_TYPE).body(body);
}

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class InvalidField {
    private String fieldName;
    private String message;
}

断点调试截图:
在这里插入图片描述

workaround

继续询问DeepSeek,又是一大堆废话,提取到关键词@JsonProperty。

一开始并没有关注这个,因为这个注解的使用场景是字段命名不匹配。

试试吧,增加@JsonProperty("appId"),也就是

public class ConfigDto {
    @NotBlank
    @JsonProperty("appId")
    private String appId;
}

能解析到appId字段。


但是!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


这两个地方的命名,明明是一模一样的啊。

分析

为啥呢?

Lombok

不是Lombok的问题,其他字段都没有问题,再说appId是一个非常常规的驼峰命名,又不是isSuccesscAppId这种不规范命名。

编译后的代码如下,没有问题
在这里插入图片描述

调试

在DTO里的字段加断点调试,
在这里插入图片描述
在Controller层加断点调试说明@JsonProperty生效:
在这里插入图片描述

为啥呢??

一个比较相似的blog,不过他这篇里的命名不太规范。

参考


http://www.kler.cn/a/505360.html

相关文章:

  • JSON 文本的多层嵌套格式
  • RabbitMQ(三)
  • Mac玩Steam游戏秘籍!
  • 【WEB】网络传输中的信息安全 - 加密、签名、数字证书与HTTPS
  • Web开发(二)CSS3基础与进阶
  • 【k8s】用户和服务账户联系(user、serviceaccount、sa)
  • Android Studio 警告信息:Use start instead of left to ensure...
  • 基于阿里云容器服务Kubernetes版(ACK)| 容器化管理云上应用
  • 非PHP开源内容管理系统(CMS)一览
  • 【ArcGIS技巧】如何给CAD里的面注记导入GIS属性表中
  • 解锁C++性能密码:TCMalloc深度剖析
  • 麒麟操作系统服务架构保姆级教程(十)rewrite跳转
  • linux环境使用docker部署多个war项目
  • rtthread学习笔记系列--24 waitqueue
  • 在 Docker 中安装并运行三个 MySQL 数据库
  • 快速上手 HarmonyOS 应用开发
  • np.gradient() 获取单个,一维,二维坐标点的梯度值
  • Oracle分析工具-Logminer手动指定归档文件
  • Tabby - 开源的自托管 AI 编码助手
  • 计算机网络速成
  • centos 7 CA认证中心
  • ChatGLM:从GLM-130B到GLM-4全系列大语言模型
  • ARM与x86:架构对比及其应用
  • AWS云计算概览(自用留存)
  • 适配器模式案例
  • 【2024年华为OD机试】 (B卷,100分)- 太阳能板最大面积(Java JS PythonC/C++)