前言
这是 最近碰到的一个问题, 大概是在 2022.05.30
前端这边 发送了一个业务请求过来, 这个请求路径是服务端这边不存在的
但是 奇怪的一点就是, 如果是以 get 请求发送过来, 服务端响应的是正确的 404 "Not Found", 但是 如果是以 post 请求发送过来, 服务端这边响应的是 405 "Method Not Allowed"
因此 之后 花了一些时间 来看一下 这个问题
测试用例
这是一个测试的 notFound 的手动处理的服务
@RestController
@RequestMapping("/HelloWorld")
public class HelloWorldController {@GetMapping("/notFound")public List<JSONObject> notFound() {List<JSONObject> result = new ArrayList<>();result.add(wrapEntity("404", "not-found"));return result;}
}
这是 mvcConfigure, 这里向容器中注册了一些错误页面的处理方式
比如 这里的 404, 直接 转发到 "/HelloWorld/notFound", 具体的这个转发步骤是在 tomcat 中处理的
/*** MyWebMvcConfigurer** @author Jerry.X.He <970655147@qq.com>* @version 1.0* @date 2022-06-12 11:39*/
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {@Beanpublic WebServerFactoryCustomizer containerCustomizer() {return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {@Overridepublic void customize(ConfigurableWebServerFactory factory) {ErrorPage errorPage = new ErrorPage(HttpStatus.NOT_FOUND, "/HelloWorld/notFound");Set<ErrorPage> errorPageSet = new LinkedHashSet<>();errorPageSet.add(errorPage);factory.setErrorPages(errorPageSet);}};}}
post 为什么响应的是 405?
如果是发送 post 请求响应结果如下, 服务端响应的是 405
首先需要注意的是 errorPage 的这部分处理 在 tomcat 中是找到 errorCode 对应的 errorPage
然后再通过 servletContext.getRequestDispatcher(errorPage.path).forward(req, resp)
然后因为我配置的 "/HelloWorld/notFound" 仅仅支持 GET
我这里原请求是 POST, dispatch 之后依然是 POST, 因此 服务端校验 method 的时候, 响应了 405
如果是 get 请求
服务器发现 没有匹配的资源, 根据 errorCode 寻找 errorPage
然后 dispatch 到 “/HelloWorld/notFound”, 然后 响应了相关的结果返回给客户端
比如 这里的 { name -> 404, age -> not-found }
ErrorPage 的相关处理流程
首先是 服务器使用这边, 根据 statusCode 查询 errorPage, 这个映射来自于 tomcat 的 StandardContext
然后 走后面的 servletContext.getRequestDispatcher(errorPage.path).forward(req, resp) 的流程
然后 我们代码中注册的 404 的 errorPage 被添加是在 WebServerFactory 初始化 StandardContext 的时候
我们的注册 errorPage 的地方是在 WebServerFactoryCustomizerBeanPostProcessor 中处理的, 当 WebServerFactory 实例初始化之后, init之前, 调用 Customizer
这样 这一整个流程就串联起来了
完