亲宝软件园·资讯

展开

SpringBoot自定义error路径失效

虎口脱险OvO 人气:11

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中可以用来自定义异常。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

加载全部内容

相关教程
猜你喜欢
用户评论