2017-06-13 73 views
2

我開發了一個應用程序spring-boot來管理useres。我需要處理這樣的對象例外:總結所有例外

{ 
    "general_errors": "service_unavailable" 
    "errors": [ 
    { 
     "key": "email", 
     "value": "is empty" 
    }, 
    { 
     "key": "postal_code", 
     "value": "required" 
    } 
    ] 
} 

舉例來說,在我的服務端時驗證是KO,我需要的是添加到錯誤列表。財產以後這樣的:

if (email == null) 
errors.addToErrorList("email", "is empty" 
... 

那麼如果有一個運行時異常

try { 
... 
}catch (InterruptedException e){ 
errors.addgeneral("general_errors", e.getMessage()); 
} 

請問您有什麼想法,我該怎麼辦呢?

我試着用@ControllerAdvice但我不khnow我如何可以實現

@ControllerAdvice 
@RestController 
public class GlobalExceptionHandler { 

    @ResponseStatus(HttpStatus.BAD_REQUEST) 
    @ExceptionHandler(value = BaseException.class) 
    public ErrorResponseWrapper handleBaseException(BaseException e) { 
     ErrorResponseWrapper error = new ErrorResponseWrapper(); 
     // error.setMessage(e.getMessage()); 
     return error; 
    } 

問候

+0

難道你不能只使用標準的JSR303驗證。示例https://www.mkyong.com/spring-mvc/spring-3-mvc-and-jsr303-valid-example/ BindingResult包含所有發現的錯誤。 – StanislavL

回答

2

要正確處理驗證錯誤,您可以使用JSR-303附帶Spring網絡。例如,假設您有一個控制器,它有兩個參數postalCodeemail。你可以創建一個名爲ApiParameters對象:

public class ApiParameters { 
    @NotNull(message = "is empty") 
    @Email(message = "is not an email") 
    private String email; 
    @NotNull(message = "is required") 
    private String postalCode; 

    public ApiParameters() { 
    } 

    // Getters + Setters 
} 

@NotNull@Email註解驗證註解(@Email是從休眠雖然)。現在

,在你的控制器,你現在可以把:

@GetMapping 
public String doStuff(@Valid ApiParameters parameters) { 
    // ... 
} 

由於@Valid註釋,如果任何參數是錯誤的,那麼BindException被拋出,你可以在一個catch控制器諮詢類,像這樣:

@ControllerAdvice 
public class ErrorHandler { 

    @ResponseStatus(HttpStatus.BAD_REQUEST) 
    @ExceptionHandler(BindException.class) 
    @ResponseBody 
    public ErrorResponse errorResponse(BindException ex) { 
     return new ErrorResponse("Validation failed", ex.getFieldErrors() 
      .stream() 
      .map(err -> new SpecificError(err.getField(), err.getDefaultMessage())) 
      .collect(Collectors.toList())); 
    } 
} 

這裏發生的是,我所說的BindExceptiongetFieldErrors()方法,它包含了所有錯誤的列表。然後映射那些類似於你想要的響應(ErrorResponseSpecificErorr)響應類:

public class ErrorResponse { 
    @JsonProperty("general_errors") 
    private String generalErrors; 
    private List<SpecificError> errors; 

    public ErrorResponse(String generalErrors, List<SpecificError> errors) { 
     this.generalErrors = generalErrors; 
     this.errors = errors; 
    } 

    public String getGeneralErrors() { 
     return generalErrors; 
    } 

    public List<SpecificError> getErrors() { 
     return errors; 
    } 
} 

public class SpecificError { 
    private String key; 
    private String value; 

    public SpecificError(String key, String value) { 
     this.key = key; 
     this.value = value; 
    } 

    public String getKey() { 
     return key; 
    } 

    public String getValue() { 
     return value; 
    } 
} 

如果你打電話給你的API與參數不夠,你現在將得到下面的JSON響應:

{ 
    "errors": [ 
     { 
      "key": "postalCode", 
      "value": "is required" 
     }, 
     { 
      "key": "email", 
      "value": "is empty" 
     } 
    ], 
    "general_errors": "Validation failed" 
} 

這個類似,您可以趕上RuntimeException S以及:

@ResponseStatus(HttpStatus.BAD_REQUEST) 
@ExceptionHandler(RuntimeException.class) 
@ResponseBody 
public ErrorResponse errorResponse(RuntimeException ex) { 
    return new ErrorResponse(ex.getMessage(), null); 
} 

但是,如果你想兩者結合起來,你會甲肝e手動調用驗證器,因爲這種方式的作用是一旦拋出異常,它將停止處理該方法。

這意味着如果您的方法會拋出RuntimeException,如果驗證錯誤已被拋出,則不會發生。

+0

Thanks @ g00glen00b我會測試一下 – Victor

+0

我實現了你的例子,但是我怎麼拋出異常。我試過'拋出新的ErrorHandler();'但它不起作用 – Victor

+0

@Victor你不必拋出'ErrorHandler'?爲什麼你會嘗試捕捉異常? – g00glen00b

0

我真的很喜歡這種模式。這是我的實現:

@ControllerAdvice 
public class GlobalExceptionHandler { 

    private final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class); 


    @ExceptionHandler(RuntimeException.class) 
    @ResponseBody 
    public ResponseEntity<ErrorWrapper> RunTimeException(RuntimeException e) { 
     e.printStackTrace(); 
     LOG.error("Error: {}", e.getMessage()); 
     return new ResponseEntity<ErrorWrapper>(new ErrorWrapper(e.getMessage()), HttpStatus.BAD_REQUEST); 
    } 

    @ExceptionHandler(ValidationException.class) 
    @ResponseBody 
    public ResponseEntity<List<ValidationError>> validationExceptionHandle(ValidationException e) { 
     e.printStackTrace(); 
     return new ResponseEntity<List<ValidationError>>(e.getErrors(), HttpStatus.BAD_REQUEST); 
    } 
} 

這裏是我的驗證方法:

private void validate(VisitTO to) { 
    List<ValidationError> errors = new ArrayList<>(); 
    Visit visit = new Visit(); 

    if (to.getDate() == null) { 
     LOG.error("Date was null."); 
     errors.add(new ValidationError("date", MAY_NOT_BE_NULL)); 
    } else { 
     Calendar cal = Calendar.getInstance(); 
     cal.setTime(to.getDate()); 
     if (cal.get(Calendar.HOUR_OF_DAY) < 15 || cal.get(Calendar.HOUR_OF_DAY) > 18) { 
      errors.add(new ValidationError("date", NOT_ALLOWED)); 
     } 
     to.setDate(roundTime(to.getDate())); 
    } 
    if (to.getId() != null) { 
     LOG.error("Id wasn't null."); 
     errors.add(new ValidationError("id", NOT_ALLOWED)); 
    } 
    if (to.getUserUuid() == null) { 
     LOG.error("UUID was null."); 
     errors.add(new ValidationError("user.uuid", MAY_NOT_BE_NULL)); 
    } else { 
     if (!LoggedUserUtils.isDoctor(LoggedUserUtils.getLoggedUser())) { 
      if (!to.getUserUuid().equals(LoggedUserUtils.getLoggedUser().getPesel())) { 
       throw new SecurityException(); 
      } 
     } 
    } 
    if (to.getCompleted() == null) { 
     LOG.error("Completed was null."); 
     errors.add(new ValidationError("completed", MAY_NOT_BE_NULL)); 
    } 
    if (userRepository.findByPesel(to.getUserUuid()) == null) { 
     LOG.error("User not found."); 
     errors.add(new ValidationError("pesel", NO_SUCH_USER)); 
    } 

    if (!errors.isEmpty()){ 
     throw new ValidationException(errors); 
    } 
} 
+1

你爲什麼喜歡這種方法?難以維護,難以本地化或提供自定義消息,難以組合,需要在UI上手動處理。基於註釋的JSR303會自動執行此操作,並提供大量有用的東西 – StanislavL

+0

在我看來,它是完美的,例如,對於批量上傳,當您必須將所有錯誤列出給用戶時,他才能修復文件。在json中返回錯誤列表很容易在前端使用 – xenteros

+1

@xenteros JSR303是避免所有這些「if()」語句的簡單方法。它仍然允許您發送JSON中的錯誤列表。 – g00glen00b