Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support using Blazor pages with status code pages middleware #51203

Open
danroth27 opened this issue Oct 7, 2023 · 8 comments
Open

Support using Blazor pages with status code pages middleware #51203

danroth27 opened this issue Oct 7, 2023 · 8 comments
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one Pillar: Complete Blazor Web
Milestone

Comments

@danroth27
Copy link
Member

danroth27 commented Oct 7, 2023

If you try to use Blazor pages with the status code pages middleware you get unexpected behavior.

Repro steps:

  • Create a Blazor Web App with global interactive server rendering
  • Add the status code pages middleware to Program.cs: app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
  • Add Components/Pages/StatusCode.razor:
@page "/StatusCode/{status:int}"

<PageTitle>@Status</PageTitle>

<h1>@Status</h1>

@code {
    [Parameter]
    public int Status { get; set; }
}
  • Run the app and browser to a non-existent page, like /test

Expected result: Blazor status page renders for the 404 response.
Actual result: Blazor status page shows up briefly and then is replaced with a plain text "Not found" page.

We special case re-executed requests from the error handling middleware. We might need to do this for the status code page middleware as well.

Related issue for handling not found requests: #45654

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-blazor Includes: Blazor, Razor Components label Oct 7, 2023
@danroth27 danroth27 changed the title Use Blazor pages with status code pages middleware Support using Blazor pages with status code pages middleware Oct 7, 2023
@mkArtakMSFT mkArtakMSFT added the bug This issue describes a behavior which is not expected - a bug. label Oct 9, 2023
@mkArtakMSFT mkArtakMSFT added this to the .NET 9 Planning milestone Oct 9, 2023
@ghost
Copy link

ghost commented Oct 9, 2023

Thanks for contacting us.

We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. We would like to keep this around to collect more feedback, which can help us with prioritizing this work. We will re-evaluate this issue, during our next planning meeting(s).
If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact issues.
To learn more about what to expect next and how this issue will be handled you can read more about our triage process here.

@mkArtakMSFT mkArtakMSFT added enhancement This issue represents an ask for new feature or an enhancement to an existing one and removed bug This issue describes a behavior which is not expected - a bug. labels Oct 9, 2023
@mkArtakMSFT mkArtakMSFT modified the milestones: Planning: WebUI, Backlog Dec 20, 2023
@ghost
Copy link

ghost commented Dec 20, 2023

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@dotnet-policy-service dotnet-policy-service bot added the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 6, 2024
@wtgodbe wtgodbe removed the pending-ci-rerun When assigned to a PR indicates that the CI checks should be rerun label Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@dotnet dotnet deleted a comment from dotnet-policy-service bot Feb 13, 2024
@likquietlyy
Copy link

Page with plain text "Not Found" is the default implementation of the Router's NotFound razor component. That means that <NotFound>...</NotFound> component is used to provide custom content when content isn't found at Blazor Web App with global interactive server rendering. But documentation tells that this section only applies to Blazor WebAssembly apps.
Is this a documentation error or we should not use the NotFound component in the Blazor Server App?

@getWombats
Copy link

Page with plain text "Not Found" is the default implementation of the Router's NotFound razor component. That means that <NotFound>...</NotFound> component is used to provide custom content when content isn't found at Blazor Web App with global interactive server rendering. But documentation tells that this section only applies to Blazor WebAssembly apps. Is this a documentation error or we should not use the NotFound component in the Blazor Server App?

I have just encountered the same problem. Trying to get some kind of workaround but adding app.UseStatusCodePagesWithRedirects("/somePageToRedirect"); to Program.cs isnt really what i am looking for. Any other suggestions?

@janrhansen
Copy link

I see two possible workarounds:

  1. add
    @rendermode InteractiveServer
    to each and every page, instead of applying it globally in (which I guess is what we mean by "Create a Blazor Web App with global interactive server rendering") like <Routes @rendermode="InteractiveServer" />
    I don't know if this approach will prevent something else from working. It is cumbersome

  2. Add it globally
    <Routes @rendermode="InteractiveServer" />
    and then in <Router...> under <NotFound> (which is now suddenly relevant again) handle the NotFound-situation directly or by including a component

<Router AppAssembly="typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)">
                <Authorizing>
                    Authorizing...
                </Authorizing>
                <NotAuthorized>
                    <RedirectToLogin></RedirectToLogin>
                </NotAuthorized>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="typeof(Layout.MainLayout)">
                <p>Route was not found</p>
            </LayoutView>
        </NotFound>
    </Router>

You would then think that the
app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");
was no longer needed, since that page is now never invoked (I only had it to catch the 404) - but NO. If you remove that line, the NotFound approach doesn't work.

@janrhansen
Copy link

@mkArtakMSFT How can this not be a bug?
Is it intentional that the <NotFound> part of <Router> is active/ignored depending on whether @rendermode="InteractiveServer" is set on the parent level or not? It feels like a bug, and unless this is intentional behaviour, I would ask that this issue is reclassified as a bug again, not an enhancement.

@purplepiranha
Copy link

purplepiranha commented May 25, 2024

@janrhansen @mkArtakMSFT I agree that this must be a bug!

The documentation states that 'Blazor Web Apps don't use the NotFound parameter...', however, when setting app.UseStatusCodePagesWithReExecute("/error/{0}");, it gets enabled. This means that my error route gets displayed momentarily and then 'Not found' is displayed instead.

Trying to get a genuine 404 page to render with Blazor is a total nightmare, and the documented approach of using UseStatusCodePagesWithRedirects is not user friendly and definitely not SEO friendly.

Whilst, there is never going to be a way to get a 404 status when working interactively, it is imperative that we can generate a proper status code response from the initial server render and that we can do this without a change in URL. A 302 redirect when it should be a 404 is simply unacceptable.

Take my scenario, a blog. The URL structure is simple:
https://mydomain.xyz/blog/{categoryUrlStub}
https://mydomain.xyz/blog/{categoryUrlStub}/{postUrlStub}

In order to get a 404 during the server render, I have some middleware that checks to see if the category and post exists. If not, it sets the status code on the response to 404. The problem then is that it still matches a route (I haven't found a way to stop this), so once the error page is rendered it then re-renders the matching route. By ensuring that a similar check is made within my page model, the client then renders the same.

Now, say I have a situation where I need an '410 - Gone' status page, or would like to use '418 - I'm a Tea Pot' status for some reason... I'm now in the situation where the server does it's job and I can see the page (briefly), but the Router still decides to carry on and as it can't find that route, and it generates a 404 page but the status has been reported as 410 or 418. This is less of a problem, because I can catch these and they'll be re-rendered, but it means that every-time something like this is done it's run the code on the server, and then re-run the code on the server (as I'm using InteractiveServer).

My feeling is that once the status code has been set to an error response, then the Router shouldn't be dealing with the route. It should be handed off to the error page(s) to then render and deal with any interaction.

@DM-98
Copy link

DM-98 commented Oct 25, 2024

Hello @danroth27

A fix for this (specifically 404 not found) is by adding it to Routes.razor like this;

@using BlazorApp21.Components.Layout
@using BlazorApp21.Components.Pages

<Router AppAssembly="typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>

    <NotFound>
        <MainLayout>
            <Body>
                <StatusCode Status="404" />
            </Body>
        </MainLayout>
    </NotFound>
</Router>

Another option which might be even more robust when it comes to all status codes, is by Excluding it from interactivity:
StatusCode.razor:

@page "/StatusCode/{status:int}"

@attribute [ExcludeFromInteractiveRouting]

<PageTitle>@Status</PageTitle>

<h1>@Status</h1>

@code {
    [Parameter]
    public int Status { get; set; }
}

App.razor:

...
<HeadOutlet @rendermode="PageRenderMode" />
...
<Routes @rendermode="PageRenderMode" />
...
@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? PageRenderMode => HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-blazor Includes: Blazor, Razor Components enhancement This issue represents an ask for new feature or an enhancement to an existing one Pillar: Complete Blazor Web
Projects
None yet
Development

No branches or pull requests

9 participants