参考文章:https://mp.weixin.qq.com/s/qwzXJ9WDeTAsSsO2297CYQ
https://www.cnblogs.com/xuwujing/p/10933082.html
目录
(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 方法收集到一个类型,并作用到所有控制器上。
该类的设计思路:
- 使用@ExceptionHandler 注解捕获指定或自定义的异常;
- 使用@ControllerAdvice 集成@ExceptionHandler 的方法到一个类中;
- 必须定义一个通用的异常捕获方法,遍于捕获未定义的异常信息;
- 自定义一个异常类,捕获针对项目或业务的异常;
- 异常的对象信息补充到统一结果枚举中;
(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;
}