記spring/spring boot中遇到的一些細節問題

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

}

附:

  1. 靜態資源配置圖:

  1. 原:

  1. 新:

相關文章
相關標籤/搜索