Exception Catching in Spring MVC

The code can use try/catch to catch known exceptions (checked exceptions), but unknown exceptions (unchecked exceptions) appear randomly in different forms at different times. When an exception is thrown, the system needs special processing to capture exception-friendly prompts instead of sending server-side exception information to the client. And record the indicator information of abnormal occurrence to facilitate developers to debug the code.

Common global processing can be achieved in the following ways:
  • ServletRequestListener: Executed at the beginning and end of a request, Spring's RequestContextListener, etc.
  • Filter: Executed at the beginning and end of Servlet, Spring's CharacterEncodingFilter, HiddenHttpMethodFilter, etc.
  • HandlerInterceptor: Executed at the beginning and end of the Controller method, Spring's LocaleChangeInterceptor, etc.
  • Spring AOP (AspectJ): face-oriented, arbitrary injection processing.


Only the built-in exception handling mechanism of Spring MVC is introduced here:
  • @ResponseStatus
  • @ExceptionHandler
  • @ControllerAdvice
  • HandlerExceptionResolver

(1) Exception capture in the Controller

implements exception capture processing by annotating @ExceptionHandler.
@Controller
public class FooController {
    @ExceptionHandler
    @ResponseStatus(NOT_FOUND)
    public void notFound(ResourceNotFoundException ex) {
        // N/A
    }
}

Exception capture can also be achieved by implementing HandlerExceptionResolver.
@Controller
public class FooController implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse res, Object handler, Exception ex) {
        // N/A
    }
}


(2) Exceptions marked by the @ResponseStatus annotation
will be automatically handled by ResponseStatusExceptionResolver.
@ResponseStatus(value = NOT_FOUND, reason = "Resource is not found.")
public class ResourceNotFoundException extends RuntimeException {
    // ...
}


(3) Global exception capture

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler
    @RespnseStatus(NOT_FOUND)
    public void notFound(ResourceNotFoundException ex) {
        // N/A
    }
}

@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Error> handle(RuntimeException ex, HttpServletResponse res){
        // N/A
        res.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    }
}


When the API and the page coexist, the global exception capture can be forwarded again to return different content.
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    @ExceptionHandler(Exception.class)
    public String error(HttpServletRequest req, Exception ex){
        // N/A
        String baseUrl = getBaseUrl (req);
        return "forward:" + baseUrl + "/error";
    }
}

@Controller
public class GeneralErrorHandlerController {

  @RequestMapping("/admin/error")
  public String screenError(HttpServletRequest req, Model model) {
    // N/A
    return "screen/admin/error";
  }

  @RequestMapping(value = "/api/internal/error")
  @ResponseBody
  public ErrorResponse apiError(HttpServletRequest req) {
    // N/A
    return ErrorResponse.build(e);
  }

}

***** Cannot automatically set status code, you need to set it yourself
***** Cannot catch all exceptions, such as presentation layer, 404 errors, etc.

@ExceptionHandler, @InitBinder, @ModelAttribute methods in @ControllerAdvice annotations will be applied to all The @RequestMapping annotated method. The above @ExceptionHandler is the most common, and @InitBinder is also used, for example:

1) By default SpringMVC sets the value of empty fields to empty strings instead of nulls. The empty string can be set to null by the following definition, and the string will be automatically trimmed.
You can also pass in the characters to be filtered: StringTrimmerEditor(String charsToDelete, boolean emptyAsNull)
@ControllerAdvice
public class NullValidAdvice {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }

}


2) Set the return parameters of JSONP
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {

    public JsonpAdvice() {
        super("callback");
    }

}


(4) The error-page
part in web.xml is not handled by Spring itself, but is handled by ServletContainer.
Jump all uncaught exceptions to a specified mapping again.

<error-page>
    <location>/error</location>
</error-page>


@Controller
class GeneralErrorHandlerController {
    @RequestMapping("error")
    public String otherError(HttpServletRequest req, Model model) {
      // N/A
    }
}


(5) Custom 404 correspondingly
set throwExceptionIfNoHandlerFound to true
<servlet>
    <servlet-name>rest-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>throwExceptionIfNoHandlerFound</param-name>
        <param-value>true</param-value>
    </init-param>
</servlet>


@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<Error> handle(NoHandlerFoundException ex){
        // N/A
    }
}


(6) Customize the error page
customization to map the exception to the view SimpleMappingExceptionResolver

@Bean
public SimpleMappingExceptionResolver exceptionResolver(){
    SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
    Properties exceptions = new Properties();
    exceptions.put(ArithmeticException.class, "error");
    resolver.setExceptionMappings(exceptions);
    return resolver;
}


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Spring MVC Exception Handling</title>
</head>
<body>

<h1>Spring MVC Exception Handling</h1>

${exception.message}

</body>
</html>

***** The test development environment can print out the exception stack on the page, which is convenient for debugging.

Spring MVC exception handling order:
ExceptionHandlerExceptionResolver: Exception marked by @ExceptionHandler annotation
->
ResponseStatusExceptionResolver: Exception marked by @ResponseStatus annotation
->
DefaultHandlerExceptionResolver: Spring's standard exception handling
->
Custom Resolver

standard exception handling DefaultHandlerExceptionResolver
quote
  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)
  MissingPathVariableException 500 (Internal Server Error)
  MissingServletRequestParameterException 400 (Bad Request)
  MissingServletRequestPartException 400 (Bad Request)
  NoHandlerFoundException 404 (Not Found)
  NoSuchRequestHandlingMethodException 404 (Not Found)
  TypeMismatchException 400 (Bad Request)


Reference:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers
https://spring.io/blog/2013/11/01/exception -handling-in-spring-mvc
http://memorynotfound.com/spring-mvc-exception-handling/
http://www.journaldev.com/2651/spring-mvc-exception-handling-controlleradvice-exceptionhandler-handlerexceptionresolver
http https://www.roytuts.com/exception-handling-best-practices-in-java/
https://speakerdeck.com/sinsengumi/spring-boot-application-infrastructure
https://github.com/jirutka/spring- rest-exception-handler
http://qiita.com/kazuki43zoo/items/757b557c05f548c6c5db
http://cgs1999.iteye.com/blog/1547197
http://www.cnblogs.com/xinzhao/p/4934247.html

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326527133&siteId=291194637