我遇到的问题之request method ‘post’ not supported
你所遇到的问题,别人肯定也遇到过。
背景
最近在做一个后台系统的spring升级为spring boot的项目,期间各种xml配置要升级为configuration Bean的方式,还有自己公司的一些jar包升级。升级的,如果想快速搞定的,可以找别人已经尝试过上线的系统拿来参考。不过我一般不屑上面这种做法,本着学习工作,工作学习的心态,怎么可以老找捷径了,话不多说,上报错信息。
报405错误,请求的方法不支持“POST"请求。
熟悉的页面,不熟悉的解决办法。
尝试解决
第一次尝试,方法上强制指定为POST请求。
尝试失败,实际上springMvc未指定method时,get,post方式都可以请求进来。
一时没有思路,开启“百度大法好”,搜了大片就下面一篇文章阅读数量多(潜台词,他阅读量多,他是大佬)
https://blog.youkuaiyun.com/weixin_42074868/article/details/85001093?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1
文章有图,有源码,但最终提供出来的解决办法让我无法苟同。
源码大法好
读完这篇博文后,虽然没有解决问题,但给我提供了解决思路,就是从源码分析问题。
开启断点,由上文可知,具体报错位置在WebContentGenerator.checkRequest方法的378行处,一次post请求会调用两次checkRequest()方法.
第一次调用时,可以看到supportedMethods为null,跳过接着执行。
第二次调用时,supportedMethods集合中只包含GET,HEAD两个方法,所以报了对应的错误,然后游览器显示403。
问题复现了,那我们该怎么接着分析问题产生的原因呢,这里我将修改supportedMethods这个集合数据的都打上了断点。
构造方法
set方法,初步怀疑是下面方法设置的两个值
果然在setSupportedMethods方法的调用上面,我们看到了这样一个控制器
/**
* 平凡的控制器,总是返回一个预先配置的视图,并可以选择设置响应状态代码。可以使用以下方式配置视图和状态
提供的配置属性。
* Trivial controller that always returns a pre-configured view and optionally
* sets the response status code. The view and status can be configured using
* the provided configuration properties.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Keith Donald
* @author Rossen Stoyanchev
*/
public class ParameterizableViewController extends AbstractController {
private Object view;
private HttpStatus statusCode;
private boolean statusOnly;
public ParameterizableViewController() {
super(false);
//这里调用了setSupportedMethods只方法传入get head方法
setSupportedMethods(HttpMethod.GET.name(), HttpMethod.HEAD.name());
}
接着向上跟踪代码在ViewControllerRegistry中,看到了addViewController这样一个方法。
/**
* Map a view controller to the given URL path (or pattern) in order to render
* a response with a pre-configured status code and view.
* <p>Patterns like {@code "/admin/**"} or {@code "/articles/{articlename:\\w+}"}
* are allowed. See {@link org.springframework.util.AntPathMatcher} for more details on the
* syntax.
*/
public ViewControllerRegistration addViewController(String urlPath) {
ViewControllerRegistration registration = new ViewControllerRegistration(urlPath);
registration.setApplicationContext(this.applicationContext);
this.registrations.add(registration);
return registration;
}
这个方法在我们系统中有使用过,还是我最近添加的。
由于我们后台系统,需要对一些url装饰,方便加上对应的head,foot固定页面资源。我原先采用了下面这篇文章,把原来的sitemesh进行了改造,这里我偷了个懒,没有单独定义一个DecoratorController写上面三个请求,而是加到了addViewControllers方法中。
spring boot整合freemarker+sitemeshhttps://www.jianshu.com/p/8484e706b5a4
重新debug
可以看到由我们的webMvcConfig类的addViewControllers方法进来的,传入了get,head。并没有post方法
最终default,perfrom,control三个静态的控制器注册了上去。
我们系统里面对应的post请求url都是/****/control这种,会被control这个控制器装饰,从而拦截了post请求。
public class WebSiteMeshFilter extends ConfigurableSiteMeshFilter {
private static final String DEFAULT_URL = "/default";
private static final String CONTROL_URL = "/control";
private static final String PERFORM_URL = "/perform";
@Override
protected void applyCustomConfiguration(SiteMeshFilterBuilder builder) {
builder.addDecoratorPath("/*stru*", DEFAULT_URL)
.addDecoratorPath("/*main*", DEFAULT_URL)
.addDecoratorPath("/*/control*", CONTROL_URL)
.addDecoratorPath("/*/perform*", PERFORM_URL)
.addExcludedPath("**ajax=true")//不需要装饰的路径
.addExcludedPath("/images/**")
.addExcludedPath("/scripts/**")
.addExcludedPath("/services/**")
.addExcludedPath("/styles/**")
.addExcludedPath("/statics/**")
.addExcludedPath("/visitWeb.html");
}
}
总结
我单独定义一个DecoratorController,把静态的addViewControllers代码删除,就跑成功了。
@Controller
public class DecoratorController {
@RequestMapping(value = "/default")
public ModelAndView index(ModelMap modelMap) {
return new ModelAndView("/decorators/default");
}
@RequestMapping(value = "/control")
public ModelAndView control(ModelMap modelMap) {
return new ModelAndView("/decorators/control");
}
@RequestMapping(value = "/perform")
public ModelAndView perform(ModelMap modelMap) {
return new ModelAndView("/decorators/perform");
}
}
奇怪的知识增加了
addViewControllers中只能添加get,head请求的url,一般常用于静态的 页面。
springboot是在spring的基础上,增添了starter相应的功能和各种扩展,但其内部保存的spring功能是完整,几乎也是完全可以使用的。这么久,spring的ioc和aop功能都几乎没有改动,除了spring第五版采用了jdk8的很大书写方式。所以不要傻傻的看别人博客说springboot不支持post方法。
希望以后一切问题从实际出发,哪里出了问题,就从哪里开始分析。
尽信书,不如无书