一、背景简介
目前的前后端交互普遍通过 Restful 的形式,而错误处理仅仅通过 HTTP 状态码往往不满足需求;
基于此,我们需要在 HTTP 状态码基础上拓展业务错误码;
本文介绍一种基于 SpringBoot 和 Exception 的实现方案。
二、实验步骤
2.1 生成 SpringBoot 基础项目
SpringBoot 官方提供了初始模板工程,下载地址:https://start.spring.io/
2.2 定义包含错误码的异常
a. 首先,我们需要定义一个 Enum 用于枚举错误码,方便使用和记忆
建立一个 package,如 org.shida.demo.enums
package org.shida.demo.enums;
public enum ApiExceptionCode {
RESOURCE_NOT_FOUND(101, "Resource not found");
private Integer value;
private String desc;
private ApiExceptionCode(Integer value, String desc) {
this.value = value;
this.desc = desc;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
本例中,定义了 RESOURCE_NOT_FOUND ,错误码为 101
b. 然后,定义一个公共异常(父类异常)
建立一个 package,如 org.shida.demo.exception
package org.shida.demo.exception;
public class ApiException extends RuntimeException{
private static final long serialVersionUID = 1L;
private Integer code;
public ApiException(Integer code, String msg) {
super(msg);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
接着,实现真正的异常,ResourceNotFoundException
package org.shida.demo.exception;
import org.shida.demo.enums.ApiExceptionCode;
public class ResourceNotFoundException extends ApiException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String msg) {
super(ApiExceptionCode.RESOURCE_NOT_FOUND.getValue(), msg);
}
}
2.3 定义 Controller 异常 处理公共方法
建立一个 package,org.shida.demo.config
package org.shida.demo.config;
import javax.servlet.http.HttpServletRequest;
import org.shida.demo.exception.ApiException;
import org.shida.demo.exception.ExceptionResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class GlobalExceptionHandler {
private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ExceptionResponse handleException(HttpServletRequest request, Exception ex) {
if (ex instanceof ApiException) {
log.warn(ex.getMessage(), ex);
ApiException apiException = (ApiException) ex;
return ExceptionResponse.create(apiException.getCode(), apiException.getMessage());
} else {
log.error(ex.getMessage(), ex);
return ExceptionResponse.create(HttpStatus.INTERNAL_SERVER_ERROR.value(), ex.getMessage());
}
}
}
这里通过判断 异常是否是 ApiException ,分别构造前端响应方式
其中,ExceptionResponse 定义如下,这也是前端最终的异常展示结构,即 code + message 形式
package org.shida.demo.exception;
public class ExceptionResponse {
private String message;
private Integer code;
public ExceptionResponse(Integer code, String message) {
this.message = message;
this.code = code;
}
public static ExceptionResponse create(Integer code, String message) {
return new ExceptionResponse(code, message);
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
2.4 编写一个简单的 Controller 进行测试
建立一个 package,com.shida.demo.web
package org.shida.demo.web;
import org.shida.demo.exception.ResourceNotFoundException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api")
public class DemoController {
@GetMapping("/demo/{hello}")
public String sayHello(@PathVariable String hello) {
if (hello.equals("exception")) {
throw new ResourceNotFoundException("资源没有找到");
} else {
return hello;
}
}
}
2.5 启动程序,通过浏览器访问 http://localhost:8080/api/demo/hello
访问 http://localhost:8080/api/demo/exception ,模拟异常发生
客户端,首先通过 HTTP 状态码判断是否出现了错误,
如果出现了错误,进一步可以通过提取 error data 中的 code 判断错误类型,同时提取 message
三、实验代码下载地址
https://pan.baidu.com/s/1bxSSKfOiJM8hOcUJ09SIxA