记spring/spring boot中遇到的一些细节问题

1.错误页注册:

404,即page not found ,这类业务错误处理,spring demo是通过error mapping 处理。有两种处理方式,其一是用过注册error controller。 其二是通过 ErrorPageRegistrar 注册ErrorPage

@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-14

spring boot 在spring boot autoconfigure 项目中,对错误页进行了默认配置。

仅webflux 项目而言:在webflux 中使用org.springframework.web.server.handler.WebHandlerDecorator装饰了WebHandler一个ExceptionHandlingWebHandler。在其中便使用了org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler对异常通过HttpStatus进行异常页匹配。

/**
 * 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。

但在项目中,因为使用request.getRequestDispatcher(uri).forward(request, response) 重定向POST请求时。导致发生异常:

DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported]

定位异常原因:在ResourceHttpRequestHandler 检查this.supportedMethods 不包含 request methods。

stack:

异常堆栈

关键代码

	// 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. 新:

转载于:https://my.oschina.net/yuyizyk/blog/2993989

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值