前言

在项目中我们会经常碰到异常处理的问题,比如用户在发送请求到我们的后端时如果请求参数或路径出现错误,在没做异常处理的情况下服务端会直接把一大串错误信息返回给用户。这种情况并不少见,用户获得这些信息并没有什么用处,我们后端可以对所有这种异常进行统一的处理然后提取重点统一返回封装后的数据。Spring官方文档给我们提供一种解决方案,使用@ControllerAdvice@ExceptionHandler注解可以将组件注册为全局异常处理类和方法。@ControllerAdvice实际上就是一个增强的@Controller,使用该controller可以实现三个方面的功能:全局异常处理、全局数据绑定、全局数据预处理。该博文将讲解其全局异常处理的使用方法。

异常处理类的注册与使用

  1. 首先要建一个处理类,使用@ControllerAdvice将其注入到IoC容器中成为全局异常处理类
    //全局异常处理
    @ControllerAdvice
    public class WebMvcExceptionHandler {

    }
  2. 使用@ExceptionHandler在方法上注明要进行捕捉的异常(Exception为大部分异常的超类)
    @ControllerAdvice
    public class WebMvcExceptionHandler {
    //其他情况:服务器内部异常
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public ResponseResult serverHandler(Exception e){
    System.out.println("服务器异常:"+e.getMessage());
    return ResponseResult.internal_server_error();
    }
    }
  3. 对出现异常的情况进行返回集合的处理。通常封装类有响应编码、响应信息、响应数据这三个基本要素,我们可以对其进行封装。

响应编码的枚举类

public enum HttpEnum {
/**
* 请求处理正常
*/
OK(200, "请求成功"),

/**
* 请求成功并且服务器创建了新的资源。
*/
CREATED(201, "创建成功"),
/**
* 用户发出的请求有错误,服务器没有进行新建或修改数据的操作
*/
INVALID_REQUEST(400, "非法请求"),
/**
* 访问内容不存在
*/
NOTFOUND(404, "访问内容不存在"),
/**
* 表示用户没有权限(令牌、用户名、密码错误)
*/
UNAUTHORIZED(401,"抱歉,您没有权限"),
/**
* 表示用户得到授权(与401错误相对),但是访问是被禁止的
*/
FORBIDDEN(403,"禁止访问"),
/**
* 系统内部错误
*/
INTERNAL_SERVER_ERROR(500, "系统内部错误");

private String msg;

private int code;

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

//获取code
public int code(){
return code;
}
//获取msg
public String msg(){
return msg;
}
}

返回数据封装集合

public class ResponseResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
//响应编码
private int code;
//提示信息
private String msg;
//响应数据
private T data;

//根据属性构建ResponseResult
private static <T> ResponseResult<T> build( int code,String msg,T data) {
return new ResponseResult<T>().setCode(code).setMsg(msg).setData(data);
}


//构建一些常用的
/**
* 请求正常
*/
public static <T> ResponseResult<T> ok(){
return build(HttpEnum.OK.code(),HttpEnum.OK.msg(),null);
}
public static <T> ResponseResult<T> ok(T data){
return build(HttpEnum.OK.code(),HttpEnum.OK.msg(),data);
}


/**
* 创建成功
*/
public static <T> ResponseResult<T> created(){
return build(HttpEnum.CREATED.code(),HttpEnum.CREATED.msg(),null);
}

/**
* 非法请求
*/
public static <T> ResponseResult<T> invalid_request(){
return build(HttpEnum.INVALID_REQUEST.code(),HttpEnum.INVALID_REQUEST.msg(),null);
}

/**
* 访问内容不存在
*/
public static <T> ResponseResult<T> notFound(){
return build(HttpEnum.NOTFOUND.code(),HttpEnum.NOTFOUND.msg(),null);
}

/**
* 没有权限
*/
public static <T> ResponseResult<T> unauthorized(){
return build(HttpEnum.UNAUTHORIZED.code(),HttpEnum.UNAUTHORIZED.msg(),null);
}
public static <T> ResponseResult<T> unauthorized(T data){
return build(HttpEnum.UNAUTHORIZED.code(),HttpEnum.UNAUTHORIZED.msg(),data);
}


/**
* 禁止访问
*/
public static <T> ResponseResult<T> forbidden(){
return build(HttpEnum.FORBIDDEN.code(),HttpEnum.FORBIDDEN.msg(),null);
}


/**
* 系统内部错误
*/
public static <T> ResponseResult<T> internal_server_error(){
return build(HttpEnum.INTERNAL_SERVER_ERROR.code(),HttpEnum.INTERNAL_SERVER_ERROR.msg(),null);
}

//getter和setter
public int getCode() {
return code;
}
//注意返回值类型
public ResponseResult<T> setCode(int code) {
this.code = code;
return this;
}

public String getMsg() {
return msg;
}
//注意返回值类型
public ResponseResult<T> setMsg(String msg) {
this.msg = msg;
return this;
}

public T getData() {
return data;
}
//注意返回值类型
public ResponseResult<T> setData(T data) {
this.data = data;
return this;
}
}

用户请求常见异常

  1. 请求方式错误。在后端为Restful风格的接口中,通常请求的方式都会有对应的规定。处理这种情况用的是HttpRequestMethodNotSupportedException
  2. 请求参数错误。如后端要求前端使用POST请求来传递表单信息,若表单数据不完整或不存在,则这种情况后端会解析为数据解析异常,使用HttpMessageNotReadableException
  3. JSON格式错误。这种情况是我使用fastjson解析json对象时遇到的问题。如前端传递进来的json对象K-V键值对不完整
    {
    "keyword":"关键字",
    “name”:
    }
    这种情况使用JSONException(只有使用阿里的fastjson才有,若不是使用fastjson可无视)
  4. 资源不存在。这种应该是最常见的,用户访问的路径为非法路径时会报出该异常。使用NoHandlerFoundException。注:正常情况下使用注解方式处理全局异常是不能处理404异常的,要在配置文件application.yml中设置
    spring:
    mvc:
    throw-exception-if-no-handler-found: true
    resources:
    add-mappings: false
  5. 默认异常。异常有这么多不可能全部都列出来,但我们可以使用他们的超类Exception