I want to specify the layout in the following error.
- Route not found.
- Not authorized.
- Forbidden.
File details
App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<!-- TODO RZ9999 when Context removed -->
<AuthorizeView Context="authenticated">
<Authorized>
<!-- TODO ErrorLayout -->
<Error403/>
</Authorized>
<NotAuthorized>
<!-- TODO ErrorLayout -->
<Error401/>
</NotAuthorized>
</AuthorizeView>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
</Found>
<NotFound>
<LayoutView Layout="@typeof(ErrorLayout)">
<Error404/>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
Error401.razor, Error403.razor, Error404.razor
<PageTitle>Error401</PageTitle>
<h3>Error401</h3>
<PageTitle>Error403</PageTitle>
<h3>Error403</h3>
<PageTitle>Error404</PageTitle>
<h3>Error404</h3>
What I have tried
According to the description in File details, ErrorLayout is used for Error404, but MainLayout is applied for Error401 and Error403.
I tried the following description, but it did not work.
Add @layout
Error401.razor as follows but MainLayoute was applied. Is @layout only valid for those with @page?
@layout ErrorLayout
<PageTitle>Error401</PageTitle>
<h3>Error401</h3>
Add LayoutView
If Error401 and Error403 are children of LayoutView, they will be nested in the MainLayout and ErrorLayout layouts.
<AuthorizeView Context="authenticated">
<Authorized>
<LayoutView Layout="@typeof(ErrorLayout)">
<Error403/>
</LayoutView>
</Authorized>
<NotAuthorized>
<LayoutView Layout="@typeof(ErrorLayout)">
<Error401/>
</LayoutView>
</NotAuthorized>
</AuthorizeView>
Questions
How do I write a Router to specify the layout in case of authorization and authentication errors?
CodePudding user response:
Is @layout only valid for those with @page?
Yes sort of... You can also add it to a layout to to specify nesting.
FYI: 401 and 403 are both captured by <NotAuthorized>
.
Use a component like:
public class RedirectToPage : ComponentBase
{
[Inject]
private NavigationManager Navigation { get; set; }
[Parameter]
public string Href { get; set; }
protected override void OnInitialized()
=> Navigation.NavigateTo(Href);
}
You could expand this to include a return address etc.
Inside your App.razor
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated == true)
{
<RedirectToPage Href="/error403" />
}
else
{
<RedirectToPage Href="/error401" />
}
</NotAuthorized>
Create the appropriate error pages using @layout ...
of your choosing.
CodePudding user response:
I solved it by creating a custom version of AuthorizeRouteView.
Create a CustomAuthorizeRouteView with the following changes based on the Blazor source AuthorizeRouteView.
public sealed class CustomAuthorizeRouteView : RouteView
{
...
[Parameter]
public Type NotAuthorizedLayout { get; set; }
...
private void RenderContentInNotAuthorizedLayout(RenderTreeBuilder builder, RenderFragment content)
{
builder.OpenComponent<LayoutView>(0);
builder.AddAttribute(1, nameof(LayoutView.Layout), NotAuthorizedLayout);
builder.AddAttribute(2, nameof(LayoutView.ChildContent), content);
builder.CloseComponent();
}
private void RenderNotAuthorizedInDefaultLayout(RenderTreeBuilder builder, AuthenticationState authenticationState)
{
var content = NotAuthorized ?? _defaultNotAuthorizedContent;
RenderContentInNotAuthorizedLayout(builder, content(authenticationState));
}
...
}
App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<CustomAuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" NotAuthorizedLayout="@typeof(ErrorLayout)">
<NotAuthorized>
@if (context.User.Identity?.IsAuthenticated != true)
{
<Error401/>
}
else
{
<Error403/>
}
</NotAuthorized>
</CustomAuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1"/>
</Found>
<NotFound>
<LayoutView Layout="@typeof(ErrorLayout)">
<Error404/>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
With this content, I was able to do what I wanted to do without navigation with the original URL.