About the global exception handling of Web applications, the last article introduced the way of Controller Advice combined with @ExceptionHandler to realize the global exception management of Web applications.

This blog post brings another unusual way to handle exceptional states by implementing a custom Handler Exception Resolver

Links to the previous blog: Global exception handling in SpringBook series of tutorials The original text: Handler Exception Resolver for Custom Exception Handling in the SpringBook series of tutorials

<!-- more -->

I. Environmental Construction

First, we need to build a web application in order to continue the follow-up testing. It is relatively simple to build a web application with SpringBook.

Create a maven project with the following pom file

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7</version>
    <relativePath/> <!-- lookup parent from update -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.45</version>
    </dependency>
</dependencies>

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>

II. HandlerExceptionResolver

1. Custom exception handling

Handler Exception Resolver, as its name implies, is a class that handles exceptions. The interface is a method, callbacks after exceptions occur, and the exception stack information is carried in the four parameters.

@Nullable
ModelAndView resolveException(
		HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);

It's easier for us to customize the exception handling class, implement the above interface, and then return the complete stack to the caller.

public class SelfExceptionHandler implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) {
        String msg = GlobalExceptionHandler.getThrowableStackInfo(ex);

        try {
            response.addHeader("Content-Type", "text/html; charset=UTF-8");
            response.getWriter().append("Custom exception handling!!! \n").append(msg).flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

// Stack information is printed as follows
public static String getThrowableStackInfo(Throwable e) {
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    e.printStackTrace(new java.io.PrintWriter(buf, true));
    String msg = buf.toString();
    try {
        buf.close();
    } catch (Exception t) {
        return e.getMessage();
    }
    return msg;
}

Looking closely at the above code implementation, there are several points to note

  • To ensure that Chinese is not scrambled, we set the return header response.addHeader("Content-Type", "text/html; charset=UTF-8"); if there is no line, Chinese scrambling will occur.
  • We do not want to return to the view. Instead, we want to write data back to response.getWriter().append("custom exception handling!!! \ n").append(msg).flush(). If there is a custom error page in the project, we can return to ModelAndView to determine the final error page returned.
  • The above code will not take effect directly. It needs to be registered. It can be registered in the subclass of WebMvcConfigurer. The example is as follows
@SpringBootApplication
public class Application implements WebMvcConfigurer {
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(0, new SelfExceptionHandler());
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}

2. Test case

We still use the use case from the previous blog post to test it.

@Controller
@RequestMapping(path = "page")
public class ErrorPageRest {

    @ResponseBody
    @GetMapping(path = "divide")
    public int divide(int sub) {
        return 1000 / sub;
    }
}

The following are the observations of 404 and 500 anomalies, respectively.

500 exceptions will enter our custom exception handling class, and 404 still goes through the default error page, so if we need to catch 404 exceptions, we still need to add them in the configuration file.

# When an error occurs, throw an exception directly
spring.mvc.throw-exception-if-no-handler-found=true
# Setting static resource mapping access paths
spring.mvc.static-path-pattern=/statics/**
# spring.resources.add-mappings=false

Why does 404 need extra processing?

Now try to explain the problem in a plain and easy-to-understand way.

  • java web applications, in addition to returning json class data, may also return web pages, js, css
  • We use @ResponseBody to indicate that a url returns json data (which is usually the case, regardless of custom implementations)
  • REST services defined by @RequestMapping in our @Controller return static resources
  • So what about js,css, pictures? We don't define a REST service in our web application.
  • So when an http request is received and the url association mapping cannot be found, it is not considered as a NoHandlerFoundException in the default scenario. Instead, it is found in the static resource (there is no NoHandlerFoundException in the static resource, why not? This exception indicates that the url request does not have a corresponding processor, but here we assign it to the static resource processor, ResourceHttpRequestHandler.

To address this, if you are interested in digging deeper, here are the key code locations

// Entry method: `org. spring framework. web. servlet. Dispatcher Servlet # doDispatch`

// debug node
Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
	noHandlerFound(processedRequest, response);
	return;
}

// Core logic
// org.springframework.web.servlet.DispatcherServlet#getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
	if (this.handlerMappings != null) {
		for (HandlerMapping hm : this.handlerMappings) {
			if (logger.isTraceEnabled()) {
				logger.trace(
						"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
			}
			HandlerExecutionChain handler = hm.getHandler(request);
			if (handler != null) {
				return handler;
			}
		}
	}
	return null;
}

3. summary

Although this blog article also introduces a new global exception handling method, the implementation effect is similar to that of Controller Advice, but this method is not recommended for the following reasons.

  • The Handler Exception Resolver approach is not as elegant as the Controller Advice approach.
  • The official DefaultHandler Exception Resolver is already very powerful and basically covers http status codes. It is not necessary for us to customize it ourselves.

II. other

web series blog posts

Project source code

1. Grey Blog

Letters are not as good as letters. The above contents are purely family statements. Due to limited personal abilities, there are inevitably omissions and errors. If you find bug s or have better suggestions, you are welcome to criticize and correct them. Thank you very much.

Below is a grey personal blog, which records all the blogs about study and work. Welcome to visit it.