I've a onpost method in .net core pagemodel class like below
public void OnPostLoad(string TZ)
{
///..statements..////
}
and i need to call the above method from my razor page - jquery ready function
i've tried like below but it's not worked for me
$(document).ready(function(){
$.post(base_url+"/Transactions/Index?handler=Load="+{TZ:timezone }, response => {
alert("test timezone");
});
});
please let me know if you have any solutions for it. thanks
Request Verification is baked into the Razor Pages framework. If you use ajax to post data, it needs anti-forgery token verification.
1.If your page contains form. The form tag helper injects a hidden form field named __RequestVerificationToken at the end of every form with an encrypted value representing the token. You just need add headers for this token:
<form method="post">
//...
</form>
#section Scripts
{
<script>
$(document).ready(function(){
var timezone = "aaa";
$.ajaxSetup({
headers: {
RequestVerificationToken:$('input:hidden[name="__RequestVerificationToken"]').val()
}
});
$.post(base_url+"/Transactions/Index?handler=Load&TZ=" + timezone ,response => {
alert("test timezone");
});
});
</script>
}
2.If your page does not contain any form, you can turn off anti-forgery token verification and then you no need add RequestVerificationToken header any more:
In ASP.NET 6
builder.Services.AddRazorPages().AddRazorPagesOptions(o =>
{
o.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute());
});
In ASP.NET Core 3.x or .NET 5
services.AddRazorPages().AddRazorPagesOptions(o =>
{
o.Conventions.ConfigureFilter(new IgnoreAntiforgeryTokenAttribute());
});
3.If your page does not contain form and you also do not want to turn off anti-forgery token verification, you can automatically add #Html.AntiForgeryToken():
#Html.AntiForgeryToken()
#section Scripts
{
<script>
$(document).ready(function(){
var timezone = "aaa";
$.ajaxSetup({
headers: {
RequestVerificationToken:$('input:hidden[name="__RequestVerificationToken"]').val()
}
});
$.post(base_url+"/Transactions/Index?handler=Load&TZ=" + timezone ,response => {
alert("test timezone");
});
});
</script>
}
Related
Some of my controller methods have the [ValidateAntiForgeryToken] attribute for the usual reasons. It's only app-internal actions, no cross-site API or similar. Now I replaced a request made from JavaScript from jQuery (which seems to send data as form fields) with a real JSON post using fetch() directly. The __RequestVerificationToken field was among the sent data so it must have ended up in a place where ASP.NET Core MVC was looking for it.
Now it's in the JSON body, there are no form fields anymore. And the request fails with code 400, probably due to the missing (not found) token.
I've searched for solutions but this has so far only been covered for the older non-Core ASP.NET from 10 years ago. Is it still possible today with current tools to send the token in the JSON body or as HTTP header (I'm fine with either one) and have it validated without much boilerplate code? I can add a special attribute class for that if needed. I already looked at the framework class but it doesn't do anything, this must be handled elsewhere.
Below is a work demo, you can refer to it. Read this to know more.
1.Customize AntiforgeryOptions in Program.cs:
builder.Services.AddAntiforgery(options =>
{
// Set Cookie properties using CookieBuilder properties†.
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
});
2.Require antiforgery validation
public IActionResult Index()
{
// ...
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Privacy()
{
// ...
return View();
}
Index.cshtml:
#inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
#{
ViewData["Title"] = "JavaScript";
var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
<input id="RequestVerificationToken" type="hidden" value="#requestToken" />
<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>
#section Scripts {
<script>
document.addEventListener("DOMContentLoaded", () => {
const resultElement = document.getElementById("result");
document.getElementById("button").addEventListener("click", async () => {
const response = await fetch("#Url.Action("Privacy")", {
method: "POST",
headers: {
RequestVerificationToken:
document.getElementById("RequestVerificationToken").value
}
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
});
});
</script>
}
result:
I'm working with an ASP.NET 6 app, generated with ASP.NET Core with React.js Visual Studio 2022 template. I've used Individual Accounts as Authentication Type when creating the project, so all Identity stuff has been nicely generated.
Now I have nice Razor views scaffolded by ASP.NET's Identity. However, I'd like to build my whole UI as React SPA application, using react-router. It means that I don't want to use Razor views, but still use ASP.NET's Identity backend.
Firstly, I wanted to implement a React form to submit changing the user password. Razor view generated for that is Identity/Pages/Account/ManageChangePassword.cshtml. It looks like that:
As soon as I submit this Razor form, the request looks as follows:
with the following payload:
So now, I basically rebuilt this form in React:
import React, { useState } from "react";
import Button from "react-bootstrap/Button";
import Form from "react-bootstrap/Form";
export const ChangePassword = () => {
const [currentPassword, setCurrentPassword] = useState<string>("");
const [newPassword, setNewPassword] = useState<string>("");
const [newPasswordConfirm, setNewPasswordConfirm] = useState<string>("");
const onChangePasswordFormSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append("Input.OldPassword", currentPassword);
formData.append("Input.NewPassword", newPassword);
formData.append("Input.ConfirmPassword", newPasswordConfirm);
fetch("Identity/Account/Manage/ChangePassword", {
method: "POST",
body: formData,
});
};
return (
<Form onSubmit={onChangePasswordFormSubmit}>
<Form.Group className="mb-3" controlId="currentPassword">
<Form.Label>Current password</Form.Label>
<Form.Control
type="password"
placeholder="Current password"
value={currentPassword}
onChange={(e) => setCurrentPassword(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="newPassword">
<Form.Label>New password</Form.Label>
<Form.Control
type="password"
placeholder="New password"
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="newPasswordConfirm">
<Form.Label>Confirm new password</Form.Label>
<Form.Control
type="password"
placeholder="Confirm new password"
value={newPasswordConfirm}
onChange={(e) => setNewPasswordConfirm(e.target.value)}
/>
</Form.Group>
<Button variant="primary" type="submit">
Change password
</Button>
</Form>
);
};
However, when submitting this form, I'm getting a HTTP 400 error:
the payload looks good at the first sight:
but I noticed that I'm missing the __RequestVerificationToken in this payload.
I guess it's coming from the fact that Identity controllers (to which I have no access) must be using [ValidateAntiForgeryToken] attribute.
If I change my form's submit code to add this payload parameter manually:
const formData = new FormData();
formData.append("Input.OldPassword", currentPassword);
formData.append("Input.NewPassword", newPassword);
formData.append("Input.ConfirmPassword", newPasswordConfirm);
formData.append(
"__RequestVerificationToken",
"CfDJ8KEnNhgi1apJuVaPQ0BdQGnccmtpiQ91u-6lFRvjaSQxZhM6tj8LATJqWAeKFIW5ctwRTdtQruvxLbhq2EVR3P1pATIyeu3FWSPc-ZJcpR_sKHH9eLODiqFPXYtdgktScsOFkbnnn5hixMvMDADizSGUBRlSogENWDucpMgVUr3nVMlGwnKAQDH7Ck4cZjGQiQ"
);
fetch("Identity/Account/Manage/ChangePassword", {
method: "POST",
body: formData,
});
};
It works fine and the request arrives correctly.
My question is: where to get __RequestVerificationToken from? How can I send it to the ASP.NET's Identity controller from a purely React form?
I noticed that when submitting my React form, this value is visible in cookies:
so the React form/browser must somehow know this value? Where does it come from?
Maybe my approach is somehow wrong here? Thanks for advising :)
The AntiForgeryToken is generated by
HtmlHelper.AntiForgeryToken();
AntiForgery.GetHtml();
AntiForgeryWorker.GetFormInputElement();
And is validated by
AntiForgery.Validate();
AntiForgeryWorker.Validate();
I'd send AntiForgery.GetHtml() to the client, and then validate it on the server.
Maybe you can even create an ajax endpoint that returns new tokens to the Client.
I suspect your are missing the cookie, can you please check if you configured your cookies for your anti forgery in your
public void ConfigureServices(IServiceCollection services)
{
// make sure you add the cookie name
services.AddAntiforgery(o => {
o.Cookie.Name = "X-CSRF-TOKEN";
});
}
or more from here on Microsoft Ref, theres stuff on doing it for SPA apps all well further below
builder.Services.AddAntiforgery(options =>
{
// Set Cookie properties using CookieBuilder properties†.
options.FormFieldName = "AntiforgeryFieldname";
options.HeaderName = "X-CSRF-TOKEN-HEADERNAME";
options.SuppressXFrameOptionsHeader = false;
});
SPA App
#inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Antiforgery
#{
ViewData["Title"] = "JavaScript";
var requestToken = Antiforgery.GetAndStoreTokens(Context).RequestToken;
}
<input id="RequestVerificationToken" type="hidden" value="#requestToken" />
<button id="button" class="btn btn-primary">Submit with Token</button>
<div id="result" class="mt-2"></div>
#section Scripts {
<script>
document.addEventListener("DOMContentLoaded", () => {
const resultElement = document.getElementById("result");
document.getElementById("button").addEventListener("click", async () => {
const response = await fetch("#Url.Action("FetchEndpoint")", {
method: "POST",
headers: {
RequestVerificationToken:
document.getElementById("RequestVerificationToken").value
}
});
if (response.ok) {
resultElement.innerText = await response.text();
} else {
resultElement.innerText = `Request Failed: ${response.status}`
}
});
});
</script>
}
To Debug it, and see if it works
[IgnoreAntiforgeryToken]
public IActionResult IndexOverride()
{
// ...
return RedirectToAction();
}
iv read several posts about ajax calls and im still confused.
My HomeControler got methods
public async Task<ActionResult> Index(string srchterm)
public async Task Publish(TrendVM trendVm)
I want to call Publish it from index.cshtml
my view is like this
#model IEnumerable<Trend>
<div class="container-fluid post-container">
#if (Model != null)
{
foreach (var trend in #Model)
{
Html.RenderPartial("_Trend",trend);
//button that calls Publish and passes it trend without refreshing the page.
}
}
</div>
is the some razer helper that will generate the request?
Recommended approach
If you have a unique record id for each trend item you are printing, you should use that id to pass it back to your server via ajx.
foreach (var trend in #Model)
{
Html.RenderPartial("_Trend",trend);
#Html.ActionLink("Publish","Publish","Home",new { id=trend.Id},
new { #class="publishLink"})
}
Basically, the above code will render an anchor tag like this for each trend item
Publish
where 450 will be replaced with the actual unique Id you have for trend item. Clicking on the link will open the url in a new page usually. I don't think you want that to happen here. So we will override the default click behaviour and make an ajax call to server.
Add this script to your page
#section Scripts
{
<script>
$(function(){
$("a.publishLink").click(function(e){
e.preventDefault();
var url=$(this).attr("href");
$.post(url,function(response){
alert("Publish completed");
});
});
});
</script>
}
Now we need to make sure our publish method accepts an id and do the processing. So change the Publish method to /Create a new method (and use that method name in our earlier markup in Html.ActionLink call)
public async Task Publish(int id)
{
// using the Id value, do some processing.
}
But if you do not want to change your Publish method signature, what you should be doing is creating a form inside your foreach loop and serialize the form and send it. You need to keep the data you want to send in input form fields. We will keep those in hidden fields for now.
foreach (var trend in #Model)
{
Html.RenderPartial("_Trend",trend);
using(Html.BeginForm("Publish","Home"))
{
#Html.HiddenFor(s=>s.Name)
#Html.HiddenFor(s=>s.TrendCode)
#Html.ActionLink("Publish","Publish","Home",new { id=trend.Id},
new { #class="publishLink"})
}
}
Assuming Name and TrendCode are 2 properties of your TrendVM.
and the javascript will be
#section Scripts
{
<script>
$(function(){
$("a.publishLink").click(function(e){
e.preventDefault();
var _f=$(this).closest("form");
$.post(_f.attr("action"),_f.serialize(),function(response){
alert("Publish completed");
});
});
});
</script>
}
You should write some js code. And use $.ajax() function. Put a button on your View:
<button id="your-submit-button" type="submit">Ajax call</button>
Put empty div somewhere on page where you will put your PartialView:
<div id="your-partial-view-container"></div>
Then put some jquery (you also can use plain old js, but it's easier with jquery) on your page. It's better to put all your js code in #section script {} that defined in your _Layout:
$(document).ready(function() {
$("#your-submit-button").click(function(){
$.ajax({
url: #Url.Action("Publish","Home"), //here you put your controller adress
type: "POST",
dataType: 'html',
data: $("#your-form-with-model-data-id").serialize(), //that's how you get data from your form to send your TrendVM to controller
success: function(data) {
$("#your-partial-view-container").html(data);
}
});
});
});
Now when you click on button your js code should be call controller and response will be added inside your div.
I have a question regarding the calling method from view.
Basically on my view I have 2 links:
1 link : When I click on it, some method should be called and executed, but nothing should change on webpage, so no postback.
2 link: When I click on it, some method should happen and postback can happen, on the same page
In controller I have:
public ActionResult FirstMethod(){ return View();}
public ActionResult SecondMethod(){ return View();}
In view:
#Html.ActionLink("Action 1", "FirstMethod", "Controller");
#Html.ActionLink("Action 2", "SecondMethod", "Controller");
So when I click on both action happens but then i get an error saying cannot find FirstMethod.chtml ..
So is this possible to have one method with postback and another one without? And how to return to the same page ... and not try to get FirstMethod.chtml ..
Following solution is based on AJAX -
Controller -
public class DemoController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult CallMe()
{
return new ContentResult() { Content = "This is Demo " };
}
}
Index.cshtml -
<h2>Index</h2>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript">
$(function () {
$("#Click").click(function () {
$.ajax({
url: "/Demo/CallMe",
type: "GET",
error: function (response) {
alert(response);
},
success: function (response) {
alert(response);
}
});
});
})
</script>
<input type="button" value="Click" id="Click" />
First navigate to /demo/Index, that will display the page with above markup with a button in the page. And when we click on the Click button, we have -
The #Html.ActionLink method basically just forwards you to the specified controller-action, you cannot change this, since this is the purpose of the method.
You have to handle the click client-side, and bind a specific action to it (post some data to a url, and do nothing afterwards). One fairly easy way to do this, is to use jQuery.Post
Example from the above jquery link.
Example: Request the test.php page, but ignore the return results.
$.post("test.php");
Actually, there is no postback concept in asp.net mvc. all interactions with server should via the controller/action.
#Html.ActionLink() method just generate a link(tag a in html) and do nothing. everything happens after you send a request(such as click the link) to controller/action, if you want do nothing when click the link, you'd better use AJAX method like this
#Html.ActionLink("Action 1", "FirstMethod", "Controller", null/*routeValues*/, new { id = "link1Id" });
<script type="text/javascript">
$(function () {
$("#link1Id").click(function () {
$.get("/Contoller/FirstMethod", function () {
//do nothing or alert(something)
});
return false;
});
})
</script>
You can simply return another view after you've done what you wanted in your controller action:
public ActionResult SecondMethod()
{
//do something
return View("FirstMethod");
}
After you've seen this you will most probably be disgusted by the use of magic strings to reference views or controllers and that disgust is completely understandable :)
Then you should look whether something like T4MVC could fit your needs.
I want to save UserName in to valC#
#{
var valC#;
}
<script>
$(document).ready(function () {
var UserName = $(".dir span").text();
UserName.trim().replace("\n", "");
alert(UserName);
});
Please tell how to save value from java-script variable to a variable of C#
</script>
If you need to push a value from client side (JavaScript) to server side (your C# code is executed on the server) you need to send it to the server in some fashion.
$(document).ready(function () {
var UserName = $(".dir span").text();
UserName.trim().replace("\n", "");
$.post("#Url.Action("SaveUserName")", { username: UserName });
});
This will post the entered username to the Action SaveUserName on the same
controller that your current view was activated from. So on your controller
you will need the following Action
public ActionResult SaveUserName(string username)
{
// This is where you would save the username on the serverside
}