如何将 ConstraintViolationException 500 错误转换为 400 错误请求?

Posted

技术标签:

【中文标题】如何将 ConstraintViolationException 500 错误转换为 400 错误请求?【英文标题】:How to convert ConstraintViolationException 500 error to 400 bad request? 【发布时间】:2020-02-25 02:28:30 【问题描述】:

如果我使用这样的约束 @NotNull 然后在控制器中

public User createUser(
            @Validated
            @RequestBody User user) 

它提供了一个非常不错的 400 异常细节。

但如果我像这样使用自己的自定义验证器:

public User createUser(
            @UserConstraint
            @RequestBody User user) 

它会抛出这样的 500 服务器错误:

javax.validation.ConstraintViolationException: createUser.user: Error with field: 'test35'
    at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:117) ~[spring-context-5.1.10.RELEASE.jar:5.1.10.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.10.RELEASE.jar:5.1.10.RELEASE]
    at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:69) ~[spring-security-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]

有没有办法在响应中获得漂亮的 400 消息?

理想情况下,400 消息应该与 Spring 的验证 JSON 相同


    "timestamp": "2019-10-30T02:33:15.489+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        
            "codes": [
                "Size.user.lastName",
                "Size.lastName",
                "Size.java.lang.String",
                "Size"
            ],
            "arguments": [
                
                    "codes": [
                        "user.lastName",
                        "lastName"
                    ],
                    "arguments": null,
                    "defaultMessage": "lastName",
                    "code": "lastName"
                ,
                25,
                1
            ],
            "defaultMessage": "size must be between 1 and 25",
            "objectName": "user",
            "field": "lastName",
            "rejectedValue": "",
            "bindingFailure": false,
            "code": "Size"
        
    ],
    "message": "Validation failed for object='user'. Error count: 1",
    "path": "/api/v1/users"

【问题讨论】:

问题解决方案:github.com/spring-projects/spring-boot/issues/… 【参考方案1】:

简单地说,在一个带有@ControllerAdvice注解的类中定义一个带有@ExceptionHandler注解的方法:

@ControllerAdvice
public class YourControllerAdvice 

    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public void handleConstraintViolationException() 
    // Intentionally left blank
    

带有@ControllerAdvice 注释的类用于处理控制器级别的异常。

【讨论】:

如果您将方法留空 - 响应正文将为空白。此外,您可以将此建议扩展到各种异常:@ExceptionHandler(value = ConstraintViolationException.class, ValidationException.class)【参考方案2】:

是的,您可以创建一个custom error handler,然后您也可以在您的回复和状态中添加任何内容。这是更改状态的简单方法:

1.- 抛出ConstraintViolationException 时更改status 的简单方法。

import javax.validation.ConstraintViolationException;

@ControllerAdvice
public class CustomErrorHandler 

    @ExceptionHandler(ConstraintViolationException.class)
    public void handleConstraintViolationException(ConstraintViolationException exception,
            ServletWebRequest webRequest) throws IOException 
        webRequest.getResponse().sendError(HttpStatus.BAD_REQUEST.value(), exception.getMessage());
    
    

2.- 发生ConstraintViolationException 时放置响应的自定义方式。

@ControllerAdvice
public class CustomErrorHandler 

    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<CustomError> handleConstraintViolationException(ConstraintViolationException exception) 
        CustomError customError = new CustomError();
        customError.setStatus(HttpStatus.BAD_REQUEST);
        customError.setMessage(exception.getMessage());
        customError.addConstraintErrors(exception.getConstraintViolations());
        return ResponseEntity.badRequest().body(customError);
    
   

【讨论】:

我想将 JSON 格式化为与 spring 的 JSON 相同的格式。你知道怎么做吗?我已经编辑了问题以显示格式 嗨@erotsppa,抱歉耽搁了,你还想这样做吗? 最重要的是,在 MVC 环境中,您如何继续使用控制器? ?。 Spring 抛出 400 实际上我有一个 BindingResult 参数,我在其中捕获所有错误并将它们显示在提交表单中。 @TheRealChx101 创建一个问题,以便我可以帮助您完成您想做的事情 谢谢。我找到了解决方案。你必须按一定的顺序排列你的参数:(@ModelAttribute Object, Errors, Model) 这样spring就不会抛出异常【参考方案3】:

由于上述解决方案并没有真正产生所需的结果,因此此处提供的链接可能会有所帮助:https://sterl.org/2020/02/spring-boot-hateoas-jsr303-validation/

如果类或方法请求主体使用@Validated 注解,那么足够有趣的 spring 的行为会有所不同。

换句话说,在课堂上,您可能会遇到 500 个错误。如果您将验证注释移到方法中,就像您已经做的那样,正常行为应该是 400。

长话短说,一旦您有了自定义包含等,您就需要稍微调整一下这些东西——就像在 Spring 中一样,它是 MethodArgumentNotValidException 而不是 ConstraintViolationException,Spring 已经作为控制器建议。

快速解决方案可能如下所示:

@Autowired
private MessageSource messageSource;

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public @ResponseBody Map<String, Object> handleConstraintViolation(ConstraintViolationException e, ServletWebRequest request) 
    // emulate Spring DefaultErrorAttributes
    final Map<String, Object> result = new LinkedHashMap<>();
    result.put("timestamp", new Date());
    result.put("path", request.getRequest().getRequestURI());
    result.put("status", HttpStatus.BAD_REQUEST.value());
    result.put("error", HttpStatus.BAD_REQUEST.getReasonPhrase());
    result.put("message", e.getMessage());
    result.put("errors", e.getConstraintViolations().stream().map(cv -> SimpleObjectError.from(cv, messageSource, request.getLocale())));
    return result;


@Getter @ToString
static class SimpleObjectError 
    String defaultMessage;
    String objectName;
    String field;
    Object rejectedValue;
    String code;

    public static SimpleObjectError from(ConstraintViolation<?> violation, MessageSource msgSrc, Locale locale) 
        SimpleObjectError result = new SimpleObjectError();
        result.defaultMessage = msgSrc.getMessage(violation.getMessageTemplate(),
                new Object[]  violation.getLeafBean().getClass().getSimpleName(), violation.getPropertyPath().toString(),
                        violation.getInvalidValue() , violation.getMessage(), locale);
        result.objectName = Introspector.decapitalize(violation.getRootBean().getClass().getSimpleName());
        result.field = String.valueOf(violation.getPropertyPath());
        result.rejectedValue = violation.getInvalidValue();
        result.code = violation.getMessageTemplate();
        return result;
    

【讨论】:

以上是关于如何将 ConstraintViolationException 500 错误转换为 400 错误请求?的主要内容,如果未能解决你的问题,请参考以下文章

如何将Ios文件上传到

Qt如何将文字变成图片?

如何将Bitmap保存为本地图片文件?

在MATLAB中如何将图导出

ASP如何将SQLSERVER数据导出到DBF(VF)

如何将CSV格式转换成JSON格式