SpringBoot全局異常處理方式

SpringBoot全局異常處理

為瞭讓客戶端能有一個更好的體驗,當客戶端發送請求到服務端發生錯誤時服務端應該明確告訴客戶端錯誤信息。

在這裡插入圖片描述

SpringBoot內置的異常處理返回的界面太雜亂,不夠友好。我們需要將異常信息做封裝處理響應給前端。本文介紹的為將錯誤信息統一封裝成如下格式json響應給前端。

{
	code:10001,
	message:xxxxx,
	request:GET url
}

自定義異常類

package com.lin.missyou.exception;
public class HttpException extends RuntimeException{
    protected Integer code;
    protected Integer httpStatusCode;
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public Integer getHttpStatusCode() {
        return httpStatusCode;
    }
    public void setHttpStatusCode(Integer httpStatusCode) {
        this.httpStatusCode = httpStatusCode;
    }
}
package com.lin.missyou.exception;
public class NotFoundException extends HttpException{
    public NotFoundException(int code){
        this.httpStatusCode = 404;
        this.code = code;
    }
}
package com.lin.missyou.exception;
public class ForbiddenException extends HttpException{
    public ForbiddenException(int code){
        this.httpStatusCode = 403;
        this.code = code;
    }
}

創建一個用於封裝異常信息的類UnifyResponse

package com.lin.missyou.core;
public class UnifyResponse {
    private int code;
    private String message;
    private String request;
    public int getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
    public String getRequest() {
        return request;
    }
    public UnifyResponse(int code, String message, String request) {
        this.code = code;
        this.message = message;
        this.request = request;
    }
}

將異常信息寫在配置文件exception-code.properties裡

lin.codes[10000] = 通用異常
lin.codes[10001] = 通用參數錯誤

自定義配置類管理配置文件

package com.lin.missyou.core.configuration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
@PropertySource(value="classpath:config/exception-code.properties")
@ConfigurationProperties(prefix = "lin")
@Component
public class ExceptionCodeConfiguration {
    private Map<Integer,String> codes = new HashMap<>();
    public Map<Integer, String> getCodes() {
        return codes;
    }
    public void setCodes(Map<Integer, String> codes) {
        this.codes = codes;
    }
    public String getMessage(int code) {
        String message = codes.get(code);
        return message;
    }
}

創建一個全局異常處理類GlobalExceptionAdvice,用@ControllerAdvice標明異常處理類。@ResponseStatus用於指定http狀態碼。

@ExceptionHandler標明異常處理器,傳入參數指定當前函數要處理哪種類型的異常。Springboot會幫我們把這些異常信息傳入到函數。第一個函數用於處理未知異常,不需要向前端提供詳細的錯誤原因,隻需提示統一的文本信息即可。

第二個函數用於處理已知異常,需要指明具體的錯誤原因,需要根據Exception傳遞過來的信息靈活的定制httpStatusCode。ResponseEntity可以自定義很多屬性,包括可以設置httpheaders,httpbodys,httpStatus。

package com.lin.missyou.core;
import com.lin.missyou.core.config.ExceptionCodeConfiguration;
import com.lin.missyou.exception.HttpException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
@ControllerAdvice
public class GlobalExceptionAdvice {
    @Autowired
    ExceptionCodeConfiguration exceptionCodeConfiguration ;
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public UnifyResponse handleException(HttpServletRequest req,Exception e){
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        System.out.println(e);
        UnifyResponse unifyResponse = new UnifyResponse(9999,"服務器錯誤",method+" "+requestUrl);
        return unifyResponse;
    }
    @ExceptionHandler(HttpException.class)
    public ResponseEntity<UnifyResponse> handleHttpException(HttpServletRequest req, HttpException e){
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        System.out.println(e);
        UnifyResponse unifyResponse = new UnifyResponse(e.getCode(),exceptionCodeConfiguration.getMessage(e.getCode()),method+" "+requestUrl);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        HttpStatus httpStatus = HttpStatus.resolve(e.getHttpStatusCode());
        ResponseEntity<UnifyResponse> responseEntity = new ResponseEntity(unifyResponse,httpHeaders,httpStatus);
        return responseEntity;
    }
    //參數校驗
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public UnifyResponse handleBeanValidation(HttpServletRequest req, MethodArgumentNotValidException e){
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        List<ObjectError> errors = e.getBindingResult().getAllErrors();
        String message = formatAllErrorMessages(errors);
        return new UnifyResponse(10001,message,method+" "+requestUrl);
    }
    private String formatAllErrorMessages(List<ObjectError> errors){
        StringBuffer errorMsg = new StringBuffer();
        errors.forEach(error ->
                errorMsg.append(error.getDefaultMessage()).append(";")
        );
        return errorMsg.toString();
    }
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public UnifyResponse handleConstrainException(HttpServletRequest req, ConstraintViolationException e){
        String method = req.getMethod();
        String requestUrl = req.getRequestURI();
        String message = e.getMessage();
        return new UnifyResponse(10001,message,method+" "+requestUrl);
    }
}

在這裡插入圖片描述

響應信息可能會出現亂碼現象,修改配置文件編碼。在設置面板搜索File Encodings,Default encoding for properties files選擇UTF-8,勾選Transparent native-to-ascii conversion

在這裡插入圖片描述

springboot全局異常處理——@ControllerAdvice+ExceptionHandler

一、全局捕獲異常後,返回json給瀏覽器

項目結構:

在這裡插入圖片描述

1、自定義異常類 MyException.java

package com.gui.restful;
/**
 * 自定義異常類
 */
public class MyException extends RuntimeException{
    private String code;
    private String msg;
    public MyException(String code,String msg){
        this.code=code;
        this.msg=msg;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

2、控制器 MyController.java

package com.gui.restful;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
/**
 * controller拋出異常
 */
@RestController
public class MyController {
    @RequestMapping("hello")
    public String hello() throws Exception{
        throw new MyException("101","系統異常");
    }
}

3、全局異常處理類 MyControllerAdvice

package com.gui.restful;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
 * 全局異常捕獲處理
 */
@ControllerAdvice //controller增強器
public class MyControllerAdvice {
    @ResponseBody
    @ExceptionHandler(value=MyException.class) //處理的異常類型
    public Map myExceptionHandler(MyException e){
        Map<String,String> map=new HashMap<>();
        map.put("code",e.getCode());
        map.put("msg",e.getMsg());
        return map;
    }
}

4、運行結果

啟動應用,訪問 http://localhost:8080/hello,出現以下結果,說明自定義異常被成功攔截

在這裡插入圖片描述

二、全局捕獲異常後,返回頁面給瀏覽器

1、自定義異常類 MyException.java(同上)

2、控制器 MyController.java(同上)

3、全局異常處理類 MyControllerAdvice

package com.gui.restful;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import java.util.HashMap;
import java.util.Map;
/**
 * 全局異常捕獲處理
 */
@ControllerAdvice //controller增強器
public class MyControllerAdvice {
    @ExceptionHandler(value=MyException.class) //處理的異常類型
    public ModelAndView myExceptionHandler(MyException e){
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("error");
        modelAndView.addObject("code",e.getCode());
        modelAndView.addObject("msg",e.getMsg());
        return modelAndView;
    }
}

4、頁面渲染 error.ftl(用freemarker渲染)

pom.xml中引入freemarker依賴

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-freemarker</artifactId>
		</dependency>

error.ftl

<!DOCTYPE>
<html>
<head>
<title>錯誤頁面</title>
</head>
<body>
<h1>code:$[code]</h1>
<h1>msg:${msg}</h1>
</body>
</html>

5、運行結果

啟動應用,訪問 http://localhost:8080/hello,出現以下結果,說明自定義異常被成功攔截

在這裡插入圖片描述

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: