SpringBoot全局异常处理(Controller、interceptor、filter)

1、Controller层的全局异常处理

通过对Controller添加注释@ControllerAdvice可以实现Controller层的全局异常处理

统一的拦截异常处理类AppExceptionHandler

@Slf4j
@RestControllerAdvice
public class AppExceptionHandler {
    @Autowired
    protected MessageSource messageSource;

    @ExceptionHandler({UserLoginException.class})
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Result handleUserLoginException(final UserLoginException userLoginException) {
        log.error("LiveAppExceptionHandler->UserLoginException={}", userLoginException);
        return Result.error(userLoginException.getCode(), userLoginException.getMessage());
    }
    
    @ExceptionHandler({UrlException.class})
    @ResponseStatus(HttpStatus.OK)
    public Result handleChanceException(final UrlException urlException) {
        log.error("【UrlException】:", UrlException);
        return Result.error(urlException.getCode(), urlException.getMessage());
    }

    @ExceptionHandler({Exception.class})
    @ResponseStatus(HttpStatus.OK)
    public Result handleException(final Exception exception) {
        log.error("【exception】:", exception);
        return Result.error(AdminErrorCodeEnum.A0001);
    }

}

Result 统一封装的返回类

@Data
public class Result<T> {
    private int code;
    private T data;
    private String msg;


    public static <T> Result<T> success(T response) {
        Result<T> result = new Result<>();
        result.setCode(0);
        result.setData(response);
        return result;
    }

    public static Result error(int code, String message) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(message);
        return result;
    }

    public static Result error(AppErrorCodeEnum errorCodeEnum) {
        Result result = new Result();
        result.setCode(errorCodeEnum.getCode());
        result.setMsg(errorCodeEnum.getMessage());
        return result;
    }

    public static Result error(AppErrorCodeEnum errorCodeEnum, MessageSource messageSource, Object... params) {
        Result jsonResult = new Result();
        jsonResult.setCode(errorCodeEnum.getCode());
        jsonResult.setMsg(messageSource.getMessage(String.valueOf(errorCodeEnum.getCode()), params, errorCodeEnum.getMessage(), Locale.SIMPLIFIED_CHINESE));
        return jsonResult;
    }
}

2、Interceptor异常统一处理

Interceptor是基于SpringMVC框架的,有时候我们需要拦截器中进行逻辑处理时(比如:tonken登陆验证)需要抛出异常,那么这里抛出的异常是可以被@ControllerAdvice注释的异常处理类捕获的。

InterceptorConfig 配置类,将具体的拦截器类注册到InterceptorConfig 中

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Autowired
    SessionInterceptor userLoginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration interceptorRegistration = registry.addInterceptor(userLoginInterceptor);
        interceptorRegistration.addPathPatterns("/**");
        interceptorRegistration.excludePathPatterns(
                "/login/**"
        );
}

实际的业务拦截器类SessionInterceptor

@Slf4j
@Component
public class SessionInterceptor implements HandlerInterceptor {

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token");
        if (StringUtils.isEmpty(token)){
            log.error("传入token参数值为空={},请先登录",token);
            throw new UserLoginException(AppErrorCodeEnum.A0003.getCode(),AppErrorCodeEnum.A0003.getMessage() );
        }
        //从redis中获取token
        LiveTokenDTO liveTokenDTO = (LiveTokenDTO) redisTemplate.opsForValue().get(token);
        if(ObjectUtil.isNotEmpty(liveTokenDTO)){  
            return true;
        } else {
            log.error("SessionInterceptor->userToken用户会话解析为空");
            throw new UserLoginException(AppErrorCodeEnum.A0003.getCode(),AppErrorCodeEnum.A0003.getMessage() );
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

异常类

@data
public class UserLoginException extends RuntimeException{
    int code;
    String message;

}

枚举类

@Getter
public enum AppErrorCodeEnum {

    A0000(10000, "参数校验失败"),
    A0001(10001, "系统繁忙,请稍后再试"),
    A0002(10002, "登录失败"),
    A0003(10003, "token失效,请重新登录");
    private int code;

    private String message;

    private String subCode;

    private String subMessage;

    AppErrorCodeEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public void setSubCodeAndSubMessage(String subCode, String subMessage) {
        setSubCode(subCode);
        setSubMessage(subMessage);
    }

    private void setSubCode(String subCode) {
        this.subCode = subCode;
    }

    private void setSubMessage(String subMessage) {
        this.subMessage = subMessage;
    }
}

3、Filter异常统一处理

过滤器Filter和基于Servlet框架编写的,抛出的异常是不可以被@ControllerAdvice注释的异常处理类捕获的,常见的异常处理的方法有两种:
(1)通过请求转发到特定的异常处理controller中进行处理

定义一个filter异常处理的Controller

@Slf4j
@RestController
@RequestMapping("/filter")
public class FilterExceptionController {

    @RequestMapping("exception")
    public void handleException() {
        throw new UrlException(AppErrorCodeEnum.A0003.getCode(), AppErrorCodeEnum.A0003.getMessage());

    }
}

请求url路径过滤器

@Slf4j
@Component
public class RequestUrlFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        String path = ((HttpServletRequest) servletRequest).getRequestURI();
        if (!path.contains("/app")) {
//            throw new UrlException(401, "filter内部抛出异常");//AppExceptionHandler 全局异常捕获不到
//转发给filter异常处理的Controller,controller中直接抛出异常,AppExceptionHandler全局异常就可已捕获到了       servletRequest.getRequestDispatcher("/filter/exception").forward(servletRequest,servletResponse);
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

}

(2)、SpringBoot 提供了 ErrorController 这个接口能力;其内置了一个 BasicErrorController 对异常进行统一的处理,当在 Controller 发生异常的时候会自动把请求 forward 到 /error 这个请求 path 下(/error 是 SpringBoot 提供的一个默认的mapping)。BasicErrorController 提供两种返回错误:1、页面返回;2、json 返回。

@RestController
public class UrlErrorController extends BasicErrorController {

    public UrlErrorController() {
        super(new DefaultErrorAttributes(), new ErrorProperties());
    }

    @Override
    @RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
    public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
        Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);
        return new ResponseEntity((String) body.get("message"), status);
    }
}

将filter异常直接抛出

@Slf4j
@Component
public class RequestUrlFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        String path = ((HttpServletRequest) servletRequest).getRequestURI();
        if (!path.contains("/app")) {
			throw new UrlException(401, "filter内部抛出异常");
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }

}

返回:java.lang.RuntimeException: UrlException(code=401, message=filter内部抛出异常)

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 中,可以通过自定义全局处理器(Handler)或拦截器(Interceptor)来处理时间的全局处理。以下是一种常见的实现方式: 1. 创建一个自定义的全局处理器(Handler)类,实现 HandlerInterceptor 接口。 ```java import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TimeInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 在请求处理之前执行,可以进行一些预处理操作 request.setAttribute("startTime", System.currentTimeMillis()); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 请求处理之后进行调用,但是在视图被渲染之前(Controller 方法调用之后) } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 整个请求结束之后被调用,包括视图渲染完成,即在 DispatcherServlet 渲染了对应的视图之后执行 long startTime = (long) request.getAttribute("startTime"); long endTime = System.currentTimeMillis(); long executionTime = endTime - startTime; System.out.println("请求处理时间:" + executionTime + "ms"); } } ``` 2. 注册全局处理器(Handler)或拦截器(Interceptor)。 ```java import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new TimeInterceptor()); } } ``` 通过以上步骤,你可以在全局处理器中记录请求的开始时间和结束时间,并计算出请求处理时间。可以根据自己的需求,在处理器中进行时间日志记录、性能监控等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值