文档章节

Spring Boot全局异常处理

狼王黄师傅
 狼王黄师傅
发布于 2018/10/16 12:26
字数 3749
阅读 93
收藏 2

Spring Boot默认的异常处理机制

    默认情况下,Spring Boot为两种情况提供了不同的响应方式:

    一种是浏览器客户端请求一个不存在的页面或服务端处理发生异常时,一般情况下浏览器默认发送的请求头中Accept: text/html,所以Spring Boot默认会响应一个html文档内容,称作“Whitelabel Error Page”。

    另一种是使用Postman等调试工具发送请求一个不存在的url或服务端处理发生异常时,Spring Boot会返回类似如下的Json格式字符串信息

{
    "timestamp": "2018-05-12T06:11:45.209+0000",
    "status": 404,
    "error": "Not Found",
    "message": "No message available",
    "path": "/index.html"
} 

 

 

 

默认错误页面路径

    Spring Boot 默认提供了程序出错的结果映射路径:/error。这个/error请求会在BasicErrorController中处理,其内部是通过判断请求头中的Accept的内容是否为text/html来区分请求是来自客户端浏览器(浏览器通常默认自动发送请求头内容Accept:text/html)还是客户端接口的调用,以此来决定返回页面视图还是 JSON 消息内容。
    相关BasicErrorController中代码如下:

package org.springframework.boot.autoconfigure.web.servlet.error;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping({"${server.error.path:${error.path:/error}}"})
public class BasicErrorController extends AbstractErrorController {
    private final ErrorProperties errorProperties;

    //构造器
    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
        this(errorAttributes, errorProperties, Collections.emptyList());
    }

    public BasicErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes, errorViewResolvers);
        Assert.notNull(errorProperties, "ErrorProperties must not be null");
        this.errorProperties = errorProperties;
    }

    //得到错误处理页面路径
    public String getErrorPath() {
        return this.errorProperties.getPath();
    }

    //返回页面形式结果
    @RequestMapping(produces = {"text/html"})
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        HttpStatus status = this.getStatus(request);
        Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
        response.setStatus(status.value());
        ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);//获取已配置的错误页面
        //如果没有配置,则返回默认的modelAndView,否则返回已配置的错误页面
        return modelAndView != null ? modelAndView : new ModelAndView("error", model);
    }

    //返回JSON格式的结果
    @RequestMapping
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = this.getStatus(request);
        return new ResponseEntity(body, status);
    }

    protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        IncludeStacktrace include = this.getErrorProperties().getIncludeStacktrace();
        if (include == IncludeStacktrace.ALWAYS) {
            return true;
        } else {
            return include == IncludeStacktrace.ON_TRACE_PARAM ? this.getTraceParameter(request) : false;
        }
    }

    //得到错误处理配置
    protected ErrorProperties getErrorProperties() {
        return this.errorProperties;
    }
}

    其中的 ErrorProperties :

package org.springframework.boot.autoconfigure.web;

import org.springframework.beans.factory.annotation.Value;

public class ErrorProperties {
    @Value("${error.path:/error}")
    private String path = "/error";

    private boolean includeException;

    private ErrorProperties.IncludeStacktrace includeStacktrace;

    private ErrorProperties.Whitelabel whitelabel;

    public ErrorProperties() {
        this.includeStacktrace = ErrorProperties.IncludeStacktrace.NEVER;
        this.whitelabel = new ErrorProperties.Whitelabel();
    }

    public String getPath() {
        return this.path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public boolean isIncludeException() {
        return this.includeException;
    }

    public void setIncludeException(boolean includeException) {
        this.includeException = includeException;
    }

    public ErrorProperties.IncludeStacktrace getIncludeStacktrace() {
        return this.includeStacktrace;
    }

    public void setIncludeStacktrace(ErrorProperties.IncludeStacktrace includeStacktrace) {
        this.includeStacktrace = includeStacktrace;
    }

    public ErrorProperties.Whitelabel getWhitelabel() {
        return this.whitelabel;
    }

    public void setWhitelabel(ErrorProperties.Whitelabel whitelabel) {
        this.whitelabel = whitelabel;
    }

    public static class Whitelabel {
        private boolean enabled = true;

        public Whitelabel() {
        }

        public boolean isEnabled() {
            return this.enabled;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }
    }

    public static enum IncludeStacktrace {
        NEVER,
        ALWAYS,
        ON_TRACE_PARAM;

        private IncludeStacktrace() {
        }
    }
}

    可以发现,默认错误页面路径为:/error。

 

修改默认错误页面路径

    在application.properties 配置文件中:

 

 

 

 

 

自定义错误页面

    覆盖默认的错误页面

    方式1(通用错误页面)、直接在/resources/templates下面创建error.html就可以覆盖默认的Whitelabel Error Page的错误页面,我项目用的是thymeleaf模板,对应的error.html代码如下:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
动态error错误页面
<p th:text="${error}"></p>
<p th:text="${status}"></p>
<p th:text="${message}"></p>
</body>
</html>

    这样运行的时候,请求一个不存在的页面或服务端处理发生异常时,展示的自定义错误界面如下:

 

    方式2(具体的某一种错误页面)、根据不同的状态码返回不同的视图页面,也就是对应的404,500等页面。

    静态页面:    

        如果只是静态HTML页面,不带错误信息的,在resources/public/下面创建error目录,在error目录下面创建对应的状态码html即可 ,例如,要将404映射到静态HTML文件,您的文件夹结构如下所示:

    静态404.html简单页面如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    静态404错误页面
</body>
</html>

    这样访问一个错误路径的时候,就会显示404错误页面:

    如果/resources/templates存在error.html,则状态码错误页面将覆盖error.html,因为具体状态码错误页面优先级比较高。

 

    动态页面:  

        如果是动态模板页面,可以带上错误信息,在resources/templates/下面创建error目录,在error目录下面命名即可:

    这里模拟下500错误,控制层代码,模拟一个除0的错误:

@Controller 
public class WebController extends  AbstractController{ 

    @ResponseBody 
    @RequestMapping(value="/test") 
    public String error(){ 
        int i=5/0; 
        return "ex"; 
    } 
} 

    500.html代码:

<!DOCTYPE html> 
<html xmlns:th="http://www.thymeleaf.org"> 
<head> 
<meta charset="UTF-8"> 
<title>Title</title> 
</head> 
<body> 
    动态500错误页面 
    <p th:text="${error}"></p> 
    <p th:text="${status}"></p> 
    <p th:text="${message}"></p> 
</body> 
</html> 

    这时访问 http://localhost:8080/test 即可看到如下错误,说明确实映射到了500.html

    如果同时存在静态页面500.html和动态模板的500.html,则后者覆盖前者。即templates/error/这个的优先级比resources/public/error高。

 

    总结:

  • error.html会覆盖默认的 whitelabel Error Page 错误提示
  • 静态错误页面优先级别比error.html高
  • 动态模板错误页面优先级比静态错误页面高

 

 

 

 

自定义异常页面的路径

    方法1:实现错误页面的配置

    1.实现WebMvcConfigurer 增加 自定义的错误处理 HandlerExceptionResolver :

@Configuration
public class MyWebMvcConfig implements WebMvcConfigurer {

    /**
     * 异常处理器
     *
     * @param exceptionResolvers
     */
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        //可添加多个HandlerExceptionResolver
        //第一个
        exceptionResolvers.add(0, new CustomExceptionResolver());
        //第二个
        exceptionResolvers.add(1, new WebExceptionResolver());
        //无须处理的异常集合,直接返回null,交由其他的异常处理类处理
    }
}

    2.自定义HandlerExceptionResolver 有两种实现:

        实现 HandlerExceptionResolver 接口,(此处 IO错误将返回到error/ioException,SQL错误将返回到error/sqlException):

package com.core.resolver;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @Description:
 * 自定义异常处理
 */
public class CustomExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        // 判断是否已进行了异常日志的记录
        String logging=(String)httpServletRequest.getAttribute("logging");
        if(logging==null||logging==""){
            Logger logger = LogManager.getLogger();
            logger.error(e.getMessage(), e);
            httpServletRequest.setAttribute("logging","true");
        }
        if(e instanceof IOException){//IO错误
            return new ModelAndView("error/ioException","ex",e);
        }else if(e instanceof SQLException){//SQL错误
            return new ModelAndView("error/sqlException","ex",e);
        }else if (e instanceof org.apache.shiro.authz.UnauthorizedException){//shiro 角色、权限验证错误
            ModelAndView mav = new ModelAndView("blank");
            mav.addObject("msg", "您没有足够的角色/权限进行操作!");
            mav.addObject("gotoPage", "login.jsp");
            return mav;
        }else if (e instanceof org.apache.shiro.authz.UnauthenticatedException){//shiro 登陆验证错误
            ModelAndView mav = new ModelAndView("blank");
            mav.addObject("msg", "您需要登陆才能继续操作!");
            mav.addObject("gotoPage", "login.jsp");
            return mav;
        }
        return null;
    }
}

        重写默认异常处理类 SimpleMappingExceptionResolver,(此处对没有进行异常日志记录的异常进行记录) :

package com.core.resolver;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @Description:
 * 重写默认异常处理类
 */
public class WebExceptionResolver extends SimpleMappingExceptionResolver {

    public WebExceptionResolver(){
        super.setExcludedExceptions(IOException.class,SQLException.class);
        super.setDefaultErrorView("error/error");//所有的异常定义默认的异常处理页面
        super.setExceptionAttribute("ex");//异常处理页面用来获取异常信息的变量名
        super.setDefaultStatusCode(500);//默认返回状态码
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        // 判断是否已进行了异常日志的记录
        String logging=(String)request.getAttribute("logging");
        if(logging==null||logging==""){
            Logger logger = LogManager.getLogger();
            logger.error(ex.getMessage(), ex);
            request.setAttribute("logging","true");
        }
        return super.doResolveException(request, response, handler, ex);
    }
}

    这种配置方法:在错误发生,BasicErrorController 将获取到其配置的错误页面。针对请求过程中,出现的异常均能处理。

    不足之处,对于渲染视图出现的错误,将无法进行处理。例如:

    

    


    方法2:局部异常处理 @Controller + @ExceptionHandler

    @ExceptionHandler注解到类的方法上,当此注解里定义的异常抛出时,此方法会被执行。

  • 如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。
  • 如果@ExceptionHandler所在的类带有@ControllerAdvice注解,则此方法会作用在所有的controller。

    举个简单例子,这里我们对除0异常用@ExceptionHandler来捕捉。

@Controller
public class WebController extends AbstractController{ 

	@RequestMapping(value="/ex") 
	@ResponseBody 
	public String error(){ 
		int i=5/0; 
		return "ex"; 
	} 

	//局部异常处理 
	@ExceptionHandler(Exception.class) 
	@ResponseBody 
	public String exHandler(Exception e){ 
		// 判断发生异常的类型是除0异常则做出响应 
		if(e instanceof ArithmeticException){ 
			return "发生了除0异常"; 
		} 
		// 未知的异常做出响应 
		return "发生了未知异常"; 
	}
} 

 

    方法3:异常处理 @ControllerAdvice + @ExceptionHandler

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。

    简单的说,进入Controller层的错误才会由@ControllerAdvice处理,拦截器抛出的错误以及访问错误地址的情况@ControllerAdvice处理不了,将由SpringBoot默认的异常处理机制处理。

    我们实际开发中,如果是要实现RESTful API,那么默认的JSON错误信息就不是我们想要的,这时候就需要统一一下JSON格式,所以需要封装一下。

/**
* 返回数据
*/
public class AjaxObject extends HashMap<String, Object> {
	private static final long serialVersionUID = 1L;

	public AjaxObject() {
		put("code", 0);
	}

	public static AjaxObject error() {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");
	}

	public static AjaxObject error(String msg) {
		return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);
	}

	public static AjaxObject error(int code, String msg) {
		AjaxObject r = new AjaxObject();
		r.put("code", code);
		r.put("msg", msg);
		return r;
	}

	public static AjaxObject ok(String msg) {
		AjaxObject r = new AjaxObject();
		r.put("msg", msg);
		return r;
	}

	public static AjaxObject ok(Map<String, Object> map) {
		AjaxObject r = new AjaxObject();
		r.putAll(map);
		return r;
	}

	public static AjaxObject ok() {
		return new AjaxObject();
	}

	public AjaxObject put(String key, Object value) {
		super.put(key, value);
		return this;
	}

	public AjaxObject data(Object value) {
		super.put("data", value);
		return this;
	}

	public static AjaxObject apiError(String msg) {
		return error(1, msg);
	}
}

    正确默认code返回0,data里面可以是集合,也可以是对象,正确情况返回的就是:

{
    code:0,
    msg:“获取列表成功”,
    data:{ 
        queryList :[]
    }
}

    如果是异常情况,返回的json则是:

{
    code:500,
    msg:“未知异常,请联系管理员”
}

    然后创建一个自定义的异常类:

public class BusinessException extends RuntimeException implements Serializable {

    private static final long serialVersionUID = 1L;
    private String msg;
    private int code = 500;
    
    public BusinessException(String msg) {
        super(msg);
        this.msg = msg;
    }
    
    public BusinessException(String msg, Throwable e) {
        super(msg, e);
        this.msg = msg;
    }
    
    public BusinessException(int code,String msg) {
        super(msg);
        this.msg = msg;
        this.code = code;
    }
    
    public BusinessException(String msg, int code, Throwable e) {
        super(msg, e);
        this.msg = msg;
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }
}

    Controller中添加一个json映射,用来处理这个异常

@Controller
public class WebController{

    @RequestMapping("/json")
    public void json(ModelMap modelMap) {
        System.out.println(modelMap.get("author"));
        int i=5/0;
    }
}

    最后创建异常处理类:

/**
 * 异常处理器
 */
@RestControllerAdvice
public class BusinessExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(getClass());

	/**
     * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
     * @param binder
     */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		System.out.println("请求有参数才进来");
	}

	/**
     * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
     * @param model
     */
	@ModelAttribute
	public void addAttributes(Model model) {
		model.addAttribute("author", "嘟嘟MD");
	}

	@ExceptionHandler(Exception.class)
	public Object handleException(Exception e,HttpServletRequest req){
		AjaxObject r = new AjaxObject();
		//业务异常
		if(e instanceof BusinessException){
			r.put("code", ((BusinessException) e).getCode());
			r.put("msg", ((BusinessException) e).getMsg());
		}else{//系统异常
			r.put("code","500");
			r.put("msg","未知异常,请联系管理员");
		}

		//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
		String contentTypeHeader = req.getHeader("Content-Type");
		String acceptHeader = req.getHeader("Accept");
		String xRequestedWith = req.getHeader("X-Requested-With");
		if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
			|| (acceptHeader != null && acceptHeader.contains("application/json"))
			|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
			return r;
		} else {
			ModelAndView modelAndView = new ModelAndView();
			modelAndView.addObject("msg", e.getMessage());
			modelAndView.addObject("url", req.getRequestURL());
			modelAndView.addObject("stackTrace", e.getStackTrace());
			modelAndView.setViewName("error");
			return modelAndView;
		}
	}
}

    @ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。

    其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,此处配置拦截Exception。

    error.html:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org" layout:decorator="layout">
<head>
    <title>Spring Boot管理后台</title>
    <script type="text/javascript">
    </script>
</head>
<body>
<div layout:fragment="content" th:remove="tag">
    <div  id="navbar">
        <h1>系统异常统一处理</h1>
        <h3 th:text="'错误信息:'+${msg}"></h3>
        <h3 th:text="'请求地址:'+${url}"></h3>

        <h2>Debug</h2>
        <a th:href="@{'https://www.google.com/webhp?hl=zh-CN#safe=strict&hl=zh-CN&q='+${msg}}"
           class="btn btn-primary btn-lg" target="_blank" id="Google">Google</a>
        <a th:href="@{'https://www.baidu.com/s?wd='+${msg}}" class="btn btn-info btn-lg"  target="_blank" id="Baidu">Baidu</a>
        <a th:href="@{'http://stackoverflow.com/search?q='+${msg}}"
           class="btn btn-default btn-lg"  target="_blank" id="StackOverFlow">StackOverFlow</a>
        <h2>异常堆栈跟踪日志StackTrace</h2>
        <div th:each="line:${stackTrace}">
            <div th:text="${line}"></div>
        </div>
    </div>
</div>
<div layout:fragment="js" th:remove="tag">
</div>
</body>
</html>

    访问http://localhost:8080/json的时候,因为是浏览器发起的,返回的是error界面:

    如果是ajax请求,返回的就是错误:

{ "msg":"未知异常,请联系管理员", "code":500 }

    这里我给带@ModelAttribute注解的方法通过Model设置了author值,在json映射方法中通过 ModelMwap 获取到该值。

    此处,用的是@RestControllerAdvice,而不是@ControllerAdvice,因为这里返回的主要是json格式,这样可以少写一个@ResponseBody。

 

 

 

 

    方法4:异常处理

    1.实现ErrorController:

package com.core.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @Description: 异常映射controller
 */
@Controller
//读取配置文件中的值
@RequestMapping({"${server.error.path:/error}"})
@EnableConfigurationProperties({ServerProperties.class})
public class ExceptionController implements ErrorController {

    private ErrorAttributes errorAttributes;

    @Autowired
    private ServerProperties serverProperties;

    @Value("${server.error.path:/error}")
    private String errorPath;

    /**
     * 初始化ExceptionController
     *
     * @param errorAttributes
     */
    @Autowired
    public ExceptionController(ErrorAttributes errorAttributes) {
        Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
        this.errorAttributes = errorAttributes;
    }

    /**
     * 定义404的ModelAndView
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = "text/html", value = "404")
    public ModelAndView errorHtml404(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        return new ModelAndView("/error/404", model);
    }

    /**
     * 定义404的JSON数据
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "404")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error404(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }

    /**
     * 定义500的ModelAndView
     *
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(produces = "text/html", value = "500")
    public ModelAndView errorHtml500(HttpServletRequest request, HttpServletResponse response) {
        response.setStatus(getStatus(request).value());
        Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        return new ModelAndView("error/500", model);
    }


    /**
     * 定义500的错误JSON信息
     *
     * @param request
     * @return
     */
    @RequestMapping(value = "500")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> error500(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));
        HttpStatus status = getStatus(request);
        return new ResponseEntity<Map<String, Object>>(body, status);
    }


    /**
     * Determine if the stacktrace attribute should be included.
     *
     * @param request  the source request
     * @param produces the media type produced (or {@code MediaType.ALL})
     * @return if the stacktrace attribute should be included
     */
    protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
        ErrorProperties.IncludeStacktrace include = this.serverProperties.getError().getIncludeStacktrace();
        if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
            return true;
        }
        if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
            return getTraceParameter(request);
        }
        return false;
    }


    /**
     * 获取错误的信息
     *
     * @param request
     * @param includeStackTrace
     * @return
     */
    private Map<String, Object> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace) {
        WebRequest webRequest = new ServletWebRequest(request);
        return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
    }

    /**
     * 是否包含trace
     *
     * @param request
     * @return
     */
    private boolean getTraceParameter(HttpServletRequest request) {
        String parameter = request.getParameter("trace");
        if (parameter == null) {
            return false;
        }
        return !"false".equals(parameter.toLowerCase());
    }

    /**
     * 获取错误编码
     *
     * @param request
     * @return
     */
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        try {
            return HttpStatus.valueOf(statusCode);
        } catch (Exception ex) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }

    @Override
    public String getErrorPath() {
        return this.errorPath;
    }
}

    实现了自定义的404、500错误的映射解析,分别对浏览器请求以及json请求做了回应。

    修改内嵌容器配置,设置错误页面映射:

package com.core.config;

@Configuration
public class MyWebMvcConfig{
    //读取配置文件中的值
    @Value("${server.error.path:/error}")
    private String errorPath;

    @Bean
    //修改内嵌容器的相关配置
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
        return factory -> {//使用lamda表达式
            //定义错误页面映射
            Set<ErrorPage> errorPageSet=new HashSet<ErrorPage>();
            errorPageSet.add(new ErrorPage(HttpStatus.NOT_FOUND,errorPath+"/404"));//404
            errorPageSet.add(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,errorPath+"/500"));//500
            // 可继续添加其他的错误页面映射
            factory.setErrorPages(errorPageSet);
        };
    }
}

    访问请求http://localhost:8080//web/myBlog/index1 ,结果如下:

© 著作权归作者所有

共有 人打赏支持
狼王黄师傅
粉丝 12
博文 248
码字总数 516783
作品 0
成都
程序员
私信 提问
SpringMVC 全局异常处理的简单应用

在SpringMVC框架的项目开发过程中,你还在使用 try{} catch(){} 输出异常吗?,那样你就真的OUT了,SpringMVC为我们提供了强大的异常捕捉类,我们只需要稍稍处理即可; 1, 编写全局处理类: 2, 将该...

ge洋
2018/01/07
0
0
baomidou/kaptcha-spring-boot-starter

kaptcha-spring-boot-starter 简介 kaptcha-spring-boot-starter 基于 springBoot2.0 和 Google Kaptcha 验证码组件,kaptcha-spring-boot-starter可以很方便的集成验证码到你的系统中。 如何...

baomidou
2018/05/05
0
0
Spring boot 前后台分离项目 怎么处理spring security 抛出的异常

最近在开发一个项目 前后台分离的 使用 spring boot + spring security + jwt 实现用户登录权限控制等操作。但是 在用户登录的时候,怎么处理spring security 抛出的异常呢?使用了@RestCont...

developlee的潇洒人生
2018/05/07
1K
3
Spring Boot 2 Webflux的全局异常处理

本文首先将会回顾Spring 5之前的SpringMVC异常处理机制,然后主要讲解Spring Boot 2 Webflux的全局异常处理机制。 SpringMVC的异常处理 Spring 统一异常处理有 3 种方式,分别为: 使用 注解...

aoho
2018/12/20
0
0
苞米豆 — 谷歌验证码快速启动器 1.1.0 发布

https://gitee.com/baomidou/kaptcha-spring-boot-starter 修复了一个重复校验引起的Np bug 支持了jdk 1.7 简介 kaptcha-spring-boot-starter 基于 springBoot 和 Google Kaptcha 验证码组件......

小锅盖
2018/07/20
2.3K
10

没有更多内容

加载失败,请刷新页面

加载更多

从 for of 聊到 Generator

你能学到什么 对 for of 更深入的理解 iterator 到底是何方神圣? 数组也是对象,为什么不能用 for of 来遍历对象呢? 如何实现对象的 for of? Generator 又是何方神圣? Generator 有什么用呢...

Jack088
38分钟前
3
0
怎么判断go-sql-driver 安装成功

.下载安装   执行下面两个命令:     下载:go get github.com/Go-SQL-Driver/MySQL     安装:go install github.com/Go-SQL-Driver/MySQL   怎么判断go-sql-driver 安装成功 ...

dragon_tech
47分钟前
0
0
刚入职阿里,告诉你真实的职场生活,兼谈P6、P7、P8的等级

一:拿下offer的人,基本上都有什么特征? 二:为什么选择阿里? 三:阿里的工作氛围什么样? 四:阿里的薪资情况? 五:阿里的晋升空间有多大? 最近部门招聘,很多工程师,包括我在内都参与...

java知识分子
今天
5
0

中国龙-扬科
今天
1
0
windows 安装nvm

1、nvw-windows的官网:https://github.com/coreybutler/nvm-windows/releases 2、选择nvm-setup.zip安装 3、配置环境变量 4、检查nvm是否安装成功 使用管理员权限打开一个命令行。输入nvm v...

灰白发
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部