LMKN
LMKN

Reputation: 111

ASPNET CORE Ajax Post results in 400 Bad Request

I am working on ASP.NET Zero which is built on ASP.NET Core. I was getting a bad request error when I used KendoUI Upload control on one of my pages. After lot of research and investigation, I realized the HTTP POST Ajax request is failing with 400 bad request error. My code samples below have some commented line for other scenarios I test. None of the existing posts in stack over flow solved my issue. I Below is my ajax call:

 $.ajax({
            url: "/test/TestCall",
            type: 'Post',
           /* data: JSON.stringify({ "Param1": "test" }),
            dataType:"json",
            processData: false,  */// tell jQuery not to process the data
            contentType: "application/json",  // tell jQuery not to set contentType
            success: function (result) {
                var res = result;
            },
            error: function (jqXHR) {
                var z = 3;
            },
            complete: function (jqXHR, status) {
                var x = 10;
            }
        });

My Controller code is: I also tried without extending from MyTestProjectControllerBase and just using the Controller base class. It did not work.

public class TestController : MyTestProjectControllerBase
{
    public IActionResult Index()
    {
        return View();
    }

   [HttpPost]
    public ActionResult TestCall()
    {
        //return Content("Name is:" );
        return new ContentResult() { Content = "test" };
    }
}

What am I missing? I tried using postman and I see this additional information 'Request cannot be fulfilled due to bad syntax'

could not figure it out after spending good 8 hours on this issue. Not sure if the issue is with Asp.net core or asp.net zero. Any pointers would be greatly appreciated.

Update after checking the comments by shyju: Startup.cs file has the following code that enables AntiForgeryTokenAttribute

 services.AddMvc(options =>
        {
            options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
        }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Updates to the ajax call and view based on answer by shyju:

  $("#backBtn").on("click", function (e) {
        var t = $("input[name='__RequestVerificationToken']").val();
        $.ajax({
            url: "/test/TestCall",
            type: 'Post',
           /* data: JSON.stringify({ "Param1": "test" }),
            dataType:"json",
            processData: false,  */
            contentType: "application/json",  
            headers: {
                "RequestVerificationToken": t
            },
            success: function (result) {
                var res = result;
            },
            error: function (jqXHR) {
                var z = 3;
            },
            complete: function (jqXHR, status) {
                var x = 10;
            }
        });
    });

My view looks like this now:removed rest of the html

<div id="container">
    @Html.AntiForgeryToken()
    <div class="k-edit-field label">Vendor Name</div>
</div

Upvotes: 10

Views: 18524

Answers (4)

Pramesh
Pramesh

Reputation: 1244

In Net Core RazorPages, you will have to pass AntiForgeryToken with JQuery Ajax Post request. if you have a Form tag with Method=Post then you do not have to worry about sending it.

$.ajax({
    type:"post",
    url:"?Handler=Update",
    dataType:"json",
    beforeSend: function(xhr){
      xhr.setRequestHeader("XSRF-TOKEN",
        $('input:hidden[name="__RequestVerificationToken"]').val());
    },
    success: function(result){
      // do something
    }
});

Then in your Razor Page

@using Microsoft.AspNetCore.Antiforgery
@inject IAntiforgery antiforgery
@{
   var token = antiforgery.GetAndStoreTokens(HttpContext).RequestToken;
}

Be sure to add

@Html.AntiForgeryToken()

Then in your StartUp.cs inside ConfigureServices method, add

services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");

Upvotes: 3

Dana
Dana

Reputation: 729

In Core -- make sure your <form> tag includes method="post".

The method is required to invoke the form tag helper which automatically adds the antiforgery token to the form. (I accidentally left off the method and didn't notice because I was ajax-posting it.)

Upvotes: 3

Edward
Edward

Reputation: 30056

Try to specify the header with X-XSRF-TOKEN.

For ABP Intercept XMLHttpRequest.

Since all libraries use JavaScript's native AJAX object, XMLHttpRequest, you can define a simple interceptor to add the token to the header:

(function (send) {
    XMLHttpRequest.prototype.send = function (data) {
        this.setRequestHeader(abp.security.antiForgery.tokenHeaderName, abp.security.antiForgery.getToken());
        return send.call(this, data);
    };
})(XMLHttpRequest.prototype.send);

For abp.security.antiForgery.tokenHeaderName, its default value is X-XSRF-TOKEN

Upvotes: 0

Shyju
Shyju

Reputation: 218892

Looks like you have AutoValidateAntiforgeryTokenAttribute filter applied globally. That means when an HTTP Post action method is called(normal or ajax), the framework will check the submitted request data and if it does not find a valid anti forgery token(RequestVerificationToken header), it will be considered a bad request and a 400 response will be sent back.

To fix this problem, you can explicitly read the value of __RequestVerificationToken hidden input (generated by the form tag helper) and send that in your ajax request headers.

var t = $("input[name='__RequestVerificationToken']").val();

$.ajax({
    url: "/test/TestCall",
    type: 'Post',
    headers:
    {
        "RequestVerificationToken": t
    },
    success: function (result) {
        alert("Success");
        var res = result;
    },
    error: function (jqXHR) {
        var z = 3;
    },
    complete: function (jqXHR, status) {
        var x = 10;
    }
});

You can make the code more robust by injecting the IAntiforgery implementation to the view/page and using the GetAndStoreTokens method.

Add this to your view

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
public string GetAntiXsrfRequestToken()
{
    return Xsrf.GetAndStoreTokens(Context).RequestToken;
}
}

and call this GetAntiXsrfRequestToken function to get the value in your javascript ( which is inside the view file)

headers:
{
    "RequestVerificationToken": '@GetAntiXsrfRequestToken()'
},

Upvotes: 17

Related Questions