前言
这篇教程主要专注于如何优雅的处理WEB中的异常。虽然我们可以手动的设置ResponseStatus
,但是还有更加优雅的方式将这部分逻辑隔离开来。Spring提供了整个应用层面的异常处理的抽象,并且只是要求您添加一些注释 - 它会处理其他所有内容。下面是一些代码的示例
如何手动处理异常
下面的代码中, DogController
将返回一个ResponseEntity
实例,该实例中包含返回的数据和HttpStatus
属性
- 如果没有抛出任何异常,则下面的代码将会返回
List<Dog>
数据作为响应体,以及200作为状态码 - 对于
DogsNotFoundException
,它返回空的响应体和404状态码 - 对于
DogServiceException
, 它返回500状态码和空的响应体
@RestController
@RequestMapping("/dogs")
public class DogsController {
@Autowired private final DogsService service;
@GetMapping
public ResponseEntity<List<Dog>> getDogs() {
List<Dog> dogs;
try {
dogs = service.getDogs();
} catch (DogsServiceException ex) {
return new ResponseEntity<>(null, null, HttpStatus.INTERNAL_SERVER_ERROR);
} catch (DogsNotFoundException ex) {
return new ResponseEntity<>(null, null, HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(dogs, HttpStatus.OK);
}
}
这种处理异常的方式最大的问题就在于代码的重复。catch部分的代码在很多其它地方也会使用到(比如删除,更新等操作)
Controller Advice
Spring提供了一种更好的解决方法,也就是Controller Advice。它将处理异常的代码在应用层面上集中管理。
现在我们的的DogsController的代码更加简单清晰了:
代码语言:javascript复制import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.NOT_FOUND;
@ControllerAdvice
public class DogsServiceErrorAdvice {
@ExceptionHandler({RuntimeException.class})
public ResponseEntity<String> handleRunTimeException(RuntimeException e) {
return error(INTERNAL_SERVER_ERROR, e);
}
@ExceptionHandler({DogsNotFoundException.class})
public ResponseEntity<String> handleNotFoundException(DogsNotFoundException e) {
return error(NOT_FOUND, e);
}
@ExceptionHandler({DogsServiceException.class})
public ResponseEntity<String> handleDogsServiceException(DogsServiceException e){
return error(INTERNAL_SERVER_ERROR, e);
}
private ResponseEntity<String> error(HttpStatus status, Exception e) {
log.error("Exception : ", e);
return ResponseEntity.status(status).body(e.getMessage());
}
}
-
handleRunTimeException
:这个方法会处理所有的RuntimeException
并返回INTERNAL_SERVER_ERROR
状态码 -
handleNotFoundException
: 这个方法会处理DogsNotFoundException
并返回NOT_FOUND
状态码。 -
handleDogsServiceException
: 这个方法会处理DogServiceException
并返回INTERNAL_SERVER_ERROR
状态码
这种实现的关键就在于在代码中捕获需检查异常并将其作为RuntimeException
抛出。
还可以用@ResponseStatus
将异常映射成状态码
@ControllerAdvice
public class DogsServiceErrorAdvice {
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler({DogsNotFoundException.class})
public void handle(DogsNotFoundException e) {}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler({DogsServiceException.class, SQLException.class, NullPointerException.class})
public void handle() {}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({DogsServiceValidationException.class})
public void handle(DogsServiceValidationException e) {}
}
在自定义的异常上添加状态码
代码语言:javascript复制@ResponseStatus(HttpStatus.NOT_FOUND)
public class DogsNotFoundException extends RuntimeException {
public DogsNotFoundException(String message) {
super(message);
}
}