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)); } }); } } }
附: