You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring中JSR-303验证myDto.mail无对应访问器的问题解决

问题描述

我有如下DTO类结构:

public class MyDto {
    @Email
    private String mail;

    public String getMail() {
        return mail;
    }

    public void setMail(String mail) {
       this.mail = mail;
   }
}

@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-11-01T17:12:49.075897700+03:00[Europe/Moscow]")
public class CustomUserPatchRequestDto {
    @JsonProperty("myDto")
    @Valid
    private JsonNullable<@Valid MyDto> myDto;
    // getter and setter
    ...

控制器代码:

@RestController
@Validated
class MyController() {
  @RequestMapping(
        method = [PATCH],
        value = ["/userscustom/{id}"],
        produces = ["application/json"],
        consumes = ["application/merge-patch+json"]
    )
    fun patchUserCustom(
        @Parameter(name = "id", description = "", required = true,in= PATH) @PathVariable("id") id: String?
     @Valid @RequestBody userPatchRequestDto: CustomUserPatchRequestDto?
    ): ResponseEntity<String?>? {
        return ResponseEntity.ok("ok")
    }

当发送以下请求体时:

{
  "myDto": {
    "mail": "12sdfdsf3"
  }
}

收到500错误响应:

{
  "code": 500,
  "reason": "JSR-303 validated property 'myDto.mail' does not have a corresponding accessor for Spring data binding - check your DataBinder's configuration (bean property versus direct field access)"
}

我尝试过在各处添加@Valid注解,但没有效果。

尝试过的解决方案

我在控制器中添加了@InitBinder:

@InitBinder
private fun activateDirectFieldAccess(dataBinder: DataBinder) {
    println("Working Inner")
    dataBinder.initDirectFieldAccess();
}

也尝试过添加独立的ControllerAdvice类:

@ControllerAdvice
class CustomControllerAdvice {

    @InitBinder
    private fun activateDirectFieldAccess(dataBinder: DataBinder) {
        println("Working Inner")
        dataBinder.initDirectFieldAccess();
    }
}

两种情况控制台都输出了"Working Inner",说明代码已执行,但问题依旧存在。

补充说明

奇怪的是,当传入格式正确的邮箱时没有任何错误,一切正常。问题仅在传入无效邮箱时出现,且表现为上述异常。


解决方案

这个问题的核心是Spring在验证嵌套的JsonNullable对象时,无法正确解析属性路径——JsonNullable是OpenAPI生成的包装类,Spring默认验证器无法直接识别其内部的MyDto实例,导致验证失败时找不到对应的属性访问器。

解决步骤:

  1. 自定义JsonNullable验证适配器
    创建自定义验证器,主动处理JsonNullable内部对象的验证逻辑:

    import org.springframework.validation.Validator;
    import org.springframework.validation.Errors;
    import org.openapitools.jackson.nullable.JsonNullable;
    import javax.validation.Validation;
    import javax.validation.ValidatorFactory;
    import javax.validation.Validator;
    
    public class JsonNullableValidator implements Validator {
        private final Validator beanValidator;
    
        public JsonNullableValidator() {
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            this.beanValidator = factory.getValidator();
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return JsonNullable.class.isAssignableFrom(clazz);
        }
    
        @Override
        public void validate(Object target, Errors errors) {
            JsonNullable<?> jsonNullable = (JsonNullable<?>) target;
            if (jsonNullable.isPresent()) {
                Object value = jsonNullable.get();
                beanValidator.validate(value, errors);
            }
        }
    }
    
  2. 注册自定义验证器
    在控制器的@InitBinder中注册这个验证器,替换默认处理逻辑:

    @InitBinder
    fun initBinder(dataBinder: DataBinder) {
        dataBinder.addValidators(JsonNullableValidator())
        dataBinder.initDirectFieldAccess()
    }
    
  3. 调整DTO注解
    可以去掉JsonNullable字段上的@Valid,让自定义验证器统一处理内部对象的验证:

    public class CustomUserPatchRequestDto {
        @JsonProperty("myDto")
        private JsonNullable<@Valid MyDto> myDto;
        // getter and setter
        ...
    }
    

原理说明

当邮箱格式无效时,自定义验证器会主动检查JsonNullable是否包含有效值,然后对内部的MyDto实例执行验证,同时正确传递错误信息,避免Spring默认验证器因无法识别JsonNullable结构而抛出属性访问器异常。

另外,也可以尝试升级OpenAPI生成器版本,部分新版本已修复JsonNullable与Spring验证的兼容性问题。


内容的提问来源于stack exchange,提问作者gstackoverflow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:15:11