ASP.NET Core 2, button click with Razor pages without MVC

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>

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>