Spring Boot处理x-www-form-urlencoded请求时DTO字段映射失效问题
@JsonProperty、@SerializedName属于JSON序列化框架专属注解,仅在@RequestBody配合JSON消息转换器的场景下生效。Spring MVC处理application/x-www-form-urlencoded请求的@ModelAttribute参数绑定时,默认通过内置WebDataBinder按JavaBean原生属性名匹配请求参数,不会识别JSON序列化类注解,因此别名配置无法生效。
未显式添加@ModelAttribute时绑定逻辑不变,是因为Spring MVC默认将非简单类型的控制器参数按@ModelAttribute规则解析。
方案1:DTO字段加Spring原生绑定注解(无额外依赖,改动最小)
直接在DTO需要做别名映射的字段上添加Spring Web原生的参数绑定注解,@ModelAttribute解析时会自动识别注解配置的别名,无需修改控制器代码:
@Data public class PlateInquiryRequestDto { private String plate; // 指定表单中type参数映射到该字段 @RequestParam("type") private Boolean hasNaj; // 指定表单中reason_id参数映射到该字段 @RequestParam("reason_id") private Integer reasonId; private String userToken; // 直接在字段上绑定请求头,无需在控制器方法参数中单独声明 @RequestHeader("deviceid") private Integer deviceId; private String token; }
如果需要做类型转换(比如示例中type传1/0要转成Boolean类型),可搭配自定义Converter或Formatter注册到Spring上下文即可全局生效。
方案2:控制器方法显式绑定参数(适合字段数量少的场景)
如果不想修改DTO定义,可直接在控制器方法上声明别名参数,手动组装DTO:
@PostMapping(path = "/inquiry", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public InsuranceInformationDto inquiryByPlate( @RequestParam String plate, @RequestParam("type") Integer typeParam, @RequestParam("reason_id") Integer reasonId, @RequestParam String token, @RequestHeader(value = "deviceid") Integer deviceId ) { PlateInquiryRequestDto inquiryRequestDTO = new PlateInquiryRequestDto(); inquiryRequestDTO.setPlate(plate); inquiryRequestDTO.setHasNaj(typeParam != null && typeParam == 1); inquiryRequestDTO.setReasonId(reasonId); inquiryRequestDTO.setToken(token); inquiryRequestDTO.setDeviceId(deviceId); // 执行业务逻辑 return insuranceService.doInquiry(inquiryRequestDTO); }
方案3:自定义表单转换器复用Jackson序列化逻辑(适合需要统一注解规则的场景)
如果需要让@JsonProperty等Jackson注解在表单请求中也生效,可以自定义FormHttpMessageConverter,将urlencoded格式的参数解析为Map后,直接复用Jackson的ObjectMapper反序列化为目标DTO,让表单请求和JSON请求走完全一致的反序列化逻辑。
配置完成后控制器参数使用@RequestBody声明,同时指定接口只接收application/x-www-form-urlencoded类型请求即可,既满足业务编码要求,又能正常使用所有Jackson序列化注解。
内容的提问来源于stack exchange,提问作者Mahmood Kohansal

