知識點-Spring Boot 異常處理彙總

猿天地發表於2018-05-24

在實際開發中,我們會遇到很多異常,在發生異常的時候Spring Boot預設提供了錯誤頁面展示給使用者。看似比較友好,其實頁面很醜。

上面講的是做頁面開發的時候遇到的問題,還有一種情況就是用來開發Rest介面,當錯誤的時候我們希望返回給使用者的是我們介面的標準格式,不是返回一段html程式碼。

接下來分別給大家介紹下解決方案:

頁面處理

首先我們來看頁面錯誤的處理情況,當我們的程式內部報錯的時候或者訪問的頁面找不到的時候,我們可以看到下面的錯誤頁面:

1.png

我們可以自己設計好看一點的頁面來替換這個頁面,這樣會更友好點,比如我們看今日頭條的頁面:

2.png

以前用Spring MVC時都是直接配置web.xml

<error-page>
    <error-code>404</error-code>
    <location>/WEB-INF/view/404.jsp</location>
</error-page>
<error-page>
    <error-code>400</error-code>
    <location>/WEB-INF/view/400.jsp</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/WEB-INF/view/500.jsp</location>
</error-page>
複製程式碼

在Spring Boot中也非常簡單,直接編寫對應的錯誤頁面,進行覆蓋即可:

/**
 * 自定義錯誤頁面覆蓋spring boot中的錯誤頁面
 * @author yinjihuan
 *
 */
@Controller
public class ErrorController {
    @GetMapping("/400")
    public String badRequest() {
        return "error/400";
    }
    @GetMapping("/404")
    public String notFound() {
        return "error/404";
    }
    @GetMapping("/500")
    public String serverError() {
        return "error/500";
    }
}
複製程式碼

頁面內容可以自己寫:

<body>
    <section id="error" class="container text-center" style="height:800px;">
        <h1>404, 頁面沒有找到</h1>
        <p>您正在尋找的頁面不存在或發生其他錯誤。</p>
        <a class="btn btn-primary" href="http://pan.cxytiandi.com">回到網站首頁</a> 
    </section>
</body>
複製程式碼

REST介面處理

在開發rest介面時,我們往往會定義統一的返回格式,列如:

{
  "status": true,
  "code": 200,
  "message": null,
  "data": [
    {
      "id": "101",
      "name": "jack"
    },
    {
      "id": "102",
      "name": "jason"
    }
  ]
}
複製程式碼

但是如果呼叫方請求我們的api時把介面地址寫錯了,就會得到一個404錯誤頁面,最友好的方式就是返回固定的JSON格式,裡面有個code為404。

所以我們需要在發生這種系統錯誤時也能返回我們自定義的那種格式

定義一個異常處理類

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
	
	/**
	 * 系統異常處理,比如:404,500
	 * @param req
	 * @param resp
	 * @param e
	 * @return
	 * @throws Exception
	 */
	@ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseData defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
		logger.error("", e);
		ResponseData r = new ResponseData();
        r.setMessage(e.getMessage());
        if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
        	 r.setCode(404);
		} else {
			 r.setCode(500);
		}
        r.setData(null);
        r.setStatus(false);
        return r;
    }
}

複製程式碼

ResponseData是我們返回格式的實體類

public class ResponseData {
	private Boolean status = true;
	private int code = 200;
	private String message;
	private Object data;
}
複製程式碼

這種在發生錯誤時這邊會捕獲到,然後封裝好返回格式,返回給呼叫方

最後關鍵的一步是在spring boot的配置檔案中加上如下配置:

#出現錯誤時, 直接丟擲異常
spring.mvc.throw-exception-if-no-handler-found=true
#不要為我們工程中的資原始檔建立對映
spring.resources.add-mappings=false
複製程式碼

然後我們呼叫一個不存在的介面時,返回的錯誤資訊就是我們自定義的那種格式了

{
  "status": false,
  "code": 404,
  "message": "No handler found for GET /rest11/auth",
  "data": null
}
複製程式碼

頁面錯誤和REST錯誤共存

當我們加好rest介面處理的方式後,訪問頁面不存在就會返回一段json資料,如果你的專案中既有rest介面,又有頁面,這個時候就有衝突了。

我們可以通過為rest介面增加統一的訪問字首,比如:/rest/xxxx來區分請求,然後用@ControllerAdvice來分別處理:

import java.io.PrintWriter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;


@ControllerAdvice
public class GlobalExceptionHandler {
	private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

	/**
	 * 系統異常處理,比如:404,500
	 * 
	 * @param req
	 * @param resp
	 * @param e
	 * @return
	 * @throws Exception
	 */
	@ExceptionHandler(value = Exception.class)
	// @ResponseBody
	public Object defaultErrorHandler(HttpServletRequest req, HttpServletResponse response, Exception e)
			throws Exception {
		logger.error("", e);
		if (req.getRequestURI().startsWith("/rest")) {
			ResponseData r = new ResponseData();
			r.setMessage(e.getMessage());
			if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
				r.setCode(404);
			} else {
				r.setCode(500);
			}
			r.setData(null);
			r.setStatus(false);
			PrintWriter writer = response.getWriter();
			writer.println(JsonUtils.toJson(r));
			writer.flush();
			writer.close();
			return null;
		} else {
			if (e instanceof org.springframework.web.servlet.NoHandlerFoundException) {
				return "error/404";
			} else {
				return "error/500";
			}
		}

	}

}
複製程式碼

更多技術分享請關注微信公眾號:猿天地

image.png

相關文章