SpringBoot系列(7)——統一異常處理

1. 固定格式

1.1 因爲返回的格式比較亂,因此先對返回格式進行統一,返回消息格式參照以下模板:java

{
    "code":1,
    "msg":"年齡不小於10歲",
    "data":null
}

{
    "code":0,
    "msg":"成功"
    "data":{
        "id":20,
        "age":18,
        "name":"hahaha"
    }
}
  • code,表明狀態碼,0爲成功,1爲異常
  • msg,提示信息,成功時返回"成功",異常時返回異常信息
  • data,返回數據,異常時data爲null,成功時包含返回的數據

1.2 按照如上的消息格式,在domain下新建Result類,該類是Http請求返回的最外層對象(code,msg,data):程序員

package com.example.demo.domain;

/**
 * Http請求返回的最外層對象
 * Created by xzf on 2017/9/19.
 */
public class Result<T> {
    //錯誤碼
    private Integer code;

    //提示信息
    private String msg;

    //具體的內容,用泛型表示
    private T data;

    public Integer getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

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

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

1.3 把設置返回結果的方法抽象成 ResultUtil 工具類(這樣可複用,避免代碼冗餘),在具體業務場景中,調用 ResultUtil 工具類中的對應方法,以下圖:web

當發生錯誤時,調用ResultUtil中的error方法;當執行「添加一個學生」成功時,則調用 ResultUtil 的success方法。spring

那麼,ResultUtil工具類中的success和error具體是怎麼定義的的?其代碼以下:json

package com.example.demo.utils;

import com.example.demo.domain.Result;

/**
 * Created by xzf on 2017/9/19.
 */
public class ResultUtil {
    public static Result success(Object object){
        Result result=new Result();
        result.setCode(0);
        result.setMsg("成功");
        result.setData(object);
        return result;
    }

    //成功的狀況下也可能不含object(即成功也可能沒有返回數據)
    public static Result success(){
        //此處調用上面定義的success方法,傳入null值
        return success(null);
    }

    public static Result error(Integer code,String msg){
        Result result=new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}

 

2. 引入異常,採用異常處理

下面這個方法經過傳入一個id,得到該id對應的學生的年齡。在該方法中,對得到的學生年齡進行了判斷,並根據判斷結果拋出異常框架

//studentService中的getAge方法
public void getAge (Integer id)throws Exception{
        Student student= studentRepository.findOne(id);
        Integer age=student.getAge();
        if (age<10){
            //返回"你可能在上小學"  code=100
            throw new StudentException(ResultEnum.PRIMARY_SCHOOL);
        }else if (age>10 && age<16){
            //返回"你可能在上初中"  code=101
            throw new StudentException(ResultEnum.MIDDLE_SCHOOL);
        }
    }

不難看出,這裏使用了自定義的異常類StudentExceptiondom

 

2.1 自定義異常

由於原生的Exception異常類,其構造方法只能傳入一個String 類型的message,而咱們定義的返回json的模板須要傳入的除了錯誤提示消息,還有錯誤碼,所以此處自定義一個異常類——StudentException工具

package com.example.demo.exception;

import com.example.demo.enums.ResultEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.plugin2.message.Message;

/**
 * Created by xzf on 2017/9/19.
 */
//Spring框架只對RuntimeException拋出的異常進行回滾,所以此處不用Exception
public class StudentException extends RuntimeException{

    private Integer code;

    public StudentException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());//父類構造方法中自己會傳一個message進去
        this.code = resultEnum.getCode();
    }

    public Integer getCode() {
        return code;
    }

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

在上面的代碼中咱們看到,在拋StudentException異常時,該異常的構造方法中傳入的參數是「ResultEnum.PRIMARY_SCHOOL 」,他們是什麼呢?不是說須要傳入「錯誤碼+返回消息」嗎?其實,這裏的「ResultEnum.PRIMARY_SCHOOL 」就包含了這兩個信息。this

 

2.2 code和message設成枚舉,統一管理

原來,爲了方便對錯誤碼(狀態碼)進行統一管理,使得錯誤碼和其相應的返回消息可以一一對應,也方便程序員查看和修改,這裏把錯誤碼和返回消息設置成一個枚舉類:spa

package com.example.demo.enums;

/**
 * 將code和message對應起來
 * Created by xzf on 2017/9/19.
 */
public enum ResultEnum {
    UNKNOW_ERROR(-1,"未知錯誤"),
    SUCCESS(0,"成功"),
    PRIMARY_SCHOOL(100,"你可能在上小學"),
    MIDDLE_SCHOOL(101,"你可能在上初中"),
    ;
    private Integer code;
    private String msg;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    /**
     * 枚舉類中給getter方法就能夠了,枚舉的使用都是使用構造方法來建立,不會再set它的值
     * @return
     */
    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

這樣一來,錯誤碼和它所對應的返回消息就一目瞭然了。

在這裏拗了半天,下面來捋一捋關係:

簡單說來,就是把原來的拋異常,改爲了拋一個自定義的異常;把原來要傳入的兩個參數(code和msg),改爲了傳入一個枚舉類參數(枚舉類中包含了這兩個信息)

 

2.3 捕獲異常

既然是拋出了異常,那麼就須要捕獲異常,並採起相應的操做。這裏咱們在handle下新建一個ExceptionHandle類,用於捕獲並處理上面的異常:

package com.example.demo.handle;

import com.example.demo.domain.Result;
import com.example.demo.exception.StudentException;
import com.example.demo.utils.ResultUtil;
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;

/**
 * Created by xzf on 2017/9/19.
 */
@ControllerAdvice
public class ExceptionHandle {

    private static final Logger logger= LoggerFactory.getLogger(ExceptionHandle.class);

    @ExceptionHandler(value = Exception.class)  //聲明捕獲的異常類
    @ResponseBody       //返回的是json數據,然而類前使用的不是@RestController註解,所以須要加上@ResponseBody
    public Result handle(Exception e){
        //判斷捕獲的異常是不是自定義的StudentException
        if (e instanceof StudentException){
            StudentException exception=(StudentException) e;
            return ResultUtil.error(exception.getCode(),exception.getMessage());
        }else {
            logger.error("【系統異常】",e);//控制檯輸出異常信息
            //前臺返回「-1,未知錯誤」,而且該異常被捕獲處理了,控制檯不顯示,因此這裏藉助日誌來排查錯誤
            return ResultUtil.error(-1,"未知錯誤!");//返回json信息爲"未知錯誤",
        }

    }

}

從上面代碼能夠看到,在處理異常的時候,傳過來的 StudentException 類中的值就能夠用來設置進Result中了

相關文章
相關標籤/搜索