有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Spring数据Rest Bean验证未应用于PUT方法?

我有一个定义如下的域类

@Data
@Entity
public class City {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private long cityID;

    @NotBlank(message = "City name is a required field")
    private String cityName;


}

当我在没有cityName的情况下发布到端点http://localhost:8080/cities时,我会得到一个ConstraintViolationException,但当我在没有cityName的情况下向端点http://localhost:8080/cities/1发送PUT请求时,我会得到以下异常,而不是ConstraintViolationException

{
  "timestamp": 1494510208982,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "org.springframework.transaction.TransactionSystemException",
  "message": "Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction",
  "path": "/cities/1"
}

那么,如何为PUT请求获取ConstraintViolationException异常呢

注意:我使用的是Spring数据Rest,因此端点由Spring生成。没有自定义的rest控制器


共 (2) 个答案

  1. # 1 楼答案

    我的解决方法是设置一个异常处理程序来处理TransactionSystemException,打开异常并像普通的ConstraintViolationException一样处理:

    @ExceptionHandler(value = {TransactionSystemException.class})
    public ResponseEntity handleTxException(TransactionSystemException ex) {
        Throwable t = ex.getCause();
        if (t.getCause() instanceof ConstraintViolationException) {
            return handleConstraintViolation((ConstraintViolationException) t.getCause(), null);
        } else {
            return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
    
    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex,
              WebRequest request) {
        List<String> errors = new ArrayList<>();
        for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
            errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage());
        }
    
        return new ResponseEntity<>(errors, new HttpHeaders(), HttpStatus.CONFLICT);
    }
    
  2. # 2 楼答案

    我认为Cepr0对PUT和POST都进行了测试,因为当您为不存在的实体发送PUT请求时,SpringDataREST在后台使用create方法。 假设没有id=100的用户: 调用“PUT users/100”与调用“POST users/”相同

    当您为现有实体发送PUT时,它将生成讨厌的TransactionSystemException

    我现在也在与数据Rest异常处理作斗争,其中有很多不一致之处

    这是我当前的RestErrorAttributes类,它解决了我的大部分问题,但我很有可能在接下来的日子里喜欢上其他人。:)

    @Component
    @Slf4j
    public class RestErrorAttributes extends DefaultErrorAttributes implements MessageSourceAware {
    
    private MessageSource messageSource;
    
    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messageSource = messageSource;
    }
    
    /** {@inheritDoc} */
    @Override
    public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) {
    
        final Map<String, Object> errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
        // Translate default message by Locale
        String message = errorAttributes.get("message").toString();
        errorAttributes.put("message",
                messageSource.getMessage(message, null, message, LocaleContextHolder.getLocale()));
        // Extend default error message by field-errors
        addConstraintViolationDetails(errorAttributes, requestAttributes);
        return errorAttributes;
    }
    
    private void addConstraintViolationDetails(Map<String, Object> errorAttributes,
            RequestAttributes requestAttributes) {
        Throwable error = getError(requestAttributes);
        if (error instanceof ConstraintViolationException) {
            errorAttributes.put("errors",
                    RestFieldError.getErrors(((ConstraintViolationException) error).getConstraintViolations()));
        }
        else if (error instanceof RepositoryConstraintViolationException) {
            errorAttributes.put("errors", RestFieldError
                    .getErrors(((RepositoryConstraintViolationException) error).getErrors().getAllErrors()));
        }
    }
    
    /** {@inheritDoc} */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) {
    
        try {
            Throwable cause = ex;
            while (cause instanceof Exception) {
                // Handle AccessDeniedException - It cannot be handled by
                // ExceptionHandler
                if (cause instanceof AccessDeniedException) {
                    response.sendError(HttpStatus.FORBIDDEN.value(), cause.getMessage());
                    super.resolveException(request, response, handler, (Exception) cause);
                    return new ModelAndView();
                }
                // Handle exceptions from javax validations
                if (cause instanceof ConstraintViolationException) {
                    response.sendError(HttpStatus.UNPROCESSABLE_ENTITY.value(), "validation.error");
                    super.resolveException(request, response, handler, (Exception) cause);
                    return new ModelAndView();
                }
                // Handle exceptions from REST validator classes
                if (cause instanceof RepositoryConstraintViolationException) {
                    response.sendError(HttpStatus.UNPROCESSABLE_ENTITY.value(), "validation.error");
                    super.resolveException(request, response, handler, (Exception) cause);
                    return new ModelAndView();
                }
                cause = ((Exception) cause).getCause();
            }
        } catch (final Exception handlerException) {
            log.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
        }
        return super.resolveException(request, response, handler, ex);
    }
    
    @Getter
    @AllArgsConstructor
    public static class RestFieldError {
        private String field;
        private String code;
        private String message;
    
        public static List<RestFieldError> getErrors(Set<ConstraintViolation<?>> constraintViolations) {
            return constraintViolations.stream().map(RestFieldError::of).collect(Collectors.toList());
        }
    
        public static List<RestFieldError> getErrors(List<ObjectError> errors) {
            return errors.stream().map(RestFieldError::of).collect(Collectors.toList());
        }
    
        private static RestFieldError of(ConstraintViolation<?> constraintViolation) {
            return new RestFieldError(constraintViolation.getPropertyPath().toString(),
                    constraintViolation.getMessageTemplate(), constraintViolation.getMessage());
    
        }
    
        private static RestFieldError of(ObjectError error) {
    
            return new RestFieldError(error instanceof FieldError ? ((FieldError) error).getField() : null,
                    error.getCode(), error.getDefaultMessage());
    
        }
    }
    

    }