2017-05-11 76 views
1

我定義的域類如下Spring Data 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; 


} 

當我張貼到端點http://localhost:8080/cities沒有的cityName我得到一個ConstraintViolationException但是當我發送一個PUT請求到端點http://localhost:8080/cities/1沒有的cityName我得到下面的異常而不是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 Data Rest,因此端點由Spring生成。沒有自定義的休息控制器。

+0

的可能的複製[PUT和POST失敗未知性質春天不同的行爲(http://stackoverflow.com/questions/34545997/put-and-post-fail-on -unknown-properties-spring-different-behavior) – pvpkiran

+1

你可以共享restcontroller類嗎? – barbakini

+0

我正在使用Spring Data Rest,因此Spring在運行時生成端點,因此不需要實現自定義控制器 –

回答

0

我認爲Cepr0的PUT和POST測試工作,因爲當你發送一個PUT請求給一個不存在的實體時,Spring Data Rest在後臺使用create方法。 假設有一個id = 100沒有用戶: 叫「把用戶/ 100」是與調用「POST用戶/」

當您發送PUT對現有的實體,它會產生討厭的TransactionSystemException。

我現在也在與Data 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()); 

    } 
} 

}