springboot-统一异常

  1. 模拟异常

不做全局统一异常时,当项目中出现异常,会报500.

如:

@PostMapping(value="/testerror")
@ApiOperation(value = "获取用户信息")
public Result.Base testerror(HttpServletRequest request){
    Integer inte = 1/0;
    return new Result.Success<>(inte);
}

访问接口时报500错误:

springboot-统一异常_第1张图片

2.处理异常

利用@ControllerAdvice@ExceptionHandler定义一个统一异常处理类

  • @ControllerAdvice:控制器增强,使@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法。
  • @ExceptionHandler:异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法

因此只需要添加如下类即可:

如果是自定义异常,可单独再配。

 

package com.wps.education.exception;

import com.wps.education.LoginInterceptor.LoginInterceptor;
import com.wps.education.common.response.Result;
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;

import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result.Base jsonErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("{} | {}", req.getRequestURL().toString(), e.getMessage());
        e.printStackTrace();
        return new Result.ErrorData<>(e.getMessage());
    }
}

接口返回:

后台输出:

3.项目级处理方式

上面的例子可以全局处理异常,并返回错误内容。

但在项目中使用时,不能给用户显示500,也不能显示这些错误内容。而是定义一套错误码,后台统一返回格式,前端处理接口中的错误码,给出相应的业务处理,或更合理的提示信息。

  1. 定义errorcode类:

import org.apache.http.HttpStatus;
/**
 * 定义错误类
 */
public abstract class ErrorCode {

    /**
     3.1 code:0表示成功, -1表示未知错误
     3.1 系统集合(1-99):
     3.4 业务相关code集合 (100-399):
     */

/** 成功 */
public static final Integer SUCCESS = 0;
/** 未知 */
public static final Integer UNKNOWN = -1;

/** 未知错误 */
public static final Result.Error ERROR_UNKNOWN = new Result.Error(UNKNOWN, "unknown error", HttpStatus.SC_INTERNAL_SERVER_ERROR);

/** system error code, 1-99 */
/** 参数错误,不正确的参数 */
public static final Result.Error ARGUMENT_INVALID = new Result.Error(1, "parameter is invalid", HttpStatus.SC_BAD_REQUEST);
/** 没有Token */
public static final Result.Error NO_TOKEN_ERROR = new Result.Error(2, "no token error", HttpStatus.SC_OK);
/** 没找到对应的对象 */
public static final Result.Error NO_SUCH_ELEMENT_ERROR = new Result.Error(3, "no such element error", HttpStatus.SC_OK);
/** 没有足够的权限 */
public static final Result.Error NO_RIGHT_ERROR = new Result.Error(4, "current user have no right do this", HttpStatus.SC_OK);
/** Token过期 */
public static final Result.Error TOKEN_EXPIRED_ERROR = new Result.Error(5, "token expired", HttpStatus.SC_OK);

/** 用户相关code集合(100-399)*/
/** 登陆参数不正确 */
public static final Result.Error LOGIN_PARAM_ERROR = new Result.Error(100, "login param error, or login expire", HttpStatus.SC_BAD_REQUEST);
/** 创建用户失败*/
public static final Result.Error INSERT_USER_ERROR = new Result.Error(161,"insert user error", HttpStatus.SC_OK);
}

 

2.定义接口返回格式类Result

package com.gary.exception;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
import org.apache.http.HttpStatus;

/**
 * 返回客户端结果类
 */
public class Result extends ErrorCode {

    @Getter
    @Setter
    public static class Base {
        private Integer code;

        public Base(Integer code) {
            this.code = code;
        }

        void check() {
            if (code == null)
                throw new IllegalArgumentException("code can not be null");
        }
    }

    @Getter
    @Setter
    @JsonInclude(value = JsonInclude.Include.NON_NULL)
    public static class Success<T> extends Base {
        private T data;
        private Integer status;

        public Success(T data) {
            super(SUCCESS);
            this.data = data;
            check();
        }

        public Success() {
            super(SUCCESS);
            this.data = (T) "success";

            check();
        }

        void check() {
            super.check();
        }
    }

    @JsonInclude(value = JsonInclude.Include.NON_EMPTY)
    @Getter
    @Setter
    public static class Error extends Base {
        private String result;
        private Integer httpStatus;

        public Error(Integer code, String result, Integer status) {
            super(code);
            this.result = result;
            this.httpStatus = status;
            check();
        }

        void check() {
            super.check();
            if (result == null || httpStatus == 0)
                throw new IllegalArgumentException("result can not be null and http status cannot be zero");
        }

    }

    @Getter
    @Setter
    @JsonInclude(value = JsonInclude.Include.NON_EMPTY)
    public static class ErrorData<T> extends Base {
        private T data;

        public ErrorData(T data) {
            super(HttpStatus.SC_INTERNAL_SERVER_ERROR);
            this.data = data;
        }

        void check() {
            super.check();
        }
    }

}

 

3.添加自定义异常类

派生自运行时异常,将Result的Error类作为成员:

package com.gary.exception;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class CustomException extends RuntimeException{
    private static final long serialVersionID = 1L;

    private Result.Error error;

    public CustomException() {
        super();
    }

    public CustomException(String message) {
        super(message);
    }

    public CustomException(String message, Throwable cause) {
        super(message, cause);
    }

    public CustomException(Throwable cause) {
        super(cause);
    }

    public CustomException(Result.Error error) {
        super(error.getResult());
        checkErrorMessage(error);
        this.error = error;
    }

    public CustomException(Result.Error error, String message) {
        super(message);
        checkErrorMessage(error);
        this.error = error;
    }

    public CustomException(Result.Error error, String message, Throwable cause) {
        super(message, cause);
        checkErrorMessage(error);
        this.error = error;
    }

    public CustomException(Result.Error error, Throwable cause) {
        super(cause);
        checkErrorMessage(error);
        this.error = error;
    }

    public void checkErrorMessage(Result.Error error){
        if(error.getCode()!=null && error.getCode()==0){
            throw new RuntimeException("errorMessage code must not be 0");
        }
    }
}

 

4.完善统一异常处理

package com.gary.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result.Base jsonErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        logger.error("{} | {}", req.getRequestURL().toString(), e.getMessage());

        e.printStackTrace();
        return new Result.ErrorData<>(e.getMessage());
    }

    @ExceptionHandler(CustomException.class)
    @ResponseBody
    public ResponseEntity handleCustomException(CustomException e) {
        Result.Error error = e.getError();
        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
        if (error != null && error.getHttpStatus() != null) {
            httpStatus = getHttpStatus(e.getError().getHttpStatus());
        } else {
            logger.error("unknown error ", e);
            error = ErrorCode.ERROR_UNKNOWN;
        }
        return handleResponse(error, httpStatus);
    }

    private ResponseEntity handleResponse(Result.Error error, HttpStatus status) {
        return handleResponse(error, null, status);
    }

    private ResponseEntity handleResponse(Result.Error error, HttpHeaders headers, HttpStatus status) {
        return new ResponseEntity<>(error, headers, status);
    }

    private HttpStatus getHttpStatus(Integer statusCode){
        HttpStatus httpStatus=HttpStatus.INTERNAL_SERVER_ERROR;
        try {
            httpStatus=HttpStatus.valueOf(statusCode);
        } catch (IllegalArgumentException e) {
            logger.error("get HttpStatus error",e);

        }
        return httpStatus;
    }
}

 

5.使用

这样的好处是正常业务和有异常的情况,接口返回前端的结构一样,前端可统一处理返回格式,获取code进行判断处理,是0则正常,其他值根据业务作不同处理。

正常业务返回:

@GetMapping("/hello")
public Result.Base hello(){
    System.out.println("/hello");
    return new Result.Success<>("hello world");
}

未处理的异常:

@GetMapping("/testerror")
public Result.Base error(){
    System.out.println("/error");
    Integer inte = 1/0;
    return new Result.Success<>("hello world");
}

默认code为500,内容为错误内容:

自定义异常:

定义错误值:

public static final Result.Error NUMBER_ERROR = new Result.Error(300,"数字不正确", HttpStatus.SC_OK);

Controller中直接返回错误

@GetMapping("/testerror")
public Result.Base error(){
    System.out.println("/error");
    try{
        Integer inte = 1/0;
    }catch (Exception e){
        return Result.NUMBER_ERROR;
    }

    return new Result.Success<>("hello world");
}

或者主动抛自定义异常:

    @GetMapping("/testerror")
    public Result.Base error(){
        System.out.println("/error");
        try{
            Integer inte = 1/0;
        }catch (Exception e){
//            return Result.NUMBER_ERROR;
            throw new CustomException(Result.NUMBER_ERROR);
        }

        return new Result.Success<>("hello world");
    }

效果一样:

两者的差别是:前者只能在conroller中使用,因为是直接返回错误。后者可以在任意地方,如service层dao层。

6.说明

其中用到了httpstatus,和lombok,因此需要添加这两个依赖包:

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <version>1.16.20version>
dependency>
<dependency>
    <groupId>org.apache.httpcomponentsgroupId>
    <artifactId>httpclientartifactId>
dependency>

你可能感兴趣的:(java_springboot,springboot,统一异常)