为什么要进行结果封装

为了让前端能够拿到统一格式的返回结果,后端在返回时,应该用统一的格式进行封装。

如何进行结果封装

  1. 定义返回结果信息的枚举类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public enum ResultInfo {
    SUCCESS(0, "Success"),
    INCORRECT_USERNAME_OR_PASSWORD(101, "Incorrect username or password");

    private final int code;
    private final String msg;

    private ResultInfo(int code, String msg) {
    this.code = code;
    this.msg = msg;
    }

    public int getCode() {
    return this.code;
    }

    public String getMsg() {
    return this.msg;
    }
    }

    状态码和信息可以自定义。

  2. 定义返回结果的工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    @Data
    public class ReturnResult {

    private int code;
    private String msg;
    private Object data;

    /**
    * 构造结果,不包含data
    *
    * @param resultInfo 结果信息
    */
    public ReturnResult(ResultInfo resultInfo) {
    this.code = resultInfo.getCode();
    this.msg = resultInfo.getMsg();
    this.data = null;
    }

    /**
    * 构造结果,包含data
    *
    * @param resultInfo 结果信息
    * @param data 要返回的数据
    */
    public ReturnResult(ResultInfo resultInfo, Object data) {
    this.code = resultInfo.getCode();
    this.msg = resultInfo.getMsg();
    this.data = data;
    }

    /**
    * 返回成功状态码及信息
    *
    * @return ReturnResult
    */
    public static ReturnResult onSuccess() {
    return new ReturnResult(SUCCESS);
    }

    /**
    * 返回成功状态码、信息、数据
    *
    * @return ReturnResult
    */
    public static ReturnResult onSuccess(Object data) {
    return new ReturnResult(SUCCESS, data);
    }

    /**
    * 返回失败状态码、信息
    *
    * @return ReturnResult
    */
    public static ReturnResult onFail(ResultInfo resultInfo) {
    return new ReturnResult(resultInfo);
    }

    /**
    * 返回失败状态码、信息、数据
    *
    * @return ReturnResult
    */
    public static ReturnResult onFail(ResultInfo resultInfo, Object data) {
    return new ReturnResult(resultInfo, data);
    }
    }
  3. 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @RestController
    @RequestMapping(produces = "application/json;charset=UTF-8")
    public class LoginController {

    @GetMapping("/login")
    public ReturnResult login(String username, String password) {
    // 省略验证登录代码
    if (...) {
    return ReturnResult.onSuccess();
    } else {
    return ReturnResult.onFail(ResultInfo.INCORRECT_USERNAME_OR_PASSWORD);
    }
    }
    }
  4. 结果展示

全局异常处理

当服务端出现异常时,为了使前端收到统一格式的响应,我们应该进行全局异常捕获处理成统一的格式返回。

  1. 自定义异常类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class MyException extends RuntimeException {

private int code;
private String msg;

public MyException(ResultInfo resultInfo) {
this.code = resultInfo.getCode();
this.msg = resultInfo.getMsg();
}

public MyException(int code, String msg) {
this.code = code;
this.msg = msg;
}

public int getCode() {
return code;
}

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

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}
}
  1. 使用@ControllerAdvice注解捕获异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Log4j2
@ControllerAdvice
public class ExceptionHandle {

@ExceptionHandler(value = NotLoginException.class)
@ResponseBody
public ReturnResult notLoginHandler(HttpServletRequest req, NotLoginException e) {
log.info(e.getMessage());
// 封装成结果返回一致的格式
return ReturnResult.onFail(ResultInfo.NOT_LOGIN);
}

@ExceptionHandler(value = NotRoleException.class)
@ResponseBody
public ReturnResult notLoginHandler(HttpServletRequest req, NotRoleException e) {
log.info(e.getMessage());
return ReturnResult.onFail(ResultInfo.NOT_ROLE);
}

@ExceptionHandler(value = MyException.class)
@ResponseBody
public ReturnResult myExceptionHandler(HttpServletRequest req, MyException e) {
log.info(e.getMsg());
return ReturnResult.onFail(e.getCode(), e.getMsg(), null);
}

@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public ReturnResult nullPointerExceptionHandler(HttpServletRequest req, NullPointerException e) {
log.error("{}: {}", e.getClass().getName(), e.getMessage());
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put("errmsg", e.getClass().getName());
return ReturnResult.onFail(ResultInfo.NULL_POINTER_EXCEPTION, dataMap);
}

@ExceptionHandler(value = Exception.class)
@ResponseBody
public ReturnResult exceptionHandler(HttpServletRequest req, Exception e) {
log.error("{}: {}", e.getClass().getName(), e.getMessage());
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put("errmsg", e.getClass().getName());
return ReturnResult.onFail(ResultInfo.UNKNOWN_ERROR, dataMap);
}
}
  1. 使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping(produces = "application/json;charset=UTF-8")
public class LoginController {

@GetMapping("/login")
public ReturnResult login(String username, String password) {
// 省略验证登录代码
if (...) {
return ReturnResult.onSuccess();
} else {
throw new MyException(ResultInfo.INCORRECT_USERNAME_OR_PASSWORD);
}
}
}