As someone pointed out in a comment, Razor pages doesn't need Controllers, like you're used to do in MVC. I also now Razor doesn't have a native handling of the button click event. To do something (in the "code-behind") when the user click a button we have at least two options:
I find a LOT of examples for MVC that show how to define the Controller functions. But as said, I don't have one.
Hence I'm trying to understand both ways. Here my test cshtml:
<form method="post">
<input type="submit" class="btn btn-primary" value="way1">Way 1/>
</form>
<button id="btn" type="button" class="btn btn-primary" value="way2">Way 2</button>
<script>
$(document).ready(function() {
$("#btn").click(function (e) {
e.preventDefault();
$.ajax({
url: "@Url.Action("Way2")",
type: "POST",
data: "foo",
datatype: "text",
success: function (data) {
alert(data);
}
});
return false;
});
});
</script>
and here the cshtml.cs:
public class TestModel : PageModel { private readonly MyContext _context;
public TestModel(MyContext context)
{
_context = context;
}
public IActionResult OnPost()
{
// here I can put something to execute
// when the submit button is clicked
}
public ActionResult Way2(string data)
{
// this is never triggered
}
With the "Way1" (submit) I'm able to catch the click of a button, but with some drawbacks. By default the page is reloaded due to the post. Sometimes I don't need to change anything, but just call a C# function. But I don't understand how to handle multiple buttons. I.e. if I have 5 buttons, how to do something different for each one?
With the "Way2" I did something wrong because the function in cs code is never reached and I get a Bad Request error (400). What did I miss?
Normal form submit
When doing a normal form submit, As per the convention, you need the handler method name to follow On{{HttpVerb}}{{YourHanderName}}
format
public ActionResult OnPostWay2(string data)
{
// to do : return something
}
Now make sure you have your submit button inside a form
tag and you mentioned the asp-page-handler
. The value of that attribute should be your handler name in the page model class (Way2
)
<form method="POST">
<button type="submit" asp-route-data="foo" asp-page-handler="Way2">Way 2</button>
</form>
The above code will generate the markup for button with a formaction
attribute which is set to the url yourBaseUrl/YourPageName?data=foo&handler=Way2
.
When the use clicks on the submit button, it will post the form this url because formaction attribute value will override the default action url of the form. When the request is received, the razor pages framework will use this parameter (handler
) and direct the request to the corresponding handler method.
Ajax call
You are getting a 400 (Bad Request) response because the framework expects the RequestVerificationToken
as part of the posted request data. If you check the view source of the page, you can see a hidden input element with name __RequestVerificationToken
inside the form. The framework uses this to prevent possible CSRF attacks. If your request does not have this information, the framework will return the 400 bad request.
To make your ajax code works, all you have to do is, send this explicitly. Here is a working sample
$("#btn").click(function(e) {
e.preventDefault();
var t = $("input[name='__RequestVerificationToken']").val();
$.ajax({
url: $(this).attr("formaction"),
headers:
{
"RequestVerificationToken": t
},
type: "POST",
data: { data: 'foo2' },
}).done(function(data) {
console.log(data);
}).fail(function(a, v, e) {
alert(e);
});
});
Now since you are making an ajax call, it makes sense to return a json response
public ActionResult OnPostWay2(string data)
{
return new JsonResult("Received "+ data + " at "+DateTime.Now);
}
In the above example, we are using some jQuery code to get the input element with name __RequestVerificationToken
and reading the value of it. A more robust approach would be injecting the IAntiforgery
implementation to the view and using the GetAndStoreTokens
method.
@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@functions{
public string GetAntiXsrfRequestToken()
{
return Xsrf.GetAndStoreTokens(Model.HttpContext).RequestToken;
}
}
<script>
$(function () {
$("#btn").click(function(e) {
e.preventDefault();
var t = '@GetAntiXsrfRequestToken()';
$.ajax({
url: $(this).attr("formaction"),
headers:
{
"RequestVerificationToken": t
},
type: "POST",
data: { data: 'foo2' },
}).done(function(data) {
console.log(data);
}).fail(function(a, v, e) {
alert(e);
});
});
})
</script>
In addition to the jQuery code shown that handles the click, you additionally have to include the name of the function in the asp-page-handler in order to route to the right function. If you don't have an asp-page-handler, the request will end up in the default OnPost() function. You want it to go to the OnPostWay2 function.
Code:
<button id="btn" type="button" class="btn btn-primary" value="way2" asp-page-handler="Way2">Way 2</button>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With