
من الناحية العملية ، غالبًا ما يكون من الضروري معالجة الاستثناءات مركزيًا داخل وحدة تحكم أو حتى تطبيق بأكمله. في هذه المقالة ، سنحلل الميزات الرئيسية التي يوفرها Spring Framework لحل هذه المشكلة ، وباستخدام أمثلة بسيطة ، دعنا نرى كيف يعمل كل شيء. من يهتم بهذا الموضوع - مرحبًا بك تحت الخفض!
في الأصل ، قبل Spring 3.2 ، كانت الطرق الرئيسية للتعامل مع الاستثناءات في التطبيق هي HandlerExceptionResolver و ExceptionHandler الشرح التوضيحي . سنقوم بتحليلها بمزيد من التفصيل أدناه ، لكن لها بعض العيوب. بدءًا من الإصدار 3.2 ، ظهر التعليق التوضيحي ControllerAdvice ، والذي يزيل القيود من الحلول السابقة. وفي ربيع 5 ، تمت إضافة فئة ResponseStatusException جديدة والتي تكون مفيدة جدًا لمعالجة الأخطاء الأساسية لواجهات برمجة تطبيقات REST .
والآن ، أول الأشياء أولاً ، دعنا نذهب!
معالجة استثناء وحدة التحكم --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 — -. , .
:
ملخص : لقد رأينا طرقًا مختلفة للتعامل مع الاستثناءات ، ولكل منها خصائصها الخاصة. في تطبيق كبير ، يمكنك العثور على عدة طرق في وقت واحد ، ولكن عليك أن تكون حذرًا للغاية ومحاولة عدم المبالغة في تعقيد منطق معالجة الخطأ. خلاف ذلك ، سيتضح أنه سيتم التعامل مع بعض الاستثناءات في المعالج الخطأ وستختلف الاستجابة عن الاستجابة المتوقعة. على سبيل المثال ، إذا كان للتطبيق العديد من المستشارين ، فعند إنشاء مستشار جديد ، تحتاج إلى التأكد من أنه لا يكسر الترتيب الحالي للتعامل مع الاستثناءات من وحدات التحكم القديمة.
لذا كن حذرا وكل شيء سيعمل بشكل رائع!