作者:mobiledu2502910181 | 来源:互联网 | 2023-10-17 13:26
在内嵌容器为tomcat的情况下,springboot一个请求发生内部错误需要调用错误页时,其实是在StandardHostValve里查找错误页,再触发一次错误页请求调用。具体代码如下
public final void invoke(Request request, Response response)throws IOException, ServletException {......Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);// Protect against NPEs if the context was destroyed during a// long running request.if (!context.getState().isAvailable()) {return;}// Look for (and render if found) an application level error pageif (response.isErrorReportRequired()) {if (t != null) {throwable(request, response, t);} else {status(request, response);}}......
}private void status(Request request, Response response) {int statusCode = response.getStatus();// Handle a custom error page for this status codeContext context = request.getContext();if (context == null) {return;}/* Only look for error pages when isError() is set.* isError() is set when response.sendError() is invoked. This* allows custom error pages without relying on default from* web.xml.*/if (!response.isError()) {return;}//根据错误码查找已注册的错误页ErrorPage errorPage = context.findErrorPage(statusCode);if (errorPage == null) {//注意这里,springboot默认只注册一个错误页/error,所以在默认情况下//所有异常错误码都会跳转到/error// Look for a default error pageerrorPage = context.findErrorPage(0);}......
}
默认情况下,springboot只注册一个错误页/error,因此查找错误码页时不会找到任何匹配的,只能跳转到/error。springboot内注册错误页是通过添加ErrorPageRegistrar实现的
private void postProcessBeforeInitialization(ErrorPageRegistry registry) {for (ErrorPageRegistrar registrar : getRegistrars()) {registrar.registerErrorPages(registry);}}private Collection getRegistrars() {if (this.registrars &#61;&#61; null) {// Look up does not include the parent contextthis.registrars &#61; new ArrayList<>(this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);this.registrars &#61; Collections.unmodifiableList(this.registrars);}return this.registrars;}
所以我们要在springboot环境内注册自己的错误码配置的话&#xff0c;添加一个自定义的ErrorPageRegistrar就可以了。为了方便起见&#xff0c;我把错误码定义和错误页配置放一起了&#xff0c;只要把下面的代码拷到任意一个Controller里就可以了
&#64;Beanpublic ErrorPageRegistrar errorPageRegistrar(){return registry -> {registry.addErrorPages(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR,"/500"));registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND,"/404"));registry.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN,"/403"));registry.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED,"/401"));};}&#64;RequestMapping("/500")&#64;ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)public RequestResult error500(HttpServletRequest request){Map body &#61; getErrorAttributes(request,true);return new RequestResult(false,"服务器异常",body);}&#64;RequestMapping("/404")&#64;ResponseStatus(HttpStatus.NOT_FOUND)public RequestResult error404(HttpServletRequest request){Map body &#61; getErrorAttributes(request,true);return new RequestResult(false,"无此路径:"&#43;body.get("path"));}&#64;RequestMapping("/403")&#64;ResponseStatus(HttpStatus.FORBIDDEN)public RequestResult error403(HttpServletRequest request){Map body &#61; getErrorAttributes(request,true);return new RequestResult(false,"无此资源访问权限:"&#43;body.get("path"));}&#64;RequestMapping("/401")&#64;ResponseStatus(HttpStatus.UNAUTHORIZED)public RequestResult error401(HttpServletRequest request){Map body &#61; getErrorAttributes(request,true);return new RequestResult(false,"未授权的访问:"&#43;body.get("path"));}