项目构建基础之统一结果和统一异常

参考文章: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.测试一下这个返回结果的类

扫描二维码关注公众号,回复: 11363053 查看本文章

在浏览器上访问 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;
    }

猜你喜欢

转载自blog.csdn.net/tangthh123/article/details/105857252