HTTP semantics might not be vital to us humans, but they are the essential glue that holds the internet together. HTTP status codes allow clients to interpret the response they are about to receive and make processing accommodations. We may all be familiar with 200 OK
, but there is a stable of other status codes that can indicate redirects, missing content, and catastrophic events.
In this post, we’ll see how we can use a middleware to process status codes respectfully so that both humans and our web clients get the necessary information necessary to take the next step.
Error Page
When we create a new Razor Pages project, we immediately have an Error
page created. Let’s take a look at the implementation.
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
When we run into the error, we see the following page.
Let’s say we go to edit a resource, only to find it does not exist. Most applications will return a 404 (Not Found)
result. Let’s see how to do that in our Razor Pages OnGet
method.
// the route is /{id:int}
public IActionResult OnGet(int id)
{
var result = GetResource(id);
if (result == null)
{
return NotFound();
}
return Page();
}
There is a problem. When we run our application, we get the default browser error page.
What’s the deal?
StatusCodePagesMiddleware
Well, it turns out we need an additional middleware known as the StatusCodePagesMiddleware
. This middleware gives us the ability to either redirect or execute a route in our web application. Let’s take a look at the internal implementation of this middleware.
var statusCodeFeature = new StatusCodePagesFeature();
context.Features.Set<IStatusCodePagesFeature>(statusCodeFeature);
await _next(context);
if (!statusCodeFeature.Enabled)
{
// Check if the feature is still available because other middleware (such as a web API written in MVC) could
// have disabled the feature to prevent HTML status code responses from showing up to an API client.
return;
}
// Do nothing if a response body has already been provided.
if (context.Response.HasStarted
|| context.Response.StatusCode < 400
|| context.Response.StatusCode >= 600
|| context.Response.ContentLength.HasValue
|| !string.IsNullOrEmpty(context.Response.ContentType))
{
return;
}
var statusCodeContext = new StatusCodeContext(context, _options, _next);
await _options.HandleAsync(statusCodeContext);
We can see that any status code above 400
and below 600
will cause the middleware to jump into action. Additionally, we can turn the feature off for any reason. In the code comments, its mentioned that it might be essential to turn this feature off for API based responses.
To add this middleware, we need to call UseStatusCodePagesWithReExecute
in our Startup.Configure
method.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
// *** Here is our middleware registration ***
app.UseStatusCodePagesWithReExecute("/Error");
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapRazorPages(); });
}
Now, when we run our previous NotFound
request, we’ll see our error page.
Take note, to preserve the URI but still return a response to the client, we want to use ReExecute
and not Redirect
.
Conclusion
While Razor Pages allows our OnGet
and OnPost
methods to be void
, we should consider HTTP semantics and always return an IActionResult
. The different return type allows us to handle error modes more gracefully, and leverage our pre-packaged Error
page. The attention to detail should keep both humans and our robotic overlords happy.
I hope you found this post helpful, and please leave a comment.