springboot全局统一异常处理及全局统一返回

1、全局统一返回格式:

项目开发形式为前后端分离,采用Restful接口形式开发,对异常的处理与业务数据统一以json形式返回。接口统一json格式的response返回格式一般定义如下:

{
    "code": 0,
    "msg": "成功",
    "data": {
        "name": "阿饭",
        "age": 16
    }
}

1.1、定义通用的返回类:

package com.fun.common.tools.result;

import com.fun.common.constants.CommonConstants;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;

@Data
public class CustomResponse<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    private int code;
    private String msg;
    private T data;

    public CustomResponse(int code) {
        this.code = code;
        this.msg = CommonConstants.SUCCESS;
    }

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

    public CustomResponse(int code, T data) {
        this.code = code;
        this.data = data;
        this.msg = CommonConstants.SUCCESS;
    }
}

1.2 定义返回值(此处以失败码举例)

package com.fun.common.tools.exception.result;

public enum ErrorResultCode {

    NAME_NOT_NULL(10001, "姓名不能为空"),
    AGE_NOT_NULL(10002, "年龄不能为空")
    NULL_POINT_EXCEPTION(10003,"空指针异常"),
    CUSTOM_EXCEPTION(10004,"具体的自定义异常");

    private int code;
    private String msg;
    
    WorkDataResultCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    
    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

1.3、定义接口返回的数据模型

package com.fun.common.tools.result;

import com.fun.common.tools.exception.result.ErrorResultCode ;

public class Result {

    public static CustomResponse success() {
        return new CustomResponse(0);
    }

    public static CustomResponse fail() {
        return new CustomResponse(-1);
    }

    public static CustomResponse success(Object data) {
        return new CustomResponse(0, data);
    }

    public static CustomResponse fail(String msg) {
        return new CustomResponse(-1, msg);
    }

    public static CustomResponse fail(ErrorResultCode resultCode) {
        return new CustomResponse(resultCode.getCode(), resultCode.getMsg());
    }

    public static CustomResponse fail(int code, String msg) {
        return new CustomResponse(code, msg);
    }
}

1.4、全局统一返回测试类

package com.fun.test.controller;

import com.fun.common.tools.exception.CustomException;
import com.fun.common.tools.exception.result.ErrorResultCode;
import com.fun.common.tools.result.CustomResponse;
import com.fun.common.tools.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * @ClassName: TestController 
 * @Author: fun
 * @Description: 测试类
 * @Date: 2020/4/16 10:24
 * @Version: 1.0.0
 * 可爱之人必有可胖之处
 * ღღ ∧_∧
 * ฅ(՞•🐽•՞)ฅ
 */

@RestController
@Slf4j
@RequestMapping("/test")
public class TestController {
    @PostMapping(value = "/test")
    public CustomResponse test(User user) {
        if (StringUtil.isEmpty(user.getName())) {
            return Result.fail(ErrorResultCode.NAME_NOT_NULL);
        }
        user.setName("阿饭");
        user.setAge(16)
        return Result.success(user);
    }
}

1.5 全局postman测试结果

1.5.1、成功:

当传入{"name": "阿饭", "age": 16},返回结果为:

{
    "code": 0,
    "msg": "成功",
    "data": {
        "name": "阿饭",
        "age": 16
    }
}

1.5.2、失败:

当传入{"name": "", "age": 16},返回结果为:

{
    "code": -1,
    "msg": "姓名不能为空",
    "data": null
}

1.6 引申:ResponseBodyAdvice 返回统一拦截处理

1.6.1ResponseBodyAdvice

在 spring 4.1 新加入的一个接口,在消息体被HttpMessageConverter写入之前允许Controller 中 @ResponseBody修饰的方法或ResponseEntity调整响应中的内容,比如做一些返回处理。

ResponseBodyAdvice接口里一共包含了两个方法

1.6.2、supports

该组件是否支持给定的控制器方法返回类型和选择的{@code HttpMessageConverter}类型

1.6.3、beforeBodyWrite

在选择{@code HttpMessageConverter}之后调用,在调用其写方法之前调用。

1.6.4 、 全局返回统一拦截处理类

@Slf4j
@RestControllerAdvice
public class GlobalDataAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public Object beforeBodyWrite(Object arg0, MethodParameter arg1, MediaType arg2,
                                  Class<? extends HttpMessageConverter<?>> arg3, ServerHttpRequest arg4, ServerHttpResponse arg5) {

        final String returnTypeName = arg1.getParameterType().getName();
        // 可判断方法返回类型,做出相应返回处理。例如:测试类中void的返回类型,获取到以后自动装载成全局统一返回格式。此处可做
        if ("void".equals(returnTypeName)) {
            return ResponseUtils.success();
        }
        if ("com.fun.common.tools.result.CustomResponse".equals(returnTypeName)) {
            return arg0;
        }
        return ResponseUtils.success(arg0);
    }
    @Override
    public boolean supports(MethodParameter arg0, Class<? extends HttpMessageConverter<?>> arg1) {

        final String returnTypeName = arg0.getParameterType().getName();
        // 用于判断是否需要做处理
        return !"com.fun.common.tools.result.CustomResponse".equals(returnTypeName);
    }

2、全局异常处理

2.1、自定义异常

package com.fun.common.tools.exception;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
/**
 * @ClassName: CustomException 
 * @Author: fun
 * @Description:自定义异常,继承RuntimeException ,可根据业务需要添加,此处不做更多扩展
 * @Date: 2020/1/6 10:23
 * @Version: 1.0.0
 * 可爱之人必有可胖之处
 * ღღ ∧_∧
 * ฅ(՞•🐽•՞)ฅ
 */
@Data
public class CustomException extends RuntimeException {
    private int code;
    private String msg;
}

2.2、全局统一异常处理类

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(NullPointerException.class)
    public CustomResponse errorHandler(NullPointerException ex) {
        return ResponseUtils.fail(ErrorResultCode.CUSTOM_EXCEPTION);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(CustomException.class)
    public CustomResponse myErrorHandler(CustomException ex) {
        return ResponseUtils.fail(ex.getCode(), ex.getMsg());
    }
}

2.3、全局统一异常处理测试

2.3.1 全局统一异常处理测试类

package com.fun.test.controller;

import com.fun.common.tools.exception.CustomException;
import com.fun.common.tools.exception.result.ErrorResultCode;
import com.fun.common.tools.result.CustomResponse;
import com.fun.common.tools.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * @ClassName: TestController 
 * @Author: fun
 * @Description: 测试类
 * @Date: 2020/4/16 10:24
 * @Version: 1.0.0
 * 可爱之人必有可胖之处
 * ღღ ∧_∧
 * ฅ(՞•🐽•՞)ฅ
 */

@RestController
@Slf4j
@RequestMapping("/test")
public class TestController {
    @PostMapping(value = "/test")
    public CustomResponse test() {
   		User user = new User();
        if (user == null) {
           throw new NullPointerException();
        }
        user.setName("阿饭");
        user.setAge(16)
        return Result.success(customResponse);
    }
}

2.3.2 postman全局统一异常处理测试结果为:

{
    "code": 10003,
    "msg": "空指针异常",
    "data": null
}

或者上面测试类中,将 throw new NullPointerException();改为抛出自定义异常,那么返回结果为:

{
    "code": 10004,
    "msg": "具体的自定义异常",
    "data": null
}

2.3.3、总结:

全局统一异常处理类根据自身需求去定义需要处理的异常即可。

3、注意:

@RestControllerAdvice注解的类,需要让spring扫描到,不然不会生效。

3.1 全局统一异常处理及全局统一返回不生效问题解决方法:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章