admin管理员组文章数量:1399006
I am upgrading the dependencies of a spring boot application with an embedded Tomcat server. When stepping through and upgrading from Spring 5.1 -> 5.2 and Spring Boot 2.1 -> 2.2 an issue arose where only POST and GET Http requests were being processed, while PATCH and DELETE were not. The Controller and JSP pages worked perfectly until I made this Spring upgrade.
For instance, when I breakpoint the below controller I can trace myself going to the Edit page. When I click Submit on that page it should then take me to the controller's update() method with RequestMethod.PATCH annotated. Since the upgrade however this instead takes me to the controller's create() method with RequestMethod.POST instead. It will then error because it's trying to add an existing item in the database, instead of updating an existing one. This is happening for multiple controllers.
I've confirmed that the normal use case of going to the Add page and clicking submit will hit the correct create() method in the controller and will create a new item.
I am required to use JAVA 8 and have pushed both Spring and Boot as far forward in versions as I can hoping this would resolve itself, but the issue remains. My current dependency versions are below.
I've attempted to breakpoint through the relevant Spring and Tomcat classes I can find to see where the error is occurring. I've confirmed that the startup building of Spring's RequestMappingInfoHandlerMapping is correctly mapping the controller's handler methods to the HTTP methods. Instead, it seems to be that the HttpServletRequest objects sent to Spring already have the incorrect HTTP methods. I've attempted to breakpoint through Tomcat classes to investigate, but am at a loss as to what is causing this, let alone how to resolve it.
Just in case I cleared out the .m2\repository folder and reloaded Maven, but to no avail.
Parent Pom.xml
<spring.version>5.3.39</spring.version>
<spring-boot.version>2.7.18</spring-boot.version>
<spring-security.version>5.8.16</spring-security.version>
<tomcat.version>9.0.102</tomcat.version>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.1.15.RELEASE</version>
</dependency>
<dependency>
<groupId>.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.15.RELEASE</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>${spring-security.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
Sub-Pom1.xml
<dependency>
<groupId>.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
Sub-Pom2.xml
<dependency>
<groupId>.apache.tomcat</groupId>
<artifactId>tomcat-el-api</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
Configuration.java
@Configuration
@Import(CoreConfiguration.class)
@EnableAutoConfiguration
@ComponentScan(".aurora.biorepository")
public class Configuration implements WebMvcConfigurer {
public static void main(String[] args) throws Exception {
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "not_test");
SpringApplication.run(ApplicationConfiguration.class, args);
}
@Bean
public ServletWebServerFactory embeddedServletContainerFactory() {
return new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
}
};
}
@Bean
public FilterRegistrationBean getFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(getMethodConvertingFilter());
registration.addUrlPatterns("/*");
registration.setDispatcherTypes(DispatcherType.FORWARD);
registration.setName("getMethodConvertingFilter");
return registration;
}
@Bean
public GetMethodConvertingFilter getMethodConvertingFilter() {
return new GetMethodConvertingFilter();
}
@Bean
@Autowired
public DomainClassConverter domainClassConverter(@Qualifier("mvcConversionService") ConversionService cs) {
return new DomainClassConverter(cs);
}
}
GetMethodConvertingFilter.java
public class GetMethodConvertingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
// do nothing
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(wrapRequest((HttpServletRequest) request), response);
}
@Override
public void destroy() {
// do nothing
}
private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) {
return new HttpServletRequestWrapper(request) {
@Override
public String getMethod() {
return "GET";
}
};
}
}
Controller.java
@Controller
@RequestMapping("/home")
public class Controller {
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String add(@ModelAttribute("modelAttribute") AddForm addForm, Errors errors, Model model, RedirectAttributes redirectAttributes) {
...doStuff();
return "home/add";
}
@RequestMapping(method = RequestMethod.POST)
public String create(@ModelAttribute("modelAttribute") @Valid AddForm addForm, Errors errors, Model model, RedirectAttributes redirectAttributes, javax.servlet.http.HttpServletRequest request) {
...doStuff();
}
@RequestMapping(value = "/{key}/edit", method = RequestMethod.GET)
public String edit(ModelMap model, @PathVariable String key) {
...doStuff();
return "home/edit";
}
@RequestMapping(method = RequestMethod.PATCH)
public String update(@ModelAttribute("modelAttribute") @Valid AddForm addForm, Errors errors, Model model, RedirectAttributes redirectAttributes, HttpServletRequest request) throws ObjectNotFoundException {
...doStuff();
}
}
Add.jsp
<tiles:insertDefinition name="base">
<tiles:putAttribute name="title"><fmt:message key=".home.Controller.add.title" /></tiles:putAttribute>
<tiles:putAttribute name="content">
<form:form action="/home" method="POST" modelAttribute="modelAttribute">
<div class="half-page">
<%@ include file="_form.jsp" %>
<div class="row">
<input type="submit" name="submit" value="<fmt:message key=".home.Controller.add.action.submit" />" class="button" />
</div>
</div>
</form:form>
</tiles:putAttribute>
<tiles:putAttribute name="javascripts">
<script type="text/javascript" src="<c:url value="/resources/js/home/_form.js" />"></script>
</tiles:putAttribute>
</tiles:insertDefinition>
Edit.jsp
<tiles:insertDefinition name="base">
<tiles:putAttribute name="title">
<fmt:message key=".home.Controller.edit.title">
<fmt:param value="${itemAddForm.item.name}" />
</fmt:message>
</tiles:putAttribute>
<tiles:putAttribute name="sectionNavigation" type="template" value="/WEB-INF/views/home/" />
<tiles:putAttribute name="content">
<form:form action="/home" method="PATCH" modelAttribute="modelAttribute">
<div class="half-page">
<%@ include file="_form.jsp" %>
<div class="row">
<input type="submit" name="submit" value="<fmt:message key=".home.Controller.edit.action.submit" />" class="button" />
</div>
</div>
</form:form>
</tiles:putAttribute>
<tiles:putAttribute name="javascripts">
<script type="text/javascript" src="<c:url value="/resources/js/home/_form.js" />"></script>
</tiles:putAttribute>
</tiles:insertDefinition>
I am upgrading the dependencies of a spring boot application with an embedded Tomcat server. When stepping through and upgrading from Spring 5.1 -> 5.2 and Spring Boot 2.1 -> 2.2 an issue arose where only POST and GET Http requests were being processed, while PATCH and DELETE were not. The Controller and JSP pages worked perfectly until I made this Spring upgrade.
For instance, when I breakpoint the below controller I can trace myself going to the Edit page. When I click Submit on that page it should then take me to the controller's update() method with RequestMethod.PATCH annotated. Since the upgrade however this instead takes me to the controller's create() method with RequestMethod.POST instead. It will then error because it's trying to add an existing item in the database, instead of updating an existing one. This is happening for multiple controllers.
I've confirmed that the normal use case of going to the Add page and clicking submit will hit the correct create() method in the controller and will create a new item.
I am required to use JAVA 8 and have pushed both Spring and Boot as far forward in versions as I can hoping this would resolve itself, but the issue remains. My current dependency versions are below.
I've attempted to breakpoint through the relevant Spring and Tomcat classes I can find to see where the error is occurring. I've confirmed that the startup building of Spring's RequestMappingInfoHandlerMapping is correctly mapping the controller's handler methods to the HTTP methods. Instead, it seems to be that the HttpServletRequest objects sent to Spring already have the incorrect HTTP methods. I've attempted to breakpoint through Tomcat classes to investigate, but am at a loss as to what is causing this, let alone how to resolve it.
Just in case I cleared out the .m2\repository folder and reloaded Maven, but to no avail.
Parent Pom.xml
<spring.version>5.3.39</spring.version>
<spring-boot.version>2.7.18</spring-boot.version>
<spring-security.version>5.8.16</spring-security.version>
<tomcat.version>9.0.102</tomcat.version>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.1.15.RELEASE</version>
</dependency>
<dependency>
<groupId>.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.1.15.RELEASE</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
<version>${spring-security.version}</version>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>${spring-security.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring-security.version}</version>
</dependency>
Sub-Pom1.xml
<dependency>
<groupId>.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
Sub-Pom2.xml
<dependency>
<groupId>.apache.tomcat</groupId>
<artifactId>tomcat-el-api</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.apache.tomcat</groupId>
<artifactId>tomcat-jasper-el</artifactId>
<version>${tomcat.version}</version>
<scope>test</scope>
</dependency>
Configuration.java
@Configuration
@Import(CoreConfiguration.class)
@EnableAutoConfiguration
@ComponentScan(".aurora.biorepository")
public class Configuration implements WebMvcConfigurer {
public static void main(String[] args) throws Exception {
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "not_test");
SpringApplication.run(ApplicationConfiguration.class, args);
}
@Bean
public ServletWebServerFactory embeddedServletContainerFactory() {
return new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
}
};
}
@Bean
public FilterRegistrationBean getFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(getMethodConvertingFilter());
registration.addUrlPatterns("/*");
registration.setDispatcherTypes(DispatcherType.FORWARD);
registration.setName("getMethodConvertingFilter");
return registration;
}
@Bean
public GetMethodConvertingFilter getMethodConvertingFilter() {
return new GetMethodConvertingFilter();
}
@Bean
@Autowired
public DomainClassConverter domainClassConverter(@Qualifier("mvcConversionService") ConversionService cs) {
return new DomainClassConverter(cs);
}
}
GetMethodConvertingFilter.java
public class GetMethodConvertingFilter implements Filter {
@Override
public void init(FilterConfig config) throws ServletException {
// do nothing
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(wrapRequest((HttpServletRequest) request), response);
}
@Override
public void destroy() {
// do nothing
}
private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) {
return new HttpServletRequestWrapper(request) {
@Override
public String getMethod() {
return "GET";
}
};
}
}
Controller.java
@Controller
@RequestMapping("/home")
public class Controller {
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String add(@ModelAttribute("modelAttribute") AddForm addForm, Errors errors, Model model, RedirectAttributes redirectAttributes) {
...doStuff();
return "home/add";
}
@RequestMapping(method = RequestMethod.POST)
public String create(@ModelAttribute("modelAttribute") @Valid AddForm addForm, Errors errors, Model model, RedirectAttributes redirectAttributes, javax.servlet.http.HttpServletRequest request) {
...doStuff();
}
@RequestMapping(value = "/{key}/edit", method = RequestMethod.GET)
public String edit(ModelMap model, @PathVariable String key) {
...doStuff();
return "home/edit";
}
@RequestMapping(method = RequestMethod.PATCH)
public String update(@ModelAttribute("modelAttribute") @Valid AddForm addForm, Errors errors, Model model, RedirectAttributes redirectAttributes, HttpServletRequest request) throws ObjectNotFoundException {
...doStuff();
}
}
Add.jsp
<tiles:insertDefinition name="base">
<tiles:putAttribute name="title"><fmt:message key=".home.Controller.add.title" /></tiles:putAttribute>
<tiles:putAttribute name="content">
<form:form action="/home" method="POST" modelAttribute="modelAttribute">
<div class="half-page">
<%@ include file="_form.jsp" %>
<div class="row">
<input type="submit" name="submit" value="<fmt:message key=".home.Controller.add.action.submit" />" class="button" />
</div>
</div>
</form:form>
</tiles:putAttribute>
<tiles:putAttribute name="javascripts">
<script type="text/javascript" src="<c:url value="/resources/js/home/_form.js" />"></script>
</tiles:putAttribute>
</tiles:insertDefinition>
Edit.jsp
<tiles:insertDefinition name="base">
<tiles:putAttribute name="title">
<fmt:message key=".home.Controller.edit.title">
<fmt:param value="${itemAddForm.item.name}" />
</fmt:message>
</tiles:putAttribute>
<tiles:putAttribute name="sectionNavigation" type="template" value="/WEB-INF/views/home/" />
<tiles:putAttribute name="content">
<form:form action="/home" method="PATCH" modelAttribute="modelAttribute">
<div class="half-page">
<%@ include file="_form.jsp" %>
<div class="row">
<input type="submit" name="submit" value="<fmt:message key=".home.Controller.edit.action.submit" />" class="button" />
</div>
</div>
</form:form>
</tiles:putAttribute>
<tiles:putAttribute name="javascripts">
<script type="text/javascript" src="<c:url value="/resources/js/home/_form.js" />"></script>
</tiles:putAttribute>
</tiles:insertDefinition>
Share
Improve this question
asked Mar 26 at 19:42
MustyProgrammerMustyProgrammer
11 bronze badge
1
|
2 Answers
Reset to default 1These are pretty old versions, and it might be an incompatibility between spring boot version, spring version, and other dependency versions.
I suggest to let spring-boot-starter-parent manage dependencies.
Add it as your parent:
<parent>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<parent>
or in your dependency management section.
<dependencyManagement>
<dependencies>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<type>pom</type>
<scope>import</scope>
<dependency>
</dependencies>
</dependencyManagement>
Then remove all the versions you have in your <dependencies> section.
Also, a good ideea is to use the maven-enforcer-plugin to make sure you don't have dependency convergence errors.
For example: your version of spring-data-commons has a dependency on spring 5.1.13, while you use 5.3.39.
If you really want to increase the version of spring security, try to see which version of spring-boot has that spring security version.
Also, when upgrading versions outside of spring-boot, you need to be careful as if it's not a patch version increase, then you risk to break the application.
For starters I would suggest to use the proper way to override dependency versions. Which is by just specifying the right property and let Spring Boot (the plugin) handle the managing of the versions.
Don't do manual dependency management for dependencies managed by Spring Boot or its starters (see my previous point). With that you are making things hard for dependency management and will lead to conflicting versions.
You have a GetMethodConvertingFilter
which turns every forward into a GET, when using Spring Security things might be an internal forward in which this filter kicks in and breaks the method. So I would suggest to remove this.
<parent>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<parent>
<properties>
<spring-framework.version>5.3.39</spring-framework.version>
<spring-security.version>5.8.16</spring-security.version>
<tomcat.version>9.0.102</tomcat.version>
<properties>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
Your own GetMethodConvertingFilter
probably also plays a role especially when using Spring Security. So I would suggest to remove it or make it smarter to determine when to do the forward (although that would be tricky as it probably executes before the security chain).
本文标签:
版权声明:本文标题:java - Spring Boot web app is processing incorrect POST requests after SpringSpring Boot upgrade - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744129137a2592089.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
.springframework
dependencies those are already pulled in by Spring Boot. The same for the.springframework.security
and.springframework.data
dependencies. You are fighting the dependency management. That being said you have a filter that makes everything a GET request. If you want to upgrade certain versions then do it in a proper way. – M. Deinum Commented Mar 27 at 7:07