
In der Praxis ist es häufig erforderlich, Ausnahmen innerhalb eines Controllers oder sogar einer gesamten Anwendung zentral zu behandeln. In diesem Artikel werden wir die Hauptfunktionen des Spring Framework zur Lösung dieses Problems analysieren und anhand einfacher Beispiele sehen, wie alles funktioniert. Wer sich für dieses Thema interessiert - willkommen unter dem Schnitt!
Vor Spring 3.2 waren die Hauptmethoden zum Behandeln von Ausnahmen in einer Anwendung die Annotation HandlerExceptionResolver und @ExceptionHandler . Wir werden sie im Folgenden genauer analysieren, aber sie haben bestimmte Nachteile. Ab Version 3.2 wurde die Annotation @ControllerAdvice eingeführt , mit der Einschränkungen gegenüber früheren Lösungen beseitigt werden. In Spring 5 wurde eine neue ResponseStatusException- Klasse hinzugefügt , die sehr praktisch ist, um grundlegende Fehler für REST-APIs zu behandeln .
Und jetzt, das Wichtigste zuerst, lass uns gehen!
Behandlung von Controller-Ausnahmen - @ExceptionHandler
@ExceptionHandler . , , .
:
@RestController
public class Example1Controller {
@GetMapping(value = "/testExceptionHandler", produces = APPLICATION_JSON_VALUE)
public Response testExceptionHandler(@RequestParam(required = false, defaultValue = "false") boolean exception)
throws BusinessException {
if (exception) {
throw new BusinessException("BusinessException in testExceptionHandler");
}
return new Response("OK");
}
@ExceptionHandler(BusinessException.class)
public Response handleException(BusinessException e) {
return new Response(e.getMessage());
}
}
testExceptionHandler, BusinessException, — . , , .
handleException . @ExceptionHandler(BusinessException.class), BusinessException. @ExceptionHandler , : @ExceptionHandler({BusinessException.class, ServiceException.class}).
— 200 JSON . , @ResponseStatus, @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR).
:
:
@ExceptionHandler , . @ExceptionHandler , , , .
HandlerExceptionResolver
HandlerExceptionResolver Spring. HandlerExceptionResolver. , , Spring . :
ExceptionHandlerExceptionResolver — @ExceptionHandler, .
DefaultHandlerExceptionResolver — Spring , :
| Exception | HTTP Status Code |
|---|---|
| BindException | 400 (Bad Request) |
| ConversionNotSupportedException | 500 (Internal Server Error) |
| HttpMediaTypeNotAcceptableException | 406 (Not Acceptable) |
| HttpMediaTypeNotSupportedException | 415 (Unsupported Media Type) |
| HttpMessageNotReadableException | 400 (Bad Request) |
| HttpMessageNotWritableException | 500 (Internal Server Error) |
| HttpRequestMethodNotSupportedException | 405 (Method Not Allowed) |
| MethodArgumentNotValidException | 400 (Bad Request) |
| MissingServletRequestParameterException | 400 (Bad Request) |
| MissingServletRequestPartException | 400 (Bad Request) |
| NoSuchRequestHandlingMethodException | 404 (Not Found) |
| TypeMismatchException | 400 (Bad Request) |
, REST API . . ModelAndView, , .
ResponseStatusExceptionResolver — @ResponseStatus.
ServiceException:
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public class ServiceException extends Exception {
public ServiceException(String message) {
super(message);
}
}
ServiceException @ResponseStatus value INTERNAL_SERVER_ERROR, - 500.
:
@RestController
public class Example2Controller {
@GetMapping(value = "/testResponseStatusExceptionResolver", produces = APPLICATION_JSON_VALUE)
public Response testResponseStatusExceptionResolver(@RequestParam(required = false, defaultValue = "false") boolean exception)
throws ServiceException {
if (exception) {
throw new ServiceException("ServiceException in testResponseStatusExceptionResolver");
}
return new Response("OK");
}
}
GET- exception=true, 500- :
— . , @ResponseStatus .
HandlerExceptionResolver , - JSON XML . , .
:
@Component
public class CustomExceptionResolver extends AbstractHandlerExceptionResolver {
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView(new MappingJackson2JsonView());
if (ex instanceof CustomException) {
modelAndView.setStatus(HttpStatus.BAD_REQUEST);
modelAndView.addObject("message", "CustomException was handled");
return modelAndView;
}
modelAndView.setStatus(HttpStatus.INTERNAL_SERVER_ERROR);
modelAndView.addObject("message", "Another exception was handled");
return modelAndView;
}
}
, . , : , ModelAndView. JSON, .
-, . . , . , — :
@RestController
public class Example3Controller {
@GetMapping(value = "/testCustomExceptionResolver", produces = APPLICATION_JSON_VALUE)
public Response testCustomExceptionResolver(@RequestParam(required = false, defaultValue = "false") boolean exception)
throws CustomException {
if (exception) {
throw new CustomException("CustomException in testCustomExceptionResolver");
}
return new Response("OK");
}
}
:
200 JSON .
@ControllerAdvice
— . Spring 3.2 @ControllerAdvice.
:
@ControllerAdvice
public class DefaultAdvice {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Response> handleException(BusinessException e) {
Response response = new Response(e.getMessage());
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
, @ControllerAdvice , .
DefaultAdvice handleException. handleException @ExceptionHandler, , , . BusinessException.
: @ExceptionHandler({BusinessException.class, ServiceException.class}). @ExceptionHandler .
, handleException ResponseEntity Response:
public class Response {
private String message;
public Response() {
}
public Response(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
, JSON . message HttpStatus.OK, 200.
:
@RestController
public class Example4Controller {
@GetMapping(value = "/testDefaultControllerAdvice", produces = APPLICATION_JSON_VALUE)
public Response testDefaultControllerAdvice(@RequestParam(required = false, defaultValue = "false") boolean exception)
throws BusinessException {
if (exception) {
throw new BusinessException("BusinessException in testDefaultControllerAdvice");
}
return new Response("OK");
}
}
, , JSON 200:
?
! :
@ControllerAdvice(annotations = CustomExceptionHandler.class)
public class CustomAdvice {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<Response> handleException(BusinessException e) {
String message = String.format("%s %s", LocalDateTime.now(), e.getMessage());
Response response = new Response(message);
return new ResponseEntity<>(response, HttpStatus.OK);
}
}
@ControllerAdvice(annotations = CustomExceptionHandler.class). CustomAdvice , @CustomExceptionHandler.
@CustomExceptionHandler :
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomExceptionHandler {
}
:
@RestController
@CustomExceptionHandler
public class Example5Controller {
@GetMapping(value = "/testCustomControllerAdvice", produces = APPLICATION_JSON_VALUE)
public Response testCustomControllerAdvice(@RequestParam(required = false, defaultValue = "false") boolean exception)
throws BusinessException {
if (exception) {
throw new BusinessException("BusinessException in testCustomControllerAdvice");
}
return new Response("OK");
}
}
Example5Controller @CustomExceptionHandler, Example4Controller . BusinessException CustomAdvice, DefaultAdvice, .
CustomAdvice — :
. @ControllerAdvice, . .
ResponseStatusException.
ResponseStatusException:
@RestController
public class Example6Controller {
@GetMapping(value = "/testResponseStatusException", produces = APPLICATION_JSON_VALUE)
public Response testResponseStatusException(@RequestParam(required = false, defaultValue = "false") boolean exception) {
if (exception) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "ResponseStatusException in testResponseStatusException");
}
return new Response("OK");
}
}
ResponseStatusException , , . @ResponseStatus — -. , .
:
Zusammenfassung : Wir haben verschiedene Arten der Behandlung von Ausnahmen gesehen, jede mit ihren eigenen Merkmalen. In einer großen Anwendung können Sie mehrere Ansätze gleichzeitig finden, aber Sie müssen sehr vorsichtig sein und versuchen, die Fehlerbehandlungslogik nicht zu komplizieren. Andernfalls wird sich herausstellen, dass eine Ausnahme im falschen Handler behandelt wird und die Antwort von der erwarteten abweicht. Wenn die Anwendung beispielsweise mehrere Berater hat, müssen Sie beim Erstellen eines neuen sicherstellen, dass die bestehende Reihenfolge der Behandlung von Ausnahmen von alten Controllern nicht verletzt wird.
Also sei vorsichtig und alles wird gut funktionieren!