Video Explanation
(code given after this video)
Please watch the following youtube video:
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"); } } }
This Blog Post/Article "(C# ASP.NET Core 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.