Ajax POST results in a 405 (Method Not Allowed) - Spring MVC

rmpt picture rmpt · Dec 13, 2015 · Viewed 13.6k times · Source

I'm trying to make an ajax call to my Spring controller/action with POST method, and return an object from the server with @ResponseBody. The strange situation is that it stop working after add spring security layer, everything was working fine before. I will try to explain my moves to solve the problem and then show you the code/captures/etc.

1. After some research I found some answers telling the problem might be related with csrf mechanism, so I disabled it and still have the issue. (spring-security.xml bellow)

2. I've made a wireshark capture to check the request/response. My ajax request is OK, my controller declaration is OK, but I don't understand why, the 405 response indicates > Allow: GET (capture bellow)

3. I've tried to access my controller action through the browser (i.e., make an GET request), and I get the error HTTP Status 405 - Request method 'GET' not supported!

4. I've tried to change the RequestMapping(method...) to RequestMethod.GET and the request arrives to the controller and works fine, but I don't want it to work on GET method, I want a POST request.

5. Changed the RequestMapping(consumes, produces, headers) to accept all kind of data, but still 405...

This is driving me crazy! I post my files bellow, so you can check it guys, any tip will be appreciated. Thanks! (IMPORTANT NOTE: this is my despair configuration)

spring-security.xml

<beans:beans 
     xmlns...(all needed declarations)>

<http pattern="/js/**" security="none" />
<http pattern="/css/**" security="none" />

<!-- enable use-expressions -->
<http auto-config="true" >
    <access-denied-handler error-page="/403" />
    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/login" access="isAnonymous()" />
    <intercept-url pattern="/403" access="permitAll" />
    <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />

    <form-login  login-page="/login"
                 username-parameter="email"
                 password-parameter="password"
                 authentication-failure-url="/login?failed" />

    <!--
    <csrf/>
    -->
</http>

 ..... (authentication)  

AdminController.java

@Controller
@RequestMapping("/admin**")
public class AdminController {

    ... (all my autowired beans)

    @RequestMapping(
        value = "/events/loadEvents",
        method = RequestMethod.POST,
        consumes = MediaType.ALL_VALUE,
        produces = MediaType.ALL_VALUE,
        headers = "Accept=*/*")
    @ResponseBody
    public Event loadEvents(@RequestParam("parentId") long parentId) {
        ... (my logic)
        return event;
    }
}

Request (wireshark capture) HTTP Request (link smudged because I've used a simplified on my question)

Response (wireshark capture) enter image description here

EDIT jquery ajax call code

$.ajax({
    type: 'POST',
    cache: false,
    url: /admin/events/loadEvents,
    data: { parentId: 1 },
    dataType = 'json',
    contentType = 'application/json',

    ...
});

Answer

rmpt picture rmpt · Dec 27, 2015

After many hours of research and tests, I finally got it, ant it was a (very very) stupid situation. So, in my question I said

so I disabled it (csrf on spring-security.xml) and still have the issue.

No, I didn't disabled it. I was trying to disable it doing

<!--
<csrf/>
-->

But I should be doing:

<csrf disabled="true"/>

Commenting csrf tag does NOT disable csrf, this is because csrf is enabled by default! After find the problem is really easy to say that is a stupid mistake, but as I added csrf tag to enable it, I thought that commenting it would disable it. Find the answer on Spring Documentation

Now, back into my problem. To fix the 405 error message in a POST AJAX call WITH CSRF ENABLED, it was really easy. I keep the csrf parameters in JS variables like this:

<script type="text/javascript">
    var csrfParameter = '${_csrf.parameterName}';
    var csrfToken = '${_csrf.token}';
</script>

and then my ajax call looks like this:

var jsonParams = {};
jsonParams['parentId'] = 1;
jsonParams[csrfParameter] = csrfToken;
$.ajax({
    type: 'POST',
    cache: false,
    url: /admin/events/loadEvents,
    data: jsonParams,
    dataType = 'json',
    contentType = 'application/json',

    ...
});

Working like a charme. Hope that helps someone in the future.