Spring Boot全局異常處理

Spring Boot默認的異常處理機制

    默認狀況下,Spring Boot爲兩種狀況提供了不一樣的響應方式:javascript

    一種是瀏覽器客戶端請求一個不存在的頁面或服務端處理髮生異常時,通常狀況下瀏覽器默認發送的請求頭中Accept: text/html,因此Spring Boot默認會響應一個html文檔內容,稱做「Whitelabel Error Page」。html

    另外一種是使用Postman等調試工具發送請求一個不存在的url或服務端處理髮生異常時,Spring Boot會返回相似以下的Json格式字符串信息java

{
    "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中代碼以下:web

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 :ajax

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。spring

 

修改默認錯誤頁面路徑

    在application.properties 配置文件中:sql

 

 

 

 

 

自定義錯誤頁面

    覆蓋默認的錯誤頁面

    方式1(通用錯誤頁面)、直接在/resources/templates下面建立error.html就能夠覆蓋默認的Whitelabel Error Page的錯誤頁面,我項目用的是thymeleaf模板,對應的error.html代碼以下:apache

<!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>

    這樣運行的時候,請求一個不存在的頁面或服務端處理髮生異常時,展現的自定義錯誤界面以下:json

 

    方式2(具體的某一種錯誤頁面)、根據不一樣的狀態碼返回不一樣的視圖頁面,也就是對應的404,500等頁面。api

    靜態頁面:    

        若是隻是靜態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;
    }
}

    實現了自定義的40四、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 ,結果以下:

相關文章
相關標籤/搜索