Handler methods in Razor Pages are methods that are automatically executed as a result of a request, implicitly returning a PageResult
for the current page. The Razor Pages framework uses a naming convention to select the appropriate handler method to execute. The default convention works by matching the HTTP method used for the request to the name of the method, which is prefixed with "On": OnGet()
, OnPost()
, OnPut()
etc.
Handler methods also have optional asynchronous equivalents: OnPostAsync()
, OnGetAsync()
etc. You do not need to add the Async
suffix. The option is provided for developers who prefer to use the Async
suffix on methods that contain asynchronous code.
As far as the Razor Pages framework is concerned, OnGet
and OnGetAsync
are the same handler. You cannot have both in the same page. If you do, the framework will raise an exception:
InvalidOperationException: Multiple handlers matched. The following handlers matched route data and had all constraints satisfied:
Void OnGetAsync(), Void OnGet()
Parameters play no part in disambiguating between handlers based on the same HTTP method, despite the fact that the compiler will allow it. Therefore the same exception will be raised even if the OnGet
method takes parameters and the OnGetAsync
method doesn't.
Handler methods must be public
and can return void
, Task
if asynchronous, or an IActionResult (or Task<IActionResult>
).
The following example illustrates basic usage in a PageModel:
public class IndexModel : PageModel
{
public string Message { get; set; }
public void OnGet()
{
Message = "Get used";
}
public void OnPost()
{
Message = "Post used";
}
}
The content age includes a form that uses the POST
method and a hyperlink, which initiates a GET
request:
<h3>@Message</h3>
<form method="post"><button class="btn btn-default">Click to post</button></form>
<p><a href="/" class="btn btn-default">Click to Get</a></p>
When the page is first navigated to, the "Get used" message is displayed because the HTTP GET
verb was used for the request, firing the OnGet()
handler.
When the "Click to post" button is pressed, the form is posted and the OnPost()
handler fires, resulting in the "Post used" message being displayed.
Clicking the hyperlink results in the "Get used" message being displayed once more.
Note that HTTP is stateless. Any values initialised in the OnGet
handler are not available in the OnPost
handler. If you completely remove the assignment in the OnPost
handler, Message
will be null when the page displays. If you want to reuse a value in an OnPost
handler, you must initialise it again.
The most common example of this is when you redisplay a form that contains dropdowns after an invalid submission. You populate the items from a database in OnGet
, but forget to repopulate in OnPost
if ModelState
is not valid.
Named Handler Methods
Razor Pages includes a feature called "named handler methods". This feature enables you to specify multiple methods that can be executed for a single verb. You might want to do this if your page features multiple forms, each one responsible for a different outcome, for example.
The following code shows a collection of named handler methods declared in a code block at the top of a Razor page (although they can also be placed in the PageModel class if you are using one):
@page
@{
@functions{
public string Message { get; set; } = "Initial Request";
public void OnGet()
{
}
public void OnPost()
{
Message = "Form Posted";
}
public void OnPostDelete()
{
Message = "Delete handler fired";
}
public void OnPostEdit(int id)
{
Message = "Edit handler fired";
}
public void OnPostView(int id)
{
Message = "View handler fired";
}
}
}
The name of the method is appended to "OnPost" or "OnGet", depending on whether the handler should be called as a result of a POST
or GET
request. The next step is to associate a specific form action with a named handler. This is achieved by setting the asp-page-handler
attribute value for a form tag helper:
<div class="row">
<div class="col-lg-1">
<form asp-page-handler="edit" method="post">
<button class="btn btn-default">Edit</button>
</form>
</div>
<div class="col-lg-1">
<form asp-page-handler="delete" method="post">
<button class="btn btn-default">Delete</button>
</form>
</div>
<div class="col-lg-1">
<form asp-page-handler="view" method="post">
<button class="btn btn-default">View</button>
</form>
</div>
</div>
<h3 class="clearfix">@Model.Message</h3>
The code above renders as three buttons, each in their own form along with the default value for the Message property:
The name of the handler is added to the form's action as a query string parameter:
As you click each button, the code in the handler associated with the query string value is executed, changing the message each time.
If you prefer not to have query string values representing the handler's name in the URL, you can use routing to add an optional route value for "handler" as part of the route template in the @page directive:
@page "{handler?}"
The name of the handler is then appended to the URL:
Parameters in Handler Methods
Handler methods can be designed to accept parameters:
public void OnPostView(int id)
{
Message = $"View handler fired for {id}";
}
In a POST handler, the parameter name must match a form field name for the incoming value to be automatically bound to the parameter:
<div class="col-lg-1">
<form asp-page-handler="view" method="post">
<button class="btn btn-default">View</button>
<input type="hidden" name="id" value="3" />
</form>
</div>
Alternatively, you can use the form tag helper's asp-route
attribute to pass parameter values as part of the URL, either as a query string value or as route data for GET requests:
<form asp-page-handler="delete" asp-route-id="10" method="post">
<button class="btn btn-default">Delete</button>
</form>
You append the name of the parameter to the asp-route
attribute (in this case "id") and then provide a value. This will result in the parameter being passed as a query string value:
Or you can extend the route template for the page to account for an optional parameter:
@page "{handler?}/{id?}"
This results in the parameter value being added as a separate segment in the URL:
Handling Multiple Actions For The Same Form
Some forms need to be designed to cater for more than one possible action. Where this is the case, you can either write some conditional code to determine which action should be taken, or you can write separate named handler methods and then use the form action tag helper to specify the handler method to execute on submission of the form:
<form method="post">
<button asp-page-handler="Register">Register Now</button>
<button asp-page-handler="RequestInfo">Request Info</button>
</form>
The value passed to the page-handler
attribute is the name of the handler method without the OnPost
prefix or Async
suffix:
public async Task<IActionResult> OnPostRegisterAsync()
{
//…
return RedirectToPage();
}
public async Task<IActionResult> OnPostRequestInfo()
{
//…
return RedirectToPage();
}
The NonHandler Attribute
There may be occasions where you don't want a public method on a page to be considered as a handler method, despite its name matching the conventions for handler method discovery. You can use the NonHandler
attribute to specify that the decorated method is not a page handler method:
[NonHandler]
public void OnGetFoo()
{
...
}