1. 固定格式
1.1 由于返回的格式比较乱,所以先对返回格式进行统一,返回消息格式参照如下模板:
{
"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 工具类中的对应方法,如下图:
当发生错误时,调用ResultUtil中的error方法;当执行“添加一个学生”成功时,则调用 ResultUtil 的success方法。
那么,ResultUtil工具类中的success和error具体是怎么定义的的?其代码如下:
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);
}
}
不难看出,这里使用了自定义的异常类StudentException
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 ”就包含了这两个信息。
2.2 code和message设成枚举,统一管理
原来,为了方便对错误码(状态码)进行统一管理,使得错误码和其相应的返回消息能够一一对应,也方便程序员查看和修改,这里把错误码和返回消息设置成一个枚举类:
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中了