1.错误页注册:html
404,即page not found ,这类业务错误处理,spring demo是经过error mapping 处理。有两种处理方式,其一是用过注册error controller。 其二是经过 ErrorPageRegistrar 注册ErrorPagejava
@Bean public org.springframework.boot.web.server.ErrorPageRegistrar errorPageRegistrar() { return registry -> { registry.addErrorPages(new ErrorPage[] {new ErrorPage(HttpStatus.NOT_FOUND,"/common/404.html")}); }; }
// to 2019-05-14react
spring boot 在spring boot autoconfigure 项目中,对错误页进行了默认配置。web
仅webflux 项目而言:在webflux 中使用org.springframework.web.server.handler.WebHandlerDecorator装饰了WebHandler一个ExceptionHandlingWebHandler。在其中便使用了org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler对异常经过HttpStatus进行异常页匹配。spring
/** * Basic global {@link org.springframework.web.server.WebExceptionHandler}, rendering * {@link ErrorAttributes}. * <p> * More specific errors can be handled either using Spring WebFlux abstractions (e.g. * {@code @ExceptionHandler} with the annotation model) or by adding * {@link RouterFunction} to the chain. * <p> * This implementation will render error as HTML views if the client explicitly supports * that media type. It attempts to resolve error views using well known conventions. Will * search for templates and static assets under {@code '/error'} using the * {@link HttpStatus status code} and the {@link HttpStatus#series() status series}. * <p> * For example, an {@code HTTP 404} will search (in the specific order): * <ul> * <li>{@code '/<templates>/error/404.<ext>'}</li> * <li>{@code '/<static>/error/404.html'}</li> * <li>{@code '/<templates>/error/4xx.<ext>'}</li> * <li>{@code '/<static>/error/4xx.html'}</li> * <li>{@code '/<templates>/error/error'}</li> * <li>{@code '/<static>/error/error.html'}</li> * </ul> * <p> * If none found, a default "Whitelabel Error" HTML view will be rendered. * <p> * If the client doesn't support HTML, the error information will be rendered as a JSON * payload. * * @author Brian Clozel * @since 2.0.0 */ public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler { //... /** * Render the error information as an HTML view. * @param request the current request * @return a {@code Publisher} of the HTTP response */ protected Mono<ServerResponse> renderErrorView(ServerRequest request) { boolean includeStackTrace = isIncludeStackTrace(request, MediaType.TEXT_HTML); Map<String, Object> error = getErrorAttributes(request, includeStackTrace); HttpStatus errorStatus = getHttpStatus(error); ServerResponse.BodyBuilder responseBody = ServerResponse.status(errorStatus) .contentType(MediaType.TEXT_HTML); return Flux .just("error/" + errorStatus.value(), "error/" + SERIES_VIEWS.get(errorStatus.series()), "error/error") .flatMap((viewName) -> renderErrorView(viewName, responseBody, error)) .switchIfEmpty(this.errorProperties.getWhitelabel().isEnabled() ? renderDefaultErrorView(responseBody, error) : Mono.error(getError(request))) .next(); } //... }
2.POST 访问spring 静态资源 响应405 Method Not Allowed org.springframework.web.servlet.config.annotation.WebMvcConfigurer.addResourceHandlers(ResourceHandlerRegistry registry) 中注册的静态资源默认只支持GET、HEAD.这样作的主要缘由是方便spring 响应静态资源时添加Cache-Control。session
但在项目中,由于使用request.getRequestDispatcher(uri).forward(request, response) 重定向POST请求时。致使发生异常:mvc
DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]
定位异常缘由:在ResourceHttpRequestHandler 检查this.supportedMethods 不包含 request methods。app
stack:ide
关键代码ui
// O org.springframework.web.servlet.support.WebContentGenerator 376 /** * Check the given request for supported methods and a required session, if any. * @param request current HTTP request * @throws ServletException if the request cannot be handled because a check failed * @since 4.2 */ protected final void checkRequest(HttpServletRequest request) throws ServletException { // Check whether we should support the request method. String method = request.getMethod(); if (this.supportedMethods != null && !this.supportedMethods.contains(method)) { throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods); } // Check whether a session is required. if (this.requireSession && request.getSession(false) == null) { throw new HttpSessionRequiredException("Pre-existing session required but none found"); } }
this.supportedMethods 在ResourceHttpRequestHandler 赋值为GET和HEAD,所以使用POST方式访问spring mvc 静态资源会抛出异常。
// TO org.springframework.web.servlet.resource.ResourceHttpRequestHandler extends WebContentGenerator public ResourceHttpRequestHandler() { super(HttpMethod.GET.name(), HttpMethod.HEAD.name()); }
那么解决方式便只有将this.supportedMethods进行扩增POST等。
在目前编写的项目中,请求资源在业务处理失败时,并非直接在当前servlet中进行反馈,而是进行一次重定向到异常处理的servlet。暂不谈论该机构是否值得,只能考虑对ResourceHttpRequestHandler对象进行改进。
咱们都知道forward(request, response) 重定向的request和response共享的原理,所以请求Method不能进行修改成GET(不考虑包装|代理|反射修改request方式)。
ResourceHttpRequestHandler的注册一般在WebMvcConfigurer.addResourceHandlers(ResourceHandlerRegistry registry)中。实际是经过 HandlerMapping org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.resourceHandlerMapping()进行注册为Bean。而后经过DispatcherServlet.initHandlerMappings(ApplicationContext context) 进行使用。
所以只须要将resourceHandlerMapping中注册的ResourceHttpRequestHandler修改this.supportedMethods便可完成经过POST方式访问静态资源。
即:
@Component public final class ErcApplicationRunner implements ApplicationRunner{ @Resource(name = "resourceHandlerMapping") HandlerMapping resourceHandlerMapping; @Override public void run(ApplicationArguments args) throws Exception { if (resourceHandlerMapping instanceof org.springframework.web.servlet.handler.SimpleUrlHandlerMapping) { org.springframework.web.servlet.handler.SimpleUrlHandlerMapping url = (SimpleUrlHandlerMapping) resourceHandlerMapping; url.getUrlMap().values().forEach(v -> { if (v instanceof ResourceHttpRequestHandler) { ResourceHttpRequestHandler handler = (ResourceHttpRequestHandler) v; handler.setSupportedMethods(ArrayUtils.add(handler.getSupportedMethods(), WebContentGenerator.METHOD_POST)); } }); } } }
附: