(C# ASP.NET Core 5 Ajax) How to POST data with AJAX Independently of (i.e., without) a Form and hence solve the HTTP Bad Request Error 400

Anti-forgery token is NOT available outside of <form> tags. This article, alongwith the appended video, explains how to use IAntiforgery service to solve this limitation, and POST data, especially as an AJAX request, when a full-fledged form is not possible?

Categories | About |     |  

Parveen,

Follow the steps explained next.

Explanation of the Problem

When a <form> consisting of various <input> tags is requested for a razor page, then the ASP.NET Core engine appends an additional <input type="hidden "> tag and uses it to attach an anti-forgery token. You can examine the "source" of any such page for a secretly added hidden <input>. This token is used as a protection against XSRF attacks.

There are times when we need to POST data to a server, but it cannot be wrapped inside a <form>, and therefore, the anti-forgery token remains un-available. The server side application treats such a request as malicious and rejects it with an error code of HTTP 400. So, how to handle this situation?

The Solution

The recommended solution is to artificially attach the RequestForgeryToken as a header to your AJAX request.

Step 1: Obtain IAntiforgery Service
Use dependency injection in the razor page to obtain access to the IAntiforgery service.
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
Step 2: Call GetAndStoreTokens... to get the token
Call the GetAndStoreTokens method to get the request token thus:
@Xsrf.GetAndStoreTokens(HttpContext).RequestToken
Step 3: Send it in the AJAX Request Header
headers:
  {

    "RequestVerificationToken": "@..."
  }

Markup of the Index.cshtml Razor Page

Following is the markup for the razor page. Notice that we send the request verification token as a request header.

@page

@model Ajax.Pages.IndexModel

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@*
  Technique:
  =========

  the anti-forgery token can be created using a service from
  within the view

  How to do:
  =========

  Inject the Microsoft.AspNetCore.Antiforgery.IAntiforgery service
  into the view and call GetAndStoreTokens. see the javascript
  below for this

*@

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf

<a href="javascript:" id="aSend">Click to POST v1=2</a>

<script type="text/javascript">

  (function () {

    aSend.addEventListener("click", function () {

      // suppose the data to be posted 
      // is a name value pair 
      let dataToSend = { v1: "2" };

      fetch("/",
        {
          method: "POST",

          body: JSON.stringify(dataToSend),

          headers:
          {
            "RequestVerificationToken":
              "@Xsrf.GetAndStoreTokens(HttpContext).RequestToken"
          }

        }
      )
        // if any exceptions - log them 
        .catch(err => console.log("network error: " + err))

        .then(response => {

          // read json from the response stream 
          response.json().then(data => {

          //  do anything - we are showing it 
          // in an alert popup 
            alert("Server responded: " + data);

          });
        });
    });
  })();

</script>

Code of the Index.cshtml.cs file

IMPORTANT: The Startup.cs file must be configured to mark razor pages as the end-points. Otherwise this code will never run. Or, watch the starting few minutes of the accompanying video on how to do it.

Following is the index.cshtml.cs backing class. The OnPost method accepts the AJAX request with the parameter v1. It responds by sending a customary OK message that is later shown as a pop-up alert on the client side.

using Microsoft.AspNetCore.Mvc;

using Microsoft.AspNetCore.Mvc.RazorPages;

using System;

using System.Threading.Tasks;

namespace Ajax.Pages
{

  public class IndexModel : PageModel
  {

    public async Task<JsonResult> OnPost(String v1)
    {

      // simulate some delay 
      await Task.Delay(100);

      return new JsonResult("OK");

    }

  }

}

Video Explanation with a Working Example

Please watch the following youtube video:


This Blog Post/Article "(C# ASP.NET Core 5 Ajax) How to POST data with AJAX Independently of (i.e., without) a Form and hence solve the HTTP Bad Request Error 400" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Comments and Discussion