Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Blazor Server 404 page when data is not found, with HTTP status code

This question has been asked many times over the last few years, but all answers appear to either:

  1. Relate to the route not being found (in this case I refer strictly to data)
  2. Require use of the IHttpContextAccessor (which Microsoft docs explicitly state not to do)
  3. Redirecting to a resource that doesn't return the correct 404 headers (no good for SEO)
  4. Show a friendly error in the current component (no good for SEO and requires other functionality to be hidden in the component as a result)
  5. Involve a significant number of aspects/components/pages and dependency injection (very complicated for something that historically has been relatively easy, e.g. with web forms or MVC)

Within the ASP.NET Framework it was easy to return a 404 status. This could be intercepted by IIS or returned as a bespoke page. It didn't matter what level in the control/business logic hierarchy this occurred at:

Web forms

if (myThing == null) {
    var ctx = HttpContext.Current;
    ctx.Response.Clear();
    ctx.Response.StatusCode = System.Net.HttpStatusCode.NotFound;
    ctx.Response.StatusDescription = "No data was found against that URL.";
    ctx.Response.End();
}

ASP.Net Framework MVC

if (myThing == null) {
    ViewBag.ErrorMessage = "No data was found against that URL.";
    Response.StatusCode = (int)HttpStatusCode.NotFound;
    return View("~/Views/Error/NotFound.cshtml");
}

.Net Core Razor Pages

if (myThing == null) {
    return NotFound();
}

.Net Core Blazor Server Components (@page)?

Is there an equivalent within a Blazor Component (.razor file) when that component is configured to act as a @page (not a component placed inside a .cshtml file)?

I appreciate from the comments that the page is found and is processing the request, but to a search engine two similar URL's with different ID parameters are indexed as different pages.

A use case example would be this:

@page "/Product/{Id:int}"
@using Microsoft.EntityFrameworkCore

<h1>Product Details</h1>

@code {

    [Inject]
    IDbContextFactory<MyDbContext> _dbContextFactory { get; set; } = default!;

    [Parameter]
    public int? Id { get; set; }

    protected override async Task OnInitializedAsync()
    {

        using var context = _dbContextFactory.CreateDbContext();

        var thingToEdit = await context.MyEntities
                    .Where(e => e.EntityId == Id.Value)
                    .FirstOrDefaultAsync();

        if (thingToEdit == null)
        {
            // Go to/show a 404 page/message, but must set HTTP status code header
        }
        else
        {
            // Show product details in this component page
        }
    }
}
like image 217
EvilDr Avatar asked Jun 30 '26 04:06

EvilDr


1 Answers

Credit: How to replace Blazor default "soft 404" with an actual 404 status code response

I just added a Razor component called PageNotFound:

@using Microsoft.AspNetCore.Http
@using Tuneality.Web.Server.Views.Layout
@inject IHttpContextAccessor HttpContextAccessor

<PageTitle>Not Found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
  @ChildContent
</LayoutView>

@code {
  [Parameter]
  public RenderFragment ChildContent { get; set; }

   protected override void OnInitialized() {
      if (HttpContextAccessor.HttpContext != null)
         HttpContextAccessor.HttpContext.Response.StatusCode = 404;
   }
}

Then I changed my <NotFound> route to point at the component:

<Router AppAssembly="@typeof(ServerApp).Assembly">
  <Found Context="routeData">
    <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
    <FocusOnNavigate RouteData="@routeData" Selector="h1"/>
  </Found>
  <NotFound>
    <PageNotFound>
      There's nothing on this page!
    </PageNotFound>
  </NotFound>
</Router>

The result is a hard 404 status code on pages which are "not found." As was said in the comments by the OP, this is important for SEO reasons. It allows us to tell search engines to stop indexing the page if they had previously done so, so that we are able to remove content from the site when needed (among other things).

Since the new component accepts children to be rendered, you can use it as a generic component elsewhere with custom "not found" messages. For example, you could have a @page "/images/{id?}" component, and if the image were deleted, render the <PageNotFound> with a message like The image #{id} has been deleted.

like image 188
Zane Claes Avatar answered Jul 03 '26 07:07

Zane Claes



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!