在一个回报中总结所有异常

时间:2017-06-13 08:01:01

标签: java spring exception spring-boot

我开发了一个用于管理useres的应用程序spring-boot。我需要像这个对象一样处理异常:

{
  "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但是我不知道如何实现它

@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;
    }

祝你好运

2 个答案:

答案 0 :(得分: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来自Hibernate)。

现在,在您的控制器上,您现在可以放置:

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

由于@Valid注释,如果任何参数错误,则抛出BindException,您可以在控制器建议类中捕获,如下所示:

@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()));
    }
}

这里发生的是我调用getFieldErrors()的{​​{1}}方法,其中包含所有错误的列表。然后我将这些映射到类似于您想要的响应的响应类(BindExceptionErrorResponse):

SpecificErorr

如果您使用参数不足调用API,现在将获得以下JSON响应:

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;
    }
}

与此类似,您也可以抓住{ "errors": [ { "key": "postalCode", "value": "is required" }, { "key": "email", "value": "is empty" } ], "general_errors": "Validation failed" }

RuntimeException

但是,如果要将两者结合起来,则必须手动调用验证器,因为它的工作方式是抛出异常后,它将停止处理该方法。

这意味着如果您的方法抛出@ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(RuntimeException.class) @ResponseBody public ErrorResponse errorResponse(RuntimeException ex) { return new ErrorResponse(ex.getMessage(), null); } ,如果已经抛出验证错误,则不会发生这种情况。

答案 1 :(得分: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);
    }
 }