Spring MVC - Request method 'POST' not supported

Kaz picture Kaz · Sep 9, 2015 · Viewed 10.8k times · Source

I am learning Spring MVC (with Thymeleaf) while porting over a JBoss Seam website to Spring MVC.

I am trying to replace a HTTPServlet (doPost to /myservlet) with a Spring Controller with the following code:

@RequestMapping(value="/myservlet", method = RequestMethod.POST)
public String executeAction(HttpServletRequest request, HttpServletResponse response) throws IOException {

     StringBuilder buffer = new StringBuilder();
     BufferedReader reader = request.getReader();

     String line;
     while((line = reader.readLine()) != null) {
         buffer.append(line);
     }

     String payload = buffer.toString();
     System.out.println("payload: " + payload);
     return "/index";
}

This method needs to read the XML Payload (String) sent via a HTTP Post to this endpoint.

When the external client (.NET - which will be used in the live environment) invokes this I get the following log messages:

[org.springframework.web.servlet.PageNotFound] (default task-3) Request method 'POST' not supported
[org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-3) Handler execution resulted in exception: Request method 'POST' not supported

I have also tried this as a HTTPServlet but with the same problem. Can someone please advise as to what I am doing wrong?

The web.xml contents are:

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <error-page>
        <error-code>404</error-code>
        <location>/404.html</location>
    </error-page>

  <!-- Send unauthorised request to the 404 page -->
  <error-page>
    <error-code>403</error-code>
    <location>/404.html</location> 
  </error-page>

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/root-context.xml</param-value>
    </context-param>

    <!-- Creates the Spring Container shared by all Servlets and Filters -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Processes application requests -->
    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
        /WEB-INF/spring/appServlet/servlet-context.xml
      </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

  <!-- Spring Security -->
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

  <listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
  </listener>

</web-app>

The xml payload:

<Jobs>
    <Job Action="Post">
        <AdvertiserName>Advertiser1</AdvertiserName>
        <AdvertiserType ValueID="15897">15897</AdvertiserType>
        <SenderReference>01111111</SenderReference>
        <DisplayReference>DISPLAYREF_635346301296069467_4445_Test89
        </DisplayReference>
        <Classification ValueID="6211">1002915</Classification>
        <SubClassification></SubClassification>
        <Position><![CDATA[CASE MANAGER]]></Position>
        <Description><![CDATA[ The Case Manager role is the vital link between all parties within the mortgage application process. ...]]></Description>
        <Country ValueID="246">United Kingdom</Country>
        <Location ValueID="12096">Yorkshire</Location>
        <Area ValueID="107646">Bradford</Area>
        <PostalCode>BD1 1EE</PostalCode>
        <ApplicationURL>http://removed.com/Application.aspx?uPjAaXJ9HmZ04+4i/bqmFAz
        </ApplicationURL>
        <Language ValueID="120036">2057</Language>
        <ContactName>Joe Bloggs</ContactName>
        <EmploymentType ValueID="2163">2163</EmploymentType>
        <StartDate></StartDate>
        <Duration></Duration>
        <WorkHours ValueID="2190">2190</WorkHours>
        <SalaryCurrency ValueID="1078">1007000</SalaryCurrency>
        <SalaryMinimum>16200.00</SalaryMinimum>
        <SalaryMaximum>16200.00</SalaryMaximum>
        <SalaryPeriod ValueID="2178">1007600</SalaryPeriod>
        <JobType>APPLICATION</JobType>
    </Job>
</Jobs>

Answer

Kaz picture Kaz · Sep 9, 2015

In case anyone else has this issue, the problem is related to the method signature and CSRF.

I got around this by following geoand's advice (thanks) by changing the method signature to add @RequestBody String payload

And by disabling CSRF for the specific URL (/myservlet) but leaving it enabled for the other URL's using the following in the spring security config:

<http auto-config="true" use-expressions="true" pattern="/myservlet" >
  <csrf disabled="true"/>
</http>

<http auto-config="true" use-expressions="true" >    
  <access-denied-handler error-page="/403" />
  <form-login login-page="/login.html" authentication-failure-url="/login-error.html" authentication-success-handler-ref="customAuthenticationSuccessHandler" />
  <logout logout-success-url="/index" />
  <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
  <intercept-url pattern="/advertiser/**" access="hasRole('ROLE_ADVERTISER')" />
  <csrf disabled="false"/>
</http>

Thank you all for your replies/comments.

Kaz