热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

Springboot集中异常处理方法实例

这篇文章主要介绍了Springboot集中异常处理方法实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

集中异常处理

以下代码可在github获取:https://github.com/Syske/learning-dome-code/tree/master/springboot-learning

方式一:ExceptionHandle

定义自己的异常类型,根据不同类型做不同处理,比如我定义的MyException:

public class MyException extends RuntimeException {
  public MyException(String msg) {
    super(msg);
  }
}

然后通过MyExceptionHandle处理该异常,需要注意的是异常不能在filter中抛出,抛出也没法捕获

	
@RestControllerAdvice
public class MyExceptionHandle {
 
  @ExceptionHandler(MyException.class)
  public Result exceptionHandle(MyException e) {
    return Result.getFailed( "system error:MyException" + e.getMessage());
  }
}

在controller、service以及拦截器的预处理方法中都可以完美捕获,这里特殊说下拦截器:

	
public class MyInterceptor implements HandlerInterceptor {
  @Override
  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    //throw new MyException("拦截器错误:MyInterceptor");
    // 这里的异常会完美捕获,并返回
    return true;
  }
 
  @Override
  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    throw new MyException("拦截器错误:MyInterceptor");
    /** 能捕获异常信息并返回给客户端,但并不会覆盖已经请求成功的返回结果,但会包含在返回结果中,比如我的返回结果:
     {"code":1,"success":true,"msg":"请求成功","result":true}{"code":0,"success":false,"msg":"system error:MyException拦截器错误:MyInterceptor","result":null}
     */
  }
 
  @Override
  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    throw new MyException("拦截器错误:MyInterceptor");
    // 这里抛出的异常并不能被捕获,会直接在后台抛出,相当于回掉函数,请求结果已经返回
  }
}

上面的解释已经够清楚了,就不做过多说明了。刚刚我们说了,上面的这种方式,对于filter是不起作用的,下面我们说的这种方式,主要就是针对filter来说的

方式二:ExceptionFilter

定义一个controller,请求路径可以自己指定,比如/error/exthrow:

	
@Controller
public class ExceptionController {
 
  @RequestMapping("/error/exthrow")
  public void rethrow(HttpServletRequest request) throws Exception {
    throw ((Exception) request.getAttribute("filter.error"));
  }
}

再定义一个异常拦截器,在需要抛出异常的拦截器中直接抛出异常,然后在异常拦截器中try-catch,发生异常时直接转发至前面定义的异常controller,这里需要注意的是,如果你的filter是实现Filter或者继承OncePerRequestFilter,那你不需要任何处理,直接request.setAttribute("filter.error", e)就可以了。

由于我把自己的filter交给shiro管理,而且是继承BasicHttpAuthenticationFilter的,不知到什么原因,直接catch到的异常类型是ServletException,为了拿到真正的异常信息,我需要通过getCause()方法获取filter中抛出的异常。因为controller抛出的异常最后还是会交给我们定义的MyExceptionHandle去处理,如果获取到的异常不是我们自定义的异常或者他的子类的话,就会返回500错误(在这个示例前,我以为所有的filter都是这样的,后来实践后发现并不是这样😂)。

@Component
public class ExceptionFilter implements Filter {
 
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    try {
      chain.doFilter(request, response);
    } catch (Exception e) {      
      request.setAttribute("filter.error", e);
      //将异常分发到/error/exthrow控制器
      request.getRequestDispatcher("/error/exthrow").forward(request, response);
    }
  }
 
  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
  }
 
  @Override
  public void destroy() {
 
  }
}

我的filter:

实现Filter接口:

	
public class MyFilter implements Filter {
  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    throw new MyException("MyFilter过滤器抛出异常");
    //filterChain.doFilter(servletRequest, servletResponse);
  }
  
}

filter2继承OncePerRequestFilter:

	
public class MyFilter2 extends OncePerRequestFilter {
  @Override
  protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
    throw new MyException("MyFilter2异常");
  }
}

filter配置类:

	
 @Bean
  public FilterRegistrationBean myFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter());
    registration.setName("myFilter");
    registration.addUrlPatterns("/*");
    //此处尽量小,要比其他Filter靠前
    registration.setOrder(1);
    return registration;
  }
 
  @Bean
  public FilterRegistrationBean myFilter2Registration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new MyFilter2());
    registration.setName("myFilter2");
    registration.addUrlPatterns("/*");
    //此处尽量小,要比其他Filter靠前
    registration.setOrder(2);
    return registration;
  }
 
  /**
   * 配置拦截器
   * @return
   */
  @Bean
  public FilterRegistrationBean exceptionFilterRegistration() {
    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new ExceptionFilter());
    registration.setName("exceptionFilter");
    //此处尽量小,要比其他Filter靠前
    registration.setOrder(-1);
    return registration;
  }

方式三:BasicErrorController

其实spring boot原生提供了异常集中处理,我们经常会看到:

但是这种方式不够友好,而且大部分情况不满足我们的需求,我们可以通过继承这个controller,然后重写error方法或者errorHtml方法,或者两个都重写,区别是errorHtml是处理请求头为text/html的请求发生的异常,而error是除了这个之外的其他异常。

下面是我定义的baseController,error部分返回的结果是空,还需要进一步的研究:

	
@RestController
@RequestMapping(value = "error")
public class MyBaseErrorController extends BasicErrorController {
 
  public MyBaseErrorController(ErrorAttributes errorAttributes) {
    super(errorAttributes, new ErrorProperties());
  }
 
  @Override
  @RequestMapping(produces = {MediaType.ALL_VALUE})
  public ResponseEntity> error(HttpServletRequest request) {
 
    return new ResponseEntity>(Result.failedResultMap(000, "未知错误"), HttpStatus.OK);
  }
 
  @RequestMapping(
      produces = {"text/html"}
  )
  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    HttpStatus status = this.getStatus(request);
    response.setStatus(status.value());
    return null;
  }
 
}

最后,对于以上问题我最后的解决方法是继承ErrorController,然后定义errorHtml和error,更重要的是@RequestMapping注解,然后在方法中response写入返回值,这种方式不够优雅:

@RestController
@RequestMapping(value = "error")
public class MyBaseErrorController implements ErrorController {
  private static final String path_default = "/error";
  @Autowired
  private ErrorAttributes errorAttributes;
  @RequestMapping(produces = {MediaType.ALL_VALUE})
  public void error(HttpServletRequest request, HttpServletResponse response) {
    setJsonError(response);
  }
  @RequestMapping(
      produces = {"text/html"}
  )
  public void errorHtml(HttpServletRequest request, HttpServletResponse response) {
    setJsonError(response);
  }
 
  @Override
  public String getErrorPath() {
    return path_default;
  }
 
  private void setJsonError(HttpServletResponse response) {
    PrintWriter writer = null;
    try {
      response.setStatus(200);
      response.setHeader("Content-type", "text/html;charset=UTF-8");
      response.setCharacterEncoding("UTF-8");
      writer = response.getWriter();
      writer.write(JSON.toJSONString(Result.getFailed("未知错误", null)));
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (writer != null) {
        writer.close();
      }
    }
  }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍如何创建一个专门用于处理浮点数的JSON处理器,并将其注册到JSON配置器中,以实现对浮点数的精确控制和格式化输出。 ... [详细]
  • 微信小程序实现拍照与图片上传功能
    本文介绍如何在微信小程序中实现用户通过拍照或从相册选择图片,并将图片上传至服务器的功能,包括调用相关API和处理上传响应。 ... [详细]
  • Node.js 中 GET 和 POST 请求的数据处理
    本文详细介绍了如何在 Node.js 中使用 GET 和 POST 方法来处理客户端发送的数据。通过示例代码展示了如何解析 URL 参数和表单数据,并提供了完整的实现步骤。 ... [详细]
  • 利用 Jest 和 Supertest 实现接口测试的全面指南
    本文深入探讨了如何使用 Jest 和 Supertest 进行接口测试,通过实际案例详细解析了测试环境的搭建、测试用例的编写以及异步测试的处理方法。 ... [详细]
  • 深入探讨ASP.NET中的OAuth、JWT与OpenID Connect
    本文作为前文关于OAuth2.0和使用.NET实现OAuth身份验证的补充,详细阐述了OAuth与JWT及OpenID Connect之间的关系和差异,旨在提供更全面的理解。 ... [详细]
  • AJAX技术允许网页在不重新加载整个页面的情况下进行异步更新,通过向服务器发送请求并接收JSON格式的数据,实现局部内容的动态刷新。 ... [详细]
  • 深入解析 Golang 中的 Cache::remember 方法实现
    本文详细探讨了如何在 Golang 中实现类似于 Laravel 的 Cache::remember 方法,通过具体的代码示例和深入的分析,帮助读者更好地理解和应用这一技术。 ... [详细]
  • Gradle复合构建详解
    自Gradle 3.3起,复合构建功能得以实现,这是一种能够整合其他独立构建的高级构建模式。本文将详细介绍复合构建与多项目构建的区别,以及如何在实际项目中应用复合构建。 ... [详细]
  • 通过特定网站,用户可以直接在网页上运行Matlab代码,无需下载或安装任何软件。访问以下链接了解更多:https://www.mathworks.cn/products/matlab-online.html ... [详细]
  • jQuery中 $(selector).each() 和 $.each() 的区别与应用
    在JavaScript开发中,jQuery库提供了两种遍历方法:$(selector).each() 和 $.each()。本文将详细探讨这两种方法的不同之处及其应用场景。 ... [详细]
  • 本文详细介绍了C#中的基本选择结构(如if、if-else、if-else-if及嵌套if)、switch结构、数组与循环控制结构(包括while、do-while、for和foreach循环)以及跳转语句(break和continue)。此外,还简要探讨了二重循环的应用和冒泡排序算法。 ... [详细]
  • 本文详细记录了利用HTML5和CSS3进行企业网站开发的过程,包括技术要点、实践技巧以及常见问题解决方法。 ... [详细]
  • 微信PC版1.2更新:支持自定义公众号菜单与多项新功能
    微信桌面应用程序最新版本1.2 for Windows现已发布,新增了保存聊天记录、使用公众号自定义菜单、同步手机表情等多项实用功能。 ... [详细]
  • 本文详细介绍了ASP.NET缓存的基本概念和使用方法,包括输出缓存、数据缓存及其高级特性,如缓存依赖、自定义缓存和缓存配置文件等。通过合理利用这些缓存技术,可以显著提升Web应用程序的性能。 ... [详细]
  • 本文详细探讨了JavaScript中的闭包与柯里化技术,这两者是函数式编程的重要组成部分,对提升代码的灵活性和可维护性具有重要作用。 ... [详细]
author-avatar
黑旦儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有