This article is participating in the “Digitalstar Project” to win a creative gift package and challenge the creative incentive money.
In daily Web development, large and small exceptions are often encountered. In this case, a unified exception handling mechanism is often needed to ensure that the client can receive friendly prompts. Spring Boot also provides a default exception handling mechanism, which is described in detail in this section.
Spring Boot default exception handling mechanism
Spring Boot provides a default exception handling mechanism. Once an exception occurs in a program, Spring Boot automatically identifies the client type (browser client or machine client) and displays the exception information in different formats based on the client type.
- For the browser client, Spring Boot responds with a “whitelabel” error view, rendering the error message in HTML format, as shown in the figure below
Figure 1: Spring Boot default error white pages
- For the machine client, Spring Boot will generate a JSON response to display the exception message.
{"timestamp": "2021-07-12T07:05:29.885+00:00", "status": 404, "error": "Not Found", "message": "No message available", "path": "/m1ain.html" }Copy the code
Spring Boot exception handling Automatic configuration principle
Spring the Boot of exception handling through configuration class ErrorMvcAutoConfiguration provides automatic configuration, the configuration class to the container into the following four components.
- ErrorPageCustomizer: This component will forward requests to /error by default if a system exception occurs.
- BasicErrorController: Handles the default /error request.
- Terrorviewresolver: DefaultErrorViewResolver that resolves exception messages to the corresponding error view.
- Terrorattributes: Used to share exception information on a page.
Let’s take a closer look at each of these components in turn.
ErrorPageCustomizer
ErrorMvcAutoConfiguration injected in to the container, a ErrorPageCustomizer components, it is mainly used in the response rules of custom error pages.
@Bean
public ErrorPageCustomizer errorPageCustomizer(DispatcherServletPath dispatcherServletPath) {
return new ErrorPageCustomizer(this.serverProperties, dispatcherServletPath);
}
Copy the code
ErrorPageCustomizer registers response rules for error pages through the registerErrorPages() method. When an exception occurs in the system, the ErrorPageCustomizer component automatically takes effect and forwards the request to/Error for processing by the BasicErrorController. The code is as follows:
@override public void registerErrorPages(ErrorPageRegistry ErrorPageRegistry) {// Forward the request to /errror (this.properties.geterror ().getPath()) ErrorPage ErrorPage = new ErrorPage(this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath())); . / / registration error page errorPageRegistry addErrorPages (errorPage); }Copy the code
BasicErrorController
ErrorMvcAutoConfiguration also injected a mistake to container BasicErrorController controller components, the code is as follows.
@Bean
@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
public BasicErrorController basicErrorController(ErrorAttributes errorAttributes,
ObjectProvider<ErrorViewResolver> errorViewResolvers) {
return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
errorViewResolvers.orderedStream().collect(Collectors.toList()));
}
Copy the code
BasicErrorController is defined as follows.
BasicErrorController @requestMapping ("${server.error. Path :${error. Path :/error}}") public BasicErrorController @requestMapping ("${server.error class BasicErrorController extends AbstractErrorController { ...... /** * This method is used to handle exceptions that occur in browser client requests ** Generate HTML pages to display exception information * @param Request * @param response * @return */ @requestMapping (Produces) = MediaType.TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {HttpStatus status = getStatus(request); //getErrorAttributes encapsulates some model data based on error information, Map<String, Object> model = collections. unmodifiableMap(getErrorAttributes(Request, getErrorAttributeOptions(request, MediaType.TEXT_HTML))); // Set the error status code response.setStatus(status.value()); // Call resolveErrorView(), ModelAndView ModelAndView = resolveErrorView(Request, Response, status, model); return (modelAndView ! = null) ? modelAndView : new ModelAndView("error", model); } /** * This method is used to handle errors in machine client requests ** generate JSON format data display error messages * @param Request * @return */ @requestMapping Public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { HttpStatus status = getStatus(request); if (status == HttpStatus.NO_CONTENT) { return new ResponseEntity<>(status); } Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL)); return new ResponseEntity<>(body, status); }... }Copy the code
Spring Boot uses BasicErrorController for unified error handling (such as the default "/error" request). Spring Boot automatically identifies the type of client making the request (browser client or machine client) and, depending on the client type, passes the request to the errorHtml() and error() methods for processing.Copy the code
Return value type | Method statement | Client type | Error message return type |
---|---|---|---|
ModelAndView | errorHtml(HttpServletRequest request, HttpServletResponse response) | Browser client | Text/HTML (error page) |
ResponseEntity<Map<String, Object>> | error(HttpServletRequest request) | Machine client (e.g. Android, IOS, Postman, etc.) | JSON |
In other words, errorHtml() method in BasicErrorController is used to handle exceptions when using browser, and Error () method is used to handle exceptions when using Android, IOS, Postman and other machine clients.
The errorHtml() method calls the resolveErrorView() method of the subclass (AbstractErrorController) as follows.
protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {// Get all the error view parsers in the container to handle the exception message for (ErrorViewResolver resolver: This.errorviewresolvers) {// Call the resolveErrorView of the error view parser to resolve to the error view page ModelAndView ModelAndView = resolver.resolveErrorView(request, status, model); if (modelAndView ! = null) { return modelAndView; } } return null; }Copy the code
When responding to a page, all ErrorViewResolver objects in the container (including defaulViewResolver) are retrieved from the parent class's resolveErrorView method. Let's parse the exception information.Copy the code
DefaultErrorViewResolver
ErrorMvcAutoConfiguration also injected a default error to the container view DefaultErrorViewResolver parser components, the code is as follows.Copy the code
@Bean
@ConditionalOnBean(DispatcherServlet.class)
@ConditionalOnMissingBean(ErrorViewResolver.class)
DefaultErrorViewResolver conventionErrorViewResolver() {
return new DefaultErrorViewResolver(this.applicationContext, this.resources);
}
Copy the code
When the client making the request is a browser, Spring Boot retrieves all ErrorViewResolver objects in the container (error view resolvers) and calls their resolveErrorView() method to parse the exception message. Naturally, this includes a TerrorView Resolver (the default error message resolver). Part of the code for DefaulViewResolver is as follows.
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered { private static final Map<HttpStatus.Series, String> SERIES_VIEWS; static { Map<HttpStatus.Series, String> views = new EnumMap<>(HttpStatus.Series.class); views.put(Series.CLIENT_ERROR, "4xx"); views.put(Series.SERVER_ERROR, "5xx"); SERIES_VIEWS = Collections.unmodifiableMap(views); }... @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, ModelAndView ModelAndView = resolve(string.valueof (status.value()), model); If (modelAndView == null && series_views.containsKey (status.series())) {// Try to parse modelAndView = with 4xx or 5xx as the error page resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { For example, error/404, error/4xx, error/500, error/5xx String errorViewName = "error/" + viewName; // When the template engine can parse these template pages, With the template engine parsing TemplateAvailabilityProvider provider = this. TemplateAvailabilityProviders. GetProvider (errorViewName, this.applicationContext); if (provider ! Return new ModelAndView(errorViewName, model); Return resolveResource(errorViewName, model); return resolveResource(errorViewName, model); } private ModelAndView resolveResource(String viewName, Map<String, Object> model) {// Go through all static resource folders for (String location: this.resources.getStaticLocations()) { try { Resource resource = this.applicationContext.getResource(location); // Static resources folder error page, For example, error/404.html, error/4xx. HTML, error/500. HTML, error/5xx. HTML resource = resource. // If the static resources folder contains the above error page, Is returned directly if (the resource. The exists () {return new ModelAndView (new DefaultErrorViewResolver. HtmlResourceView (resource), model); } } catch (Exception ex) { } } return null; }... }Copy the code
Terrorviewresolver resolves the exception message using the following steps:
- Generate an error view error/status based on the error status code (for example, 404, 500, 400, etc.), such as error/404, error/500, error/400.
- Try parsing the Error /status view with the template engine by looking for error/status.html in the templates directory on the classpath classpath. For example, error/404.html, error/500. HTML, error/400.html.
- If the template engine can parse to the Error/Status view, wrap the view and data back into ModelAndView and end the parsing process, otherwise go to Step 4.
- Look for error/status. HTML in each static resource folder. If the error page is found in the static resource folder, go back and end the parsing process.
- Change the error status code (for example, 404, 500, 400) to 4xx or 5XX. Then repeat the first four steps. If the resolution is successful, return and end the parsing process.
- Handle the default “/error” request using the Spring Boot default Whitelabel Error Page.
DefaultErrorAttributes
ErrorMvcAutoConfiguration also injected a component to the container DefaultErrorAttributes default attribute processing tool, the code is as follows.
@Bean
@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
public DefaultErrorAttributes errorAttributes() {
return new DefaultErrorAttributes();
}
Copy the code
Terrorattributes is Spring Boot’s default error attribute handling tool, which takes exception or error information from a request and returns it as a Map object, part of which is described below.
public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered { ...... @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttributeOptions options) { Map<String, Object> errorAttributes = getErrorAttributes(webRequest, options.isIncluded(Include.STACK_TRACE)); if (! options.isIncluded(Include.EXCEPTION)) { errorAttributes.remove("exception"); } if (! options.isIncluded(Include.STACK_TRACE)) { errorAttributes.remove("trace"); } if (! options.isIncluded(Include.MESSAGE) && errorAttributes.get("message") ! = null) { errorAttributes.remove("message"); } if (! options.isIncluded(Include.BINDING_ERRORS)) { errorAttributes.remove("errors"); } return errorAttributes; } private Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> errorAttributes = new LinkedHashMap<>(); errorAttributes.put("timestamp", new Date()); addStatus(errorAttributes, webRequest); addErrorDetails(errorAttributes, webRequest, includeStackTrace); addPath(errorAttributes, webRequest); return errorAttributes; }... }Copy the code
When Spring Boot's default ErrorController (BasicErrorController) handles errors, it calls the TerrorAttributes getErrorAttributes() method to getError or exception information, Encapsulate model data (Map objects) and return it to a page or JSON data. The model data mainly contains the following attributes:Copy the code
- Timestamp: timestamp;
- Status: indicates the error status code
- Error: indicates an error message
- Exception: Exception object that causes request processing to fail
- Message: error/exception message
- Trace: error/exception stack information
- Path: THE URL path requested when an error/exception is thrown
All attributes encapsulated in model data via TerrorAttributes can be retrieved directly from the page or JSON.
###* Update 75/100 🍅 Click on the author’s home page: Java Li Yangyong access to fans exclusive learning interview materials