SpringBoot自定义/error路径失效的解决
目录
- SpringBoot自定义/error路径失效
- 背景
- 配置信息
- 解决思路
- SpringBoot的/error的自定义处理
- 它的具体定义如下
- 他有三个方法
SpringBoot自定义/error路径失效
背景
最近使用SpringBoot做controller统一异常处理的时候,配置好映射路径(/error),使用SpringBoot自带的异常通知注解@ControllerAdvice配置好异常处理类,按理说在Controller发生异常的时候重定向到自定义错误页面(这里是重定向到SpringMVC的映射路径),可实际调试的时候却定向到了SpringBoot默认的错误页面。
配置信息
此处配置只是一个小示例,省略了对异常的处理。
@ControllerAdvice(annotations = Controller.class) public class ExceptionAdvice { @ExceptionHandler({Exception.class}) // 此方法参数有多个,具体可参考相关文档 public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendRedirect(request.getContextPath() + "/error"); } }
@Controller public class HomeController { @GetMapping("/error") public String getErrorPage(){ // 此处使用了Thymeleaf模板,返回的是页面 return "/site/error/500"; } }
解决思路
由于我在controller中配置了/error的映射路径,我通过网页路径直接访问该路径也是上面那个错误页面,断点调试也没有进入我自定义的controller。
再看页面显示的状态码,不是404,说明该路径是没有问题的。
这个问题困惑了我很久,一开始没转过弯来。
说下我的解决思路,因为我在使用的SpringBoot配置文件是application.yml,这个文件在idea中会有提示,我在该配置文件中输入了error,如下图:
这里有一个server.error.path=/error,看到这个就大概知道原因了,路径冲突了,我在SpringMVC中配置的映射路径也是error。
尝试验证一下,将SpringMVC的路径修改了一下,果然可以正常访问了。
有没有可以不改变SpringMVC路径的方法呢?我一开始是在application.yml中加入下面这段配置:
server: error: # 此处可随便写一个路径,或者留空也行,只有不和自定义的error路径冲突 path:
这样确实可以解决路径冲突的问题,可是这只是去忽略它,而不是去修改它。
通过查阅资料发现,SpringBoot会为我们创建一个叫BasicErrorController的类,该类由Spring创建并默认用来处理Controller中的异常,如果能替换掉该类,就可以解决我们的问题。
如何替换?Spring提供的方法提供一个类型实现ErrorController接口,其实BasicErrorController也是实现了该类。
所有我们只需要将我们自定义ExceptionAdvice类实现该接口,实现相应方法即可,修改后代码如下:
@ControllerAdvice(annotations = Controller.class) public class ExceptionAdvice implements ErrorController{ private static final String ERROR_PATH = "/error"; @ExceptionHandler({Exception.class}) // 此方法参数有多个,具体可参考相关文档 public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendRedirect(request.getContextPath() + "/error"); } @Override public String getErrorPath() { return ERROR_PATH; } }
小结:其实就是一个很简单的问题,而我在一开始的时候却在纠结是不是注解使用错误之类的事,debug方向是对的,就是一时间没转过弯来,导致这个问题困扰了我挺长的时间,好在及时理清思路后能解决该问题。
SpringBoot的/error的自定义处理
在springboot项目里,如果没有统一异常处理,或者如果没有处理全面,又或者在springCloud zuul中调用微服务接口出错时,spring会自动把错误转发到默认给/error处理。
正常情况下,可以配置错误页面来给用户提示错误,如404,500等。但是在前后分离项目中,可能更期望给前台返回一个特定格式的json来展示错误信息。所以可以用代码来自定义异常错误信息。
/error端点的实现来源于SpringBoot的org.springframework.boot.autoconfigure.web.BasicErrorController,
它的具体定义如下
@RequestMapping @ResponseBody 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<Map<String, Object>>(body, status);}
通过调用 getErrorAtt豆butes 方法来根据请求参数组织错误信息的返回结果,而这里的 getErrorAtt豆bu七es 方法会将具体组织逻辑委托给 org.springframework.boot.autoconfigure.web.ErrorAttributes接口提供的 ge七ErrorAttributes 来实现。
在 Spring Boot 的自动化配置机制中,默认会采用 org.springframework.boot.autoconfigure.web.DefaultErrorAttribut作为该接口的实现。
在spring注册这个bean的时候,使用了注解@ConditionalOnMissingBean(value = ErrorAttributes.class, search =SearchStrategy.CURRENT)
说明只有在不存在ErrorAttributes的bean的时候,才会使用DefaultErrorAttributes来处理,如果我们可以自定义一个,就可以使用我们的类来处理异常了。
编写一个类继承DefaultErrorAttributes
他有三个方法
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) public Throwable getError(RequestAttributes requestAttributes)
他们的执行顺序就如上述顺序。
我们可以在getErrorAttributes方法中拿到所有的异常信息,展示如下:
{"timestamp":1528190975129,"status":200,"error":"OK","exception":"java.lang.RuntimeException","message":"error............","path":"/a/b"}
可以在resolveException中拿到异常信息,如果需要返回json,则可以利用response来输出到前台,比如:
/* 使用response返回 */ response.setStatus(HttpStatus.OK.value()); // 设置状态码 response.setContentType(MediaType.APPLICATION_JSON_VALUE); // 设置ContentType response.setCharacterEncoding("UTF-8"); // 避免乱码 response.setHeader("Cache-Control", "no-cache, must-revalidate"); try { response.getWriter().print("json.........."); response.getWriter().flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { response.getWriter().close(); } catch (IOException e) { e.printStackTrace(); } }
这样,当有异常发生时,就可以在前台收到异常的json信息,而这个也可以代替统一异常处理使用。同时在springCloud zuul中可以用来自定义异常。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。