在使用SpringBoot开发Web应用程序的时候,不可避免的会出现发生错误的情况,而在SpringBoot中也提供了一个默认的错误路由映射/error,当系统出现错误的时候,会就将请求转发到这个错误页面来显示异常。那么我们如何在Web应用程序中实现一个统一的异常处理呢?下面我们就来看看如何实现这个需求。
使用@ControllerAdvice和@ExceptionHandler
在Spring Boot中提供了@ControllerAdvice和@ExceptionHandler两个注解,其中@ControllerAdvice是一个增强型的Controller,主要用来处理集中的多个Controller中的异常。其配合@ExceptionHandler来完成对特定异常的统一处理,如下所示。
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<?> resourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> globalExceptionHandler(Exception ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
在上面的例子中,我们自定义了一个ResourceNotFoundException异常类,而其中ErrorDetails则是一个自定义的错误相应结构。我们可以在GlobalExceptionHandler类中定义多个@ExceptionHandler注解标注的方法,这样我们就可以对不同类型的异常进行统一的处理了。
使用@RestControllerAdvice
当然如果,在我们的项目中使用的是RESTful风格的开发也就是说所有的Controller都是@RestController类型的,即返回JSON数据而不是视图,这个时候,我们就可以通过@RestControllerAdvice注解来代替@ControllerAdvice,因为@RestControllerAdvice注解从名称上看其实就是@ControllerAdvice注解的RESTFul的版本。我们可以通过如下的方式来使用它,如下所示。
@RestControllerAdvice
public class RestExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorDetails> handleResourceNotFoundException(ResourceNotFoundException ex) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), "Resource Not Found");
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDetails> handleGlobalException(Exception ex) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(), "Internal Server Error");
return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
这样,统一异常处理返回的结果就是一个JSON结构的数据,而不是视图对象了。当然除了上面的这两种方式我们还可以通过如下的方式来实现统一异常处理。
使用@ResponseStatus
在SpringBoot中如果我们想要对一些简单的异常进行处理,则不需要那么复杂的全局异常处理结构,我们可以直接在异常的类上通过@ResponseStatus注解,来实现异常统一处理,如下所示。
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
通过这种方式就可以在异常抛出的时候,返回指定的HTTP状态值可对应的异常信息。方便我们在调用端能够及时的定位异常错误。
自定义ErrorController
既然可以自定义异常,那么我们可不可以自定义Controller呢?答案是可以的,Spring Boot提供了一个默认的错误处理机制,我们可以通过ErrorController接口来自定义错误处理逻辑,对系统异常进行统一的处理,如下所示。
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
@Controller
public class CustomErrorController implements ErrorController {
@RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute("javax.servlet.error.status_code");
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "error-404";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error-500";
}
}
return "error";
}
@Override
public String getErrorPath() {
return "/error";
}
}
总结
上面的这几种方式在SpringBoot中实现应用程序的统一异常处理,在实际操作的过程中,我们可以根据具体的业务场景来选择合适的实现方式,如果项目比较大我们建议通过注解的方式来实现,因为可以实现统一的管理,如果项目较小的情况下,我们可以通过自定义ErrorController的方式或者是使用异常注解的方式来实现。具体选择哪一种还要根据具体的业务场景来定。