項目構建基礎之統一結果和統一異常

參考文章:https://mp.weixin.qq.com/s/qwzXJ9WDeTAsSsO2297CYQ

                  https://www.cnblogs.com/xuwujing/p/10933082.html

目錄

 (1) 統一結果返回

 (2) 統一異常處理


 (1)統一結果返回

目前的前後端開發大部分數據的傳輸格式都是json,因此定義一個統一規範的數據格式有利於前後端交互與UI 的展示。

統一結果的一般格式: 1.是否響應 2.響應狀態碼 3.狀態碼的描述 4.響應數據5.其他標識符

a.  ResultCodeEnum

package com.test.common.result;

import lombok.Getter;

/**
 * @Author tanghh
 * @Date 2020/4/30 11:06
 */
@Getter
public enum ResultCodeEnum {
    SUCCESS(true,20000,"成功"),
    UNKNOWN_ERROR(false,20001,"未知錯誤"),
    PARAM_ERROR(false,20002,"參數錯誤"),
    ;


    // 響應是否成功
    private Boolean success;
    // 響應狀態碼
    private Integer code;
    // 響應信息
    private String message;

    ResultCodeEnum(boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }
}

b.統一返回結果類

package com.test.common.result;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author tanghh
 * @Date 2020/4/30 13:44
 */
@Data
public class R {
    private Boolean success;

    private Integer code;

    private String message;

    private Map<String, Object> data = new HashMap<>();

    // 構造器私有
    private R(){}

    // 通用返回成功
    public static R ok() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
        r.setCode(ResultCodeEnum.SUCCESS.getCode());
        r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
        return r;
    }

    // 通用返回失敗,未知錯誤
    public static R error() {
        R r = new R();
        r.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess());
        r.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode());
        r.setMessage(ResultCodeEnum.UNKNOWN_ERROR.getMessage());
        return r;
    }

    // 設置結果,形參爲結果枚舉
    public static R setResult(ResultCodeEnum result) {
        R r = new R();
        r.setSuccess(result.getSuccess());
        r.setCode(result.getCode());
        r.setMessage(result.getMessage());
        return r;
    }

    /**------------使用鏈式編程,返回類本身-----------**/

    // 自定義返回數據
    public R data(Map<String,Object> map) {
        this.setData(map);
        return this;
    }

    // 通用設置data
    public R data(String key,Object value) {
        this.data.put(key, value);
        return this;
    }

    // 自定義狀態信息
    public R message(String message) {
        this.setMessage(message);
        return this;
    }

    // 自定義狀態碼
    public R code(Integer code) {
        this.setCode(code);
        return this;
    }

    // 自定義返回結果
    public R success(Boolean success) {
        this.setSuccess(success);
        return this;
    }
}

c.寫一個接口。

package com.test.controller;

import com.test.common.result.R;
import com.test.model.Student;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author tanghh
 * @Date 2020/4/30 13:45
 */
@RestController
@RequestMapping("/test")
public class TestResultController {


    @GetMapping(value = "/listAllStudent")
    public R list() {
        List<Student> list = new ArrayList<>();
        Student student1 = new Student(1,"soup");
        Student student2 = new Student(2,"soup_tang");

        list.add(student1);
        list.add(student2);
        return R.ok().data("items", list).message("用戶列表");
    }
}

d.測試一下這個返回結果的類

在瀏覽器上訪問 http://localhost:8006/test/listAllStudent

 

(2) 統一異常處理

在使用統一返回結果時,對於在程序運行時有些異常我們無法提前預知,不能走到我們return的R對象返回,這個時候我們就需要定義一個統一的全局異常來捕獲這些信息,並作爲一種結果返回控制層

@ControllerAdvice

改註解爲統一異常處理的核心,是一種作用於控制層的切面通知(Advice),該註解能夠將通用的@ExceptionHandler

@InitBinder 和@ModelAttributes 方法收集到一個類型,並作用到所有控制器上。

該類的設計思路:

  1. 使用@ExceptionHandler 註解捕獲指定或自定義的異常;
  2. 使用@ControllerAdvice 集成@ExceptionHandler 的方法到一個類中;
  3. 必須定義一個通用的異常捕獲方法,遍於捕獲未定義的異常信息;
  4. 自定義一個異常類,捕獲針對項目或業務的異常;
  5.  
  6. 異常的對象信息補充到統一結果枚舉中;

(1)自定義基礎接口類

整個路徑的文件如下:(我這邊爲了方便演示,直接將不同包下的文件放到這個一個包了,開發的時候不要學我,)

首先定義一個基礎的接口類,自定義的錯誤描述枚舉類需實現該接口。

package com.test.common.exception;

/**
 * @Author tanghh
 * @Date 2020/4/30 17:06
 */
public interface BaseErrorInfoInterface {
    /**
     * 錯誤碼
     * @return
     */
    String getResultCode();
    /**
     * 錯誤描述
     * @return
     */
    String getResultMsg();
}

(2)自定義一個枚舉類,實現上述接口。

package com.test.common.exception;

/**
 * @Author tanghh
 * @Date 2020/4/30 17:06
 */
public enum CommonEnum implements BaseErrorInfoInterface {
    // 數據操作錯誤定義
    SUCCESS("200", "成功!"),
    BODY_NOT_MATCH("400","請求的數據格式不符!"),
    SIGNATURE_NOT_MATCH("401","請求的數字簽名不匹配!"),
    NOT_FOUND("404", "未找到該資源!"),
    INTERNAL_SERVER_ERROR("500", "服務器內部錯誤!"),
    SERVER_BUSY("503","服務器正忙,請稍後再試!");

    /** 錯誤碼 */
    private String resultCode;

    /** 錯誤描述 */
    private String resultMsg;

    CommonEnum(String resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }

    @Override
    public String getResultCode() {
        return resultCode;
    }

    @Override
    public String getResultMsg() {
        return resultMsg;
    }

}

(3)自定義異常子類,用於處理我們發生的業務異常。

package com.test.common.exception;

/**
 * @Author tanghh
 * @Date 2020/4/30 17:07
 */
public class BizException extends RuntimeException {
    private static final long serialVersionUID = 1L;

    /**
     * 錯誤碼
     */
    protected String errorCode;
    /**
     * 錯誤信息
     */
    protected String errorMsg;

    public BizException() {
        super();
    }

    public BizException(BaseErrorInfoInterface errorInfoInterface) {
        super(errorInfoInterface.getResultCode());
        this.errorCode = errorInfoInterface.getResultCode();
        this.errorMsg = errorInfoInterface.getResultMsg();
    }

    public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
        super(errorInfoInterface.getResultCode(), cause);
        this.errorCode = errorInfoInterface.getResultCode();
        this.errorMsg = errorInfoInterface.getResultMsg();
    }

    public BizException(String errorMsg) {
        super(errorMsg);
        this.errorMsg = errorMsg;
    }

    public BizException(String errorCode, String errorMsg) {
        super(errorCode);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public BizException(String errorCode, String errorMsg, Throwable cause) {
        super(errorCode, cause);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }


    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    public String getMessage() {
        return errorMsg;
    }

    @Override
    public Throwable fillInStackTrace() {
        return this;
    }

}

(4)自定義數據格式

package com.test.common.exception;

import com.alibaba.fastjson.JSONObject;

/**
 * @Author tanghh
 * @Date 2020/4/30 17:07
 */
public class ResultBody {
    /**
     * 響應代碼
     */
    private String code;

    /**
     * 響應消息
     */
    private String message;

    /**
     * 響應結果
     */
    private Object result;

    public ResultBody() {
    }

    public ResultBody(BaseErrorInfoInterface errorInfo) {
        this.code = errorInfo.getResultCode();
        this.message = errorInfo.getResultMsg();
    }

    public String getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    /**
     * 成功
     *
     * @return
     */
    public static ResultBody success() {
        return success(null);
    }

    /**
     * 成功
     * @param data
     * @return
     */
    public static ResultBody success(Object data) {
        ResultBody rb = new ResultBody();
        rb.setCode(CommonEnum.SUCCESS.getResultCode());
        rb.setMessage(CommonEnum.SUCCESS.getResultMsg());
        rb.setResult(data);
        return rb;
    }

    /**
     * 失敗
     */
    public static ResultBody error(BaseErrorInfoInterface errorInfo) {
        ResultBody rb = new ResultBody();
        rb.setCode(errorInfo.getResultCode());
        rb.setMessage(errorInfo.getResultMsg());
        rb.setResult(null);
        return rb;
    }

    /**
     * 失敗
     */
    public static ResultBody error(String code, String message) {
        ResultBody rb = new ResultBody();
        rb.setCode(code);
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    /**
     * 失敗
     */
    public static ResultBody error( String message) {
        ResultBody rb = new ResultBody();
        rb.setCode("-1");
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }

}

(5)自定義全局異常類

package com.test.common.exception;

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;

import javax.servlet.http.HttpServletRequest;

/**
 * @Author tanghh
 * @Date 2020/4/30 17:08
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 處理自定義的業務異常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BizException.class)
    @ResponseBody
    public  ResultBody bizExceptionHandler(HttpServletRequest req, BizException e){
        logger.error("發生業務異常!原因是:{}",e.getErrorMsg());
        return ResultBody.error(e.getErrorCode(),e.getErrorMsg());
    }

    /**
     * 處理空指針的異常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e){
        logger.error("發生空指針異常!原因是:",e);
        return ResultBody.error(CommonEnum.BODY_NOT_MATCH);
    }


    /**
     * 處理其他異常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
    @ResponseBody
    public ResultBody exceptionHandler(HttpServletRequest req, Exception e){
        logger.error("未知異常!原因是:",e);
        return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR);
    }
}

(6)定義一個實體類

package com.test.common.exception;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "student", catalog = "")
public class Student {

    private Integer sid;
    private String sname;
    @Id
    @GeneratedValue
    public Integer getSid() {
        return sid;
    }

    public void setSid(Integer sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public Student() {
    }

    public Student(Integer sid, String sname) {
        setSid(sid);
        setSname(sname);
    }
}

(7)Controller (爲了測試效果,我這邊弄出了一些異常)

package com.test.common.exception;

/**
 * @Author tanghh
 * @Date 2020/4/30 17:09
 */

import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping(value = "/api")
public class TestStudentController {
    @PostMapping("/user")
    public boolean insert(@RequestBody Student student) {
        System.out.println("開始新增...");
        //如果姓名爲空就手動拋出一個自定義的異常!
        if(student.getSname()==null){
            throw  new BizException("-1","用戶姓名不能爲空!");
        }
        return true;
    }

    @PutMapping("/user")
    public boolean update(@RequestBody Student student) {
        System.out.println("開始更新...");
        //這裏故意造成一個空指針的異常,並且不進行處理
        String str=null;
        str.equals("111");
        return true;
    }

    @DeleteMapping("/user")
    public boolean delete(@RequestBody Student student)  {
        System.out.println("開始刪除...");
        //這裏故意造成一個異常,並且不進行處理
        Integer.parseInt("abc123");
        return true;
    }

    @GetMapping("/user")
    public List<Student> findByUser(Student student) {
        System.out.println("開始查詢...");
        List<Student> userList =new ArrayList<>();
        Student student1=new Student(1,"soup_tang");
        userList.add(student1);
        return userList;
    }

}

(8)測試新增(我這邊測試工具用的是postman,)

    @PostMapping("/user")
    public boolean insert(@RequestBody Student student) {
        System.out.println("開始新增...");
        //如果姓名爲空就手動拋出一個自定義的異常!
        if(student.getSname()==null){
            throw  new BizException("-1","用戶姓名不能爲空!");
        }
        return true;
    }

 

(9)測試put 請求,返回請求的格式不符

全局異常是報的是請求的參數格式不符,而後臺內部報的是空指針,全局異常優先處理。

 

(10) 測試一下刪除方法  

  @DeleteMapping("/user")
    public boolean delete(@RequestBody Student student)  {
        System.out.println("開始刪除...");
        //這裏故意造成一個異常,並且不進行處理
        Integer.parseInt("abc123");
        return true;
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章