0
votes

I am trying to save a response I get in a callback function of a third party JS function (of a payment gateway) to my server using an AJAX POST but I am getting a status code of - 405 Method Not Allowed. The Response I get back is - message : Request method 'POST' not supported, description : The specified HTTP method is not allowed for the requested resource.

I have the following Javascript Client code -

gateway.pay(token, {
                    onSuccess : function(result) {
                        console.log(result);
                        saveTransactionResult(result);
                    },
                    onError : function(result) {
                        //
                    },
                    onClose : function() {
                        //
                    }
                });
.....

saveTransactionResult : function(result) { // save result to server.
    $.ajax({
        headers : {
            Accept : "text/plain",
            "Content-Type" : "application/json"
        },
        url : "/myshop/checkout/payment/complete",
        type : "POST",
        data : JSON.stringify(result),
        dataType : "text",
        success : function(data) {
            console.log(data);
            console.log("Payment Details have been saved!");
        },
        error : function() {
            console.log("an error has occurred while posting response!");
        }
    });
}

Corresponding Server-side Spring MVC Handler -

@ResponseBody
@RequestMapping(value = "/complete", method = RequestMethod.POST, consumes =
{ "application/json" })
public String saveResponse(@RequestBody final ResponseData responseData)
{
    final boolean result = paymentService.savePayment(responseData);
    if (result)
    {
        return "Success";
    }
    else
    {
        return "Failure";
    }
}

Most of the answers on StackOverflow for "405 Method not allowed" lead to issues related to Cross Domain Requests but I am not sure if my request here is a cross browser request as it is being called by local code written in the callback of a third party js. If that's not the case, is it simply an issue with my request headers or the Ajax formulation? Does an Ajax request has to have a handler with the annotation @ResponseBody, even if it functionally does not want to return anything specific to the browser?

Another thing I noticed on analysis of this Ajax request on Chrome was that a CSRFToken was getting appended to the Request Payload in the following manner automatically -

{"response":"from","payment":"gateway"}&CSRFToken=7c4c40-b4c4c-44cd3-94c4-a44c437

Is this what may be causing the issue? If so, do I need to do any specific handling for this CSRF Token in my request handler?

PS - I have added the ajax "headers" as I was earlier getting a 415 (unsupported media type) status code without them, as suggested in this post - POST JSON fails with 415 Unsupported media type, Spring 3 mvc but now I have a 405!

1
You post to url "/myshop/checkout/payment/complete", but controller mapping value is "/complete". May be, problem is here?Bor Laze
No that's not the issue. "/complete" is the mapping of my handler method. The rest of the url is taken care of by the RequestMapping on top of my Controller.Anurag

1 Answers

0
votes

So the issue was with my request getting filtered out in the org.springframework.security.web.csrf.CsrfFilter, as the CSRF Token was not found server-side in both the Request headers or the Request Parameters.

At the Client-side, the CSRFToken in the request payload was getting appended by a generic application-wide $.ajaxPrefilter function, which is responsible for adding the CSRFToken to all POST Ajax requests throughout the application in a different manner corresponding to the content-type of the payload. Looking at this function I realize that the general rule of thumb is to append the CSRFToken in the "data" field of the AJAX request if the data is of the type window.FormData, and add the CSRFToken to the header of the Ajax request if the data is of the type "application/json" as follows -

jqXHR.setRequestHeader('CSRFToken', ACC.config.CSRFToken);

By explicitly specifying the Ajax parameter contentType to application/json, my ajaxPrefilter was able to correctly put the CSRFToken in the Request Header, instead of appending it along with the JSON request payload. Following request made it work -

saveTransactionResult : function(result) { // save result to server.
    $.ajax({
        headers : {
            Accept : "text/plain",
            "Content-Type" : "application/json"
        },
        url : "/myshop/checkout/payment/complete",
        type : "POST",
        data : JSON.stringify(result),
        dataType : "text",
        contentType : "application/json; charset=utf-8",
        success : function(data) {
            console.log(data);
            console.log("Payment Details have been saved!");
        },
        error : function() {
            console.log("an error has occurred while posting response!");
        }
    });
}