在Micronaut中未执行ConstraintViolationException处理程序

时间:2019-06-19 22:04:33

标签: java bean-validation hibernate-validator micronaut

我有一个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禁止。但是相反,我得到了默认的HTTP Bad Request错误消息。

为什么我的处理程序没有被执行? 如何使其执行?

我正在使用 Micronaut 1.1.3 作为网络框架,并使用 Hibernate Validator 作为javax.validation实现。

5 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

对我来说,它与@Error(exception = PersistenceException::class)(科特琳)一起使用。

答案 2 :(得分:1)

@Error

ConstraintViolationException 注释处理仅在控制器上时才对我有用。

最终设法通过替换 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();
    }
}

答案 3 :(得分:0)

可以应用于方法以将其映射到错误路由的

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

} 

答案 4 :(得分:0)

我认为这里的错误是 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<JsonError> 更改为适合您需要的 HttpResponse<*>