【问题标题】:ConstraintViolationException handler isn't executed in MicronautConstraintViolationException 处理程序未在 Micronaut 中执行
【发布时间】:2019-06-19 22:04:33
【问题描述】:

我有一个看起来像这样的ConstraintViolationException 处理程序类:

@Produces
@Singleton
@Requires(classes = {ConstraintViolationException.class, ExceptionHandler.class})
public class ConstraintsViolationsExceptionHandler
        implements ExceptionHandler<ConstraintViolationException, HttpResponse> {

    @Override
    public HttpResponse
    handle(HttpRequest request, ConstraintViolationException exception) {
        return HttpResponse
                .status(HttpStatus.FORBIDDEN)
                .contentType(MediaType.APPLICATION_JSON)
                .characterEncoding("UTF-8")
                .body(new SignUpPhoneNumberErrorResponse<>(400,
                        "Wrong data used",
                        new ArrayList<>(exception.getConstraintViolations())));
    }
}  

SignUpPhoneNumberErrorResponse 是我的错误处理 POJO,它被序列化为 JSON 非常好。

我的控制器如下所示:

@Controller(PhoneAuthAndLoginConstants.CONTROLLER_BASE_PATH)
@Validated
public class UserPhoneNumberRegistrationAndLoginController {

    @Inject
    MongoDbUserSignUpPhoneNumberDAO mongoDbUserSignUpPhoneNumberDAO;

    @Post(uri = PhoneAuthAndLoginConstants.CONTROLLER_SIGN_UP_PATH,
            consumes = MediaType.APPLICATION_JSON,
            produces = MediaType.APPLICATION_JSON)
    public Single<ResponseDataEncapsulate>
    signUpForPhoneVerification(@Valid @Body UserSignUpPhoneNumberEntity phoneNumber) {
        return mongoDbUserSignUpPhoneNumberDAO.sendVerification(phoneNumber);
    }

    @Post(uri = PhoneAuthAndLoginConstants.CONTROLLER_SIGN_UP_PATH
            +
            PhoneAuthAndLoginConstants.CONTROLLER_SIGN_UP_VERIFICATION_CODE_PARAM,
            consumes = MediaType.APPLICATION_JSON,
            produces = MediaType.APPLICATION_JSON)
    public Single<ResponseDataEncapsulate>
    sendUserSignUpConfirmation(@Valid @Body UserAccountStateSignUpEntity verificationData,
                               HttpHeaders httpHeaders) {
        return mongoDbUserSignUpPhoneNumberDAO.signUp(verificationData);
    }
}  

UserAccountStateSignUpEntity 的 POJO 如下所示:

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserAccountStateSignUpEntity implements UserSignUpEntity {
    @NotNull @NotBlank @Size(min = 5, max = 13) private String phoneNumber;
    @NotNull @NotBlank @Size(min = 7, max = 7) private String verificationCode;
    @JsonIgnore private Boolean verifiedAccount = Boolean.FALSE;

    public UserAccountStateSignUpEntity(String phoneNumber, String verificationCode) {
        this.phoneNumber = phoneNumber;
        this.verificationCode = verificationCode;
        this.verifiedAccount = Boolean.TRUE;
    }

    @Override
    public Map<String, Object> makePhoneEntityMapForMongo() {
        HashMap<String, Object> returnMap = new HashMap<String, Object>() {{
            put("phoneNumber", phoneNumber);
            put("verificationCode", verificationCode);
            put("verifiedAccount", verifiedAccount);
        }};

        return Collections.unmodifiableMap(returnMap);
    }
}  

我发送这样的请求负载:

{
    "phoneNumber" : "91-123456789",
    "verificationCode" : "18887"
}  

这应该会触发ConstraintViolationException 并且我的处理程序代码应该执行并且我应该得到一个 HTTP Forbidden。但相反,我收到了默认的 HTTP Bad Request 错误消息。

为什么我的处理程序没有被执行? 可以做些什么来让它执行?

我使用 Micronaut 1.1.3 作为 Web 框架,使用 Hibernate Validator 作为 javax.validation 实现。

【问题讨论】:

  • 你是使用io.micronaut.configuration:micronaut-hibernate-validator还是原来的hebernate依赖?看看docs.micronaut.io/1.2.0.RC1/guide/index.html#datavalidation
  • @tsarenkotxt 我尝试对我的 POJO 使用 @Introspected 注释,但仍然没有结果。在我的情况下,只有 @Introspected 注释才有意义。
  • @tsarenkotxt 我正在使用 Micronaut v1.1.3,您提供的指南包含 v1.2.0.RC1 的 @Inteospected 注释,而 v1.1.3 中没有。
  • 是的,你对版本是正确的,我的意思是你的类路径中的依赖项,以确保你添加io.micronaut.configuration:micronaut-hibernate-validator
  • @tsarenkotxt 我已经有了所需的依赖项,并且在日志中可见

标签: java bean-validation hibernate-validator micronaut


【解决方案1】:

@Error 可应用于将其映射到错误路由的方法,并且当发生任何 ConstraintViolationException 时,SignUpPhoneNumberErrorResponse 将作为错误响应正文返回。

更多详情请访问Micronaut docs

@Controller("/${path}")
@Validated
public class UserPhoneNumberRegistrationAndLoginController {

    @Post
    public HttpResponse method(@Valid @Body UserAccountStateSignUpEntity verificationData, HttpHeaders httpHeaders) {
        return null;
    }

    @Error(exception = ConstraintViolationException.class)
    public SignUpPhoneNumberErrorResponse onSavedFailed(HttpRequest request, ConstraintViolationException ex) {
        return new SignUpPhoneNumberErrorResponse(500,
                        "Wrong data used",
                        String.valueOf(ex.getConstraintViolations().stream().map( e -> e.getPropertyPath()+": "+e.getMessage()).collect(Collectors.toList())),
                "Application",
                "Error",
                System.currentTimeMillis());
    }

    @Error(status = HttpStatus.NOT_FOUND, global = true)  
    public HttpResponse notFound(HttpRequest request) {
        //return custom 404 error body
    }

} 

【讨论】:

  • 我知道@Error 注释,但它并没有很好地工作。唯一对我有用的解决方案是我已经回答的那个。
  • 相同的@Error 不适用于单独的侦听器。但是,它确实适用于 @Controller 类上的 ConstraintViolationException(添加到控制器类),但随后它在失败的验证路径中添加了控制器方法,例如myMethod.someClass.someFiled。看来@Error 必须在@Controller 上才能工作。
【解决方案2】:

我最近在 Micronaut 中遇到了同样的错误处理问题。据我所知,抛出的不是org.hibernate.exception.ConstraintViolationException,而是javax.persistence.PersistenceException。

对我来说,它适用于 @Error(exception = PersistenceException::class) (Kotlin)。

【讨论】:

    【解决方案3】:

    ConstraintViolationException 的@Error 注释处理仅在控制器上对我有用。

    最终通过替换 micronaut ConstraintExceptionHandler bean 设法像这样处理它:

    @Produces
    @Replaces(io.micronaut.validation.exceptions.ConstraintExceptionHandler.class)
    @Requires(classes = {ConstraintViolationException.class, ExceptionHandler.class})
    public class ConstraintExceptionHandler extends io.micronaut.validation.exceptions.ConstraintExceptionHandler {
    
        @Override
        public HttpResponse handle(HttpRequest request, ConstraintViolationException exception) {
            return return HttpResponse
                    .badRequest();
        }
    }
    

    【讨论】:

    【解决方案4】:

    我删除了@Requires(classes = {ConstraintViolationException.class, ExceptionHandler.class}) 注释并用逗号分隔的字符串替换了正文中的ArrayList&lt;&gt;,它开始正常工作。虽然我不知道删除注释的后果。

    【讨论】:

      【解决方案5】:

      我认为这里的错误是 Micronaut 已经为 ConstraintViolationException 定义了一个异常处理程序,如 here 所述

      为了覆盖这个,我建议从我链接的页面复制并粘贴定义。

      @Produces
      @Singleton
      @Primary
      @Requires(classes={javax.validation.ConstraintViolationException.class,ExceptionHandler.class})
      public class ConstraintExceptionHandler
      extends java.lang.Object
      implements ExceptionHandler<javax.validation.ConstraintViolationException,HttpResponse<JsonError>>
      

      (正文可以填写,extends java.lang.Object不是必须的)

      这里重要的部分是我添加了io.micronaut.context.annotation.Primary 注释。这将有利于您的处理程序而不是另一个定义的处理程序。我从 ConversionErrorHandler 偷了这个技巧,因为我遇到了和你一样的问题,但是使用了那个处理程序。

      现在您当然可以根据自己的需要将HttpResponse&lt;JsonError&gt; 更改为HttpResponse&lt;*&gt;。

      【讨论】:

        猜你喜欢
        • 2020-07-03
        • 1970-01-01
        • 2021-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-24
        • 2011-03-10
        • 1970-01-01
        相关资源
        最近更新 更多