yeskery

SpringBoot覆盖默认的错误处理方式

覆盖默认的错误处理方式

默认错误处理机制的响应内容格式不一定是你相中的。理由可能如下:

  1. Whitelabel Error Page 页面的样式太单调,用户体验不好。

  2. Json 格式的结果字符串不统一,与你配合的前端人员更希望统一格式,好做统一的显示处理。比如与前端人员配合时统一指定响应结果格式为 {status:true,msg:”xxx”,data:{xxx}},但 Spring Boot 的 Json 格式是 {status:500,message:”error occur”,path:”/exception”……}

那么你可能更期望可以修改默认的处理方式,改变响应内容格式。Spring Boot 开发指南上给出了几种方法。

  1. 自定义一个bean,实现 ErrorController 接口,那么默认的错误处理机制将不再生效。

  2. 自定义一个 bean,继承 BasicErrorController 类,使用一部分现成的功能,自己也可以添加新的public方法,使用 @RequestMapping 及其 produces 属性指定新的地址映射。

  3. 自定义一个 ErrorAttribute 类型的 bean,那么还是默认的两种响应方式,只不过改变了内容项而已。

  4. 继承 AbstractErrorController。

BasicErrorController源码

SpringBoot 在页面 发生异常的时候会自动把请求转到 /error,SpringBoot 内置了一个 BasicErrorController 对异常进行统一的处理,当然也可以自定义这个路径。

  1. server:
  2. port: 8080
  3. error:
  4. path: /custom/error

BasicErrorController 提供两种返回错误一种是页面返回、当你是页面请求的时候就会返回页面,另外一种是 json 请求的时候就会返回 json 错误:

  1. @RequestMapping(produces = "text/html")
  2. public ModelAndView errorHtml(HttpServletRequest request,
  3. HttpServletResponse response) {
  4. HttpStatus status = getStatus(request);
  5. Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
  6. request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
  7. response.setStatus(status.value());
  8. ModelAndView modelAndView = resolveErrorView(request, response, status, model);
  9. return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
  10. }
  11. @RequestMapping
  12. @ResponseBody
  13. public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
  14. Map<String, Object> body = getErrorAttributes(request,
  15. isIncludeStackTrace(request, MediaType.ALL));
  16. HttpStatus status = getStatus(request);
  17. return new ResponseEntity<Map<String, Object>>(body, status);
  18. }

分别会有如下两种返回

spring_boot_error_type

自定义一个bean,继承BasicErrorController类

BasicErrorController,这个是 SpringBoot 的默认错误处理,也是一种全局处理方式。咱们可以模仿这种处理方式自定义自己的全局错误处理 下面定义了一个自己的 BasicErrorController,可以根据自己的需求自定义 errorHtml() 和 error() 的返回值。

SpringBoot提供了一种特殊的Bean定义方式,可以让我们容易的覆盖已经定义好的Controller,原生的BasicErrorController是定义在ErrorMvcAutoConfiguration中的 具体代码如下:

  1. @Bean
  2. @ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)
  3. public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) {
  4. return new BasicErrorController(errorAttributes, this.serverProperties.getError(),
  5. this.errorViewResolvers);
  6. }

可以看到这个注解 @ConditionalOnMissingBean 意思就是定义这个 bean 当 ErrorController.class 这个没有定义的时候, 意思就是说只要我们在代码里面定义了自己的ErrorController.class 时,这段代码就不生效了。

自定义 MyErrorController:

  1. package com.lf.exception;
  2. import org.springframework.boot.autoconfigure.web.BasicErrorController;
  3. import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
  4. import org.springframework.boot.autoconfigure.web.ServerProperties;
  5. import org.springframework.http.HttpStatus;
  6. import org.springframework.http.MediaType;
  7. import org.springframework.http.ResponseEntity;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.stereotype.Controller;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import org.springframework.web.bind.annotation.ResponseBody;
  12. import org.springframework.web.servlet.ModelAndView;
  13. import javax.servlet.http.HttpServletRequest;
  14. import javax.servlet.http.HttpServletResponse;
  15. import java.util.HashMap;
  16. import java.util.Map;
  17. /**
  18. * Created by LF on 2017/4/16.
  19. */
  20. @Controller
  21. public class MyErrorController extends BasicErrorController {
  22. public MyErrorController(ServerProperties serverProperties) {
  23. super(new DefaultErrorAttributes(), serverProperties.getError());
  24. }
  25. /**
  26. * 覆盖默认的Json响应
  27. */
  28. @Override
  29. public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
  30. Map<String, Object> body = getErrorAttributes(request,
  31. isIncludeStackTrace(request, MediaType.ALL));
  32. HttpStatus status = getStatus(request);
  33. //输出自定义的Json格式
  34. Map<String, Object> map = new HashMap<String, Object>();
  35. map.put("status", false);
  36. map.put("msg", body.get("message"));
  37. return new ResponseEntity<Map<String, Object>>(map, status);
  38. }
  39. /**
  40. * 覆盖默认的HTML响应
  41. */
  42. @Override
  43. public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
  44. //请求的状态
  45. HttpStatus status = getStatus(request);
  46. response.setStatus(getStatus(request).value());
  47. Map<String, Object> model = getErrorAttributes(request,
  48. isIncludeStackTrace(request, MediaType.TEXT_HTML));
  49. ModelAndView modelAndView = resolveErrorView(request, response, status, model);
  50. //指定自定义的视图
  51. return(modelAndView == null ? new ModelAndView("error", model) : modelAndView);
  52. }
  53. }

总结

其实查看 BasicErrorController 源码,响应结果不论是 html 还是 json,内容源都是 ErrorAttribute。第三种方法只能改变内容,却改变不了格式,特别是 html 页面的样式,笔者就不予考虑了。

无论第一种还是第二种办法,都需要你看下BasicErrorController的继承体系及实现,因为 BasicErrorController 也是实现了 ErrorController。

采用第一种方式你可以具有完全的控制权,你可以摒弃默认的 “Whitelabel Error Page”,指定自己的视图及视图样式,你可以指定响应的Json格式内容等等,因为 BasicErrorController 不再起作用。

参照了 BasicErrorController 的源码,感觉第二种方法可能更简便些,所以实现了第二种方法。第二种方法的思路其实就是你可以通过继承,利用 BasicErrorController 已有的功能,或者进行扩展。

本文转载自:https://blog.csdn.net/L_Sail/article/details/70198886

评论

发表评论 点击刷新验证码

提示

该功能暂未开放