7.【原创】Spring Mvc自定义DispatcherServlet类,处理404异常
1.原因:
自己在写纯接口服务的时候,使用Spring Mvc方式在web.xml中配置拦截器,使用@RestControllerAdvice注解定义了一个全局的异常处理器,在SpringBoot中,它可正常拦截到类似NoHandlerFoundException(404)、500、406等异常,但是在Spring Mvc确无法做到。在使用Spring Mvc时,此框架通过DispatcherServlet类进行转发请求时,例如404此种异常会直接在DispatcherServlet类中抛出,然后由Tomcat捕获进行错误页面的响应处理,并不会转发到Controller层中。也就无法到达@RestControllerAdvice注解的全局异常处理类中处理。
2.处理:
2.1.继承DispatcherServlet,自定义转发器,重写noHandlerFound方法处理404异常,因为此方法返回值是void,所以采用抛出自定义的异常,让@RestControllerAdvice的全局异常处理类处理。再重写processHandlerException方法处理其他异常,例如500,406等。
2.2.在web.xml中将原先的DispatcherServlet替换成自定义的CustomizeDispatcherServlet类即可。话不多说,上代码。
3.实现:
3.1.自定义转发器DispatcherServlet重写noHandlerFound和processHandlerException方法
package cn.drip.dripblog.filter; import cn.drip.dripblog.constant.consist.DripException; import cn.drip.dripblog.constant.enums.ExpStatus; import cn.drip.dripblog.model.dto.Result; import com.alibaba.fastjson.support.spring.FastJsonJsonView; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; /** * @Author: Chunhai.Hu * @Date: 2020/9/8 0008 15:46 * @Last Modified by: Chunhai.Hu * @Last Modified time: 2020/9/8 0008 15:46 * @Desc: 自定义DispatcherServlet,重写noHandlerFound方法,处理HTTP通用异常,如404 */ public class CustomizeDispatcherServlet extends DispatcherServlet { // 抛出404的异常:交由GlobalExceptionHandler处理 @Override protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception { throw new NoHandlerFoundException(request.getMethod(), request.getRequestURL().toString(), new HttpHeaders()); } // 拦截对应异常:拦截除404之外的HTTP异常 @Override protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 共用参数 ModelAndView model = new ModelAndView(); FastJsonJsonView view = new FastJsonJsonView(); Map<String, String> map = new HashMap<String, String>(); if (ex instanceof HttpRequestMethodNotSupportedException) { // 处理405异常:请求方式不支持 map.put("status", String.valueOf(HttpStatus.METHOD_NOT_ALLOWED.value())); map.put("message", ex.getMessage()); } else {
// 不想自定义处理的异常:交还父类处理 return super.processHandlerException(request, response, handler, ex); } // 设置返回 view.setAttributesMap(map); model.setView(view); return model; } }
3.2.使用RestControllerAdvice注解定义全局的异常处理器GlobalExceptionHandler
package cn.drip.dripblog.filter; import cn.drip.dripblog.constant.consist.DripException; import cn.drip.dripblog.constant.enums.ExpStatus; import cn.drip.dripblog.model.dto.Result; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.NoHandlerFoundException; import javax.servlet.http.HttpServletRequest; /* * @Created with IntelliJ IDEA. * @Author: Chunhai.Hu * @Date: 2020/6/29 2:38 * @1.Desc: 全局异常处理,如果使用@RestControllerAdvice 注解则会将返回的数据类型转换成JSON格式 * @2.在Spring mvc中404,500,505等异常通过DispatcherServlet类抛出被tomcat捕获, * @无法到达Controller,也就无法到达@ControllerAdvice * @处理方式:声明自定义的CustomizeDispatcherServlet类,重写noHandlerFound和processHandlerException方法处理 */ @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * @param req * @param e * @return * @处理: 自定义异常 */ @ExceptionHandler(DripException.class) public Result DripExceptionHandler(HttpServletRequest req, DripException e) { e.printStackTrace(); logger.error("DripException Exception Cause by:{}", e.getMessage()); return new Result(e.getStatus(), e.getMessage(), e.getExpMsgAttach()); } /** * @处理404异常 * @param req * @param e * @return */ @ExceptionHandler(NoHandlerFoundException.class) public Result handlerNoFoundException(HttpServletRequest req, NoHandlerFoundException e) { logger.error("NoHandlerFoundException Exception Cause by:{}", e.getMessage()); return new Result(String.valueOf(HttpStatus.NOT_FOUND.value()), e.getMessage()); } /** * @param req * @param e * @return * @处理:空指针异常 */ @ExceptionHandler(NullPointerException.class) public Result nullPointerExceptionHandler(HttpServletRequest req, NullPointerException e) { e.printStackTrace(); logger.error("NullPointerException Exception Cause by:{}", e.getMessage()); return new Result(ExpStatus.SERVICE_BUSY); } /** * @param req * @param e * @return * @处理:未涉及的异常 */ @ExceptionHandler(Exception.class) public Result ExceptionHandler(HttpServletRequest req, Exception e) { e.printStackTrace(); logger.error("Exception Cause by:{}", e.getMessage()); return new Result(ExpStatus.SERVICE_BUSY); } }
3.3.将web.xml中将原先的DispatcherServlet替换成自定义的CustomizeDispatcherServlet类
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <!--1.1:配置Spring监听器,加载spring核心容器配置:默认只加载WEB-INF下的applicationContext.xml文件--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--1.2:设置Spring配置文件路径--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--步骤2.1:配置spring的前端控制器,让所有请求都由其处理(此为自定义的DispatcherServlet)--> <servlet> <servlet-name>CustomizeDispatcherServlet</servlet-name> <servlet-class>cn.drip.dripblog.filter.CustomizeDispatcherServlet</servlet-class> <!--配置初始化参数:读取springmvc的配置文件:使用类路径方式--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet.xml</param-value> </init-param> <!--配置加载策略:一启动时就加载--> <load-on-startup>1</load-on-startup> </servlet> <!--2.2:配置映射路径及拦截范围--> <servlet-mapping> <servlet-name>CustomizeDispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
4.注意事项:
4.1.以上代码中的DripException、ExpStatus、Result和Status类都是我自定义的类,具体使用时,可依情况进行对应替换。