2017-06-13 56 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