问题
应项目要求,将工程中spring版本从[4.1.6.RELEASE]升级到[4.3.22.RELEASE],处理依赖冲突后,项目正常启动,大部分接口正常调用,部分接口异常,报错信息如下:
javax.servlet.ServletException: No adapter for handler [org.springframework.web.servlet.handler.AbstractHandlerMapping$PreFlightHandler@7e0b6142]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
at org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1202)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:947)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at com.vivo.framework.spring.webmvc.VivoDispatcherServlet.doService(VivoDispatcherServlet.java:96)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doOptions(FrameworkServlet.java:908)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:656)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
定位分析
通过栈信息,跟踪到DispatcherServlet类,发现是No adapter for handler for PreFlightHandler,没有adapter处理options预请求。回看异常的接口,全部为跨域接口,且在发送doOptions阶段出现异常。代码如下:
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
通过分别在4.16版本和4.3.2.2版本断点调试,比对发现,两版中,默认HandlerAdapter实现类均为RequestMappingHandlerAdapter,但4.16版本options预请求处理类为HandlerMethod,而4.3.22版本中为AbstractHandlerMapping$PreFlightHandler,RequestMappingHandlerAdapter不支持处理PreFlightHandler,所以报出该异常。
解决办法(二选一)
- spring配置文件中增加如下配置,框架会自动实例化HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter这两个bean,其中HttpRequestHandlerAdapter可以处理options预请求
<mvc:annotation-driven>
- 手动配置HttpRequestHandlerAdapter的bean
<bean id="httpRequestHandlerAdapter" class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
总结
SpringMVC处理Options类型的请求,需要HttpRequestHandlerAdapter,如果没有就会抛异常
拓展
浏览器将CORS请求分为两类:简单请求(simple request)和非简单请求(not-simple-request),简单请求不会doOptions,而非简单请求会doOptions。区分条件如下:
同时满足下列三大条件,就属于简单请求,否则属于非简单请求
- 请求方式只能是:GET、POST、HEAD
- HTTP请求头限制这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID
- Content-type只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain
典型非简单请求如,Content-type为application/json或自定义header,这些发生跨域请求时均需预请求一次。
网友评论