This is the 20th day of my participation in the August More Text Challenge

The default processing mechanism

Default effect:

Browser access: return a default error page

Browser headers:

2. Other clients use JSON data by default

Request header:

1, the principle of

Automatic configuration of ErrorMvcAutoConfiguration, error handling

Add the following components to the container:

(1). ErrorPageCustomizer

@Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { ErrorPage errorPage = new ErrorPage( this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath())); errorPageRegistry.addErrorPages(errorPage); } ====getPath() method public String getPath() {return this.path; } @Value("${error.path:/error}") private String path = "/error"; // The system fails. (Error page rules for web.xml registration)Copy the code

(2). BasicErrorController: Handles default /error requests and adaptively returns pages or JSON data

@Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class BasicErrorController extends AbstractErrorController {// Based on the client request header, Select the corresponding data: HTML or json @requestMapping (produces = MediaType.TEXT_HTML_VALUE) // Generate HTML data, Public ModelAndView errorHtml(HttpServletRequest Request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections .unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView ! = null) ? modelAndView : new ModelAndView("error", model); } @requestMapping // Generate JSON data, 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, isIncludeStackTrace(request, MediaType.ALL)); return new ResponseEntity<>(body, status); }}Copy the code

(3). Terrorviewresolver (default parser for loading error pages)

@Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView modelAndView = resolve(String.valueOf(status.value()), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { error/404,error/500 String errorViewName = "error/" + viewName; / / template engine can parse the page address can use template engine parsing TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName, this.applicationContext); if (provider ! Return new ModelAndView(errorViewName, model); } return resolveResource(errorViewName, model); }Copy the code

(4). Terrorattribute, customizing error response information,

@override public 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

2. Default implementation steps

Once a 4XX or 5XX error occurs in the system; ErrorPageCustomizer takes effect (custom error rules); It will come to the /error request; BasicErrorController will handle:

(1). Response page, errorHtml(…) methods

// BasicErrorController.java -> errorHtml(...) Methods... ModelAndView modelAndView = resolveErrorView(request, response, status, model); . ============= protected ModelAndView resolveErrorView(HttpServletRequest request, HttpServletResponse response, HttpStatus status, Map<String, Object> model) {// All errorViewResolvers get ModelAndViews, which are resolved by TerrorViewResolver for (ErrorViewResolver: this.errorViewResolvers) { ModelAndView modelAndView = resolver.resolveErrorView(request, status, model); if (modelAndView ! = null) { return modelAndView; } } return null; }Copy the code

(2). Response JSON data, error(…) methods

Customizing error responses

1, how to customize the response error page

  • With a template engine; Error/status code [Name the error page as error status code. HTML in the template engine folder under the error folder], the occurrence of the error status code will be sent to the corresponding page;

    • We can use 4xx or 5xx as filename to match all errors of this type, exact status code.html preferred.
  • Information available to pages (captured by TerrorAttribute)

    • Timestamp: indicates the timestamp

    • Status: indicates the status code

      • Error: indicates an error message
      • Exception: exception object
      • “Message” : abnormal message
      • Errors: indicates the error of JSR303 data verification
  • Without a template engine (the /error folder cannot be found in the template engine) [timestamp,status….] [timestamp,status….]

  • None of the above, use the SpringBoot default warning page

2. How to customize JSON data that responds incorrectly

This parameter is required in SpringBoot 2.x

[${exception}]] server.error. Include-exception =trueCopy the code
  • (1). Custom exception handling & Returning custom JSON data: (Flaw: Browser access will also return JSON data)

    @ControllerAdvice public class MyExceptionHandler { @ResponseBody @ExceptionHandler public Map<String,Object> handlerException(Exception e){ Map<String,Object> map = new HashMap<>(); Map. put("code"," username does not exist "); map.put("message",e.getMessage()); return map; }} // Cannot adapt accordingly. Both browsers and other clients return JSON dataCopy the code
  • (2). Forward to /error for adaptive response effect processing (defect: pages and JSON cannot display custom data)

    @ExceptionHandler(UserNotExistException.class) public String handlerException(Exception e, HttpServletRequest request){ Map<String,Object> map = new HashMap<>(); Map. put("code"," username does not exist "); map.put("message",e.getMessage()); Before / / forward incoming error status code itself, the request according to the requirements of custom error classes. The setAttribute (" javax.mail. Servlet. Error. Status_code ", 500); Return "forward:/error"; }Copy the code
  • (3). Achieve browser or other client access, corresponding corresponding page, and display custom information

BasicErrorController class === @requestMapping (Produces = MediaType.TEXT_HTML_VALUE) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { ... Map<String, Object> model = Collections .unmodifiableMap(getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); . return (modelAndView ! = null) ? modelAndView : new ModelAndView("error", model); } === @RequestMapping public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { ... Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); return new ResponseEntity<>(body, status); }Copy the code

Error message was obtained by the getErrorAttributes() method.

This method is written using the AbstractErrorController abstract class that implements the ErrorController interface by default in SpringBoot automatic configuration.

So we have three ways to write custom error messages:

  1. Subclass AbstractErrorController and put it in a container

  2. Write an implementation class that implements the ErrorController interface and place it in a container (optional)

  3. Terrorattributes subclass which also gets default error message (optional)

    ErrorMvcAutoConfiguration class @ Bean @ ConditionalOnMissingBean (value = ErrorAttributes. Class, search = SearchStrategy.CURRENT) public DefaultErrorAttributes errorAttributes() { return new DefaultErrorAttributes(this.serverProperties.getError().isIncludeException()); } // If the ErrorAttributes class does not use the default, we write the ErrorAttributes implementation classCopy the code
    ErrorAttribute@Component Public class MyErrorAttributes extends DefaultErrorAttributes {public MyErrorAttributes() { super(true); } @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String,Object> map = super.getErrorAttributes(webRequest,includeStackTrace); map.put("code23","user not exist 23"); // errorMap the key in the request field, The values passed in from the custom exception class Map<String,Object> extMap = (Map<String, Object>) webrequest.getAttribute ("errorMap",0); map.put("ex",extMap); return map; }}Copy the code

    Ps: The error message object added to the error page returned by the browser must be a key defined in the map. Otherwise, the error message object is empty.

Summary: In order to return custom JSON data when accessed by other clients, ensure that the custom data can be retrieved on the return page.

Write custom exception class -> exception class write MAP1 to error key-value pair and store MAP1 data in the Request field -> forward to /error which can be adaptive to return JSON or page -> write terrorAttributes subclass, You can also get the default returned data – > get the map2 of the default data, – > error page: default error data (default object), map2 data (map2 key), map1 data (map2 key. Map1 key)