级联参数 Task<AthenticationState> 如何在 Blazor WASM 的 AuthorizeView 和 AuthorizedRouteView 中展开并暴露为 "context"?

How does cascaded parameter Task<AthenticationState> get unwrapped and exposed as "context" in AuthorizeView and AuthorizedRouteView in Blazor WASM?

在 AuthorizeView 和 AuthorizedRouteView 组件中有一个名为“context”的 AuthenticationState 类型的对象。此对象允许通过 context.User.

访问 ClaimsPrincipal

我可以在源代码中看到,AuthenticationState 通过在 CascadingAuthenticationState 组件中实现的 CascadingValue 组件作为任务传递给这些组件(该组件又在 App.razor 中的组件层次结构的顶部定义) ).

但是,当我检查 AuthorizeRouteView 的源时,我可以看到名为 ExistingCascadedAuthenticationState 的任务类型的级联参数。然而,对我来说,任务如何以及在何处被解包并作为“上下文”公开,这完全是个谜。有人知道答案吗?

级联参数为Task<AuthenticationState> context 这告诉您 context,当等待时,将 return AuthenticationState 类型的对象。所以你得到的是一个任务。任务等待时,returns 值 return 由任务编辑。访问用户的实际语法是

var state = await context;
var user = state.User;

此外,您可以为级联参数指定任何名称。所以

[CascadingParameter]
public Task<AuthenticationState> AuthState {get;set;}

var state = await AuthState;
var user = state.User;

同样有效。

enet 的评论帮助我找到了答案。

当我们有一个 RenderFragment<TValue> 委托时,<TValue> 默认公开为 @context

比如在AuthorizeRouteView中我们有一个参数NotAuthorized:

[Parameter]
public RenderFragment<AuthenticationState> NotAuthorized { get; set; }

在这种情况下 AuthenticationStateTValue,因此 AuthenticationState 暴露为 @context

这篇关于 Blazor 大学的文章是我理解这个概念的关键:Passing placeholders to RenderFragments

编辑 2022-05-29

@somedotnetguy 最近添加的答案使渲染模板的工作原理更加清晰。我建议还阅读他的回答以获得更完整的图片。

需要深入挖掘,有点复杂。

  1. AuthorizeView 继承自 AuthorizeViewCore
  2. AuthorizedRouteView 构建自己的 AuthorizeRouteViewCore 继承自 AuthorizeViewCore.

AuthorizedRouteView底部的代码:

private sealed class AuthorizeRouteViewCore : AuthorizeViewCore
{
    [Parameter]
    public RouteData RouteData { get; set; } = default!;

    protected override IAuthorizeData[]? GetAuthorizeData()
      => AttributeAuthorizeDataCache.GetAuthorizeDataForType(RouteData.PageType);
}

AuthorizedRouteView 将任何级联捕获到 ExistingCascadedAuthenticationState。如果一个存在(不为空),则 CascadingAuthenticationState 存在于 App 中,因此无需再做任何事情。如果它为 null,则它会将 CascadingAuthenticationState 作为组件根组件添加到其渲染片段中。这保证了 Task<AuthenticationState> 是级联的。

AuthorizeViewCore 捕获级联值:

[CascadingParameter] private Task<AuthenticationState>? AuthenticationState { get; set; }

它在 OnParametersSetAsync

中“展开”
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);

并在 BuildRenderTree 中用于您看到的“上下文”。

var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState!));

内容来自:

RenderFragment<TValue>

声明如下,其中 TValue- content - 声明为 AuthenticationState :

[Parameter] public RenderFragment<AuthenticationState>? Authorized { get; set; }

我也在想同样的事情:

怎么会突然在组件的内部标记中(在其开始和结束标记之间,当它在父级中使用时)我们可以写 @context 值从何而来?

提供的答案帮助我弄明白了,Blazor University - RenderFragements (entire chapter, this page and the 4 following)

上有很好的解释

正如在官方文档 here and here 中看到的那样,AuthorizeView 有一个 属性 ChildContent 类型 RenderFragment<AuthenticationState> 装饰着 Parameter

[Microsoft.AspNetCore.Components.Parameter]
public Microsoft.AspNetCore.Components.RenderFragment<Microsoft.AspNetCore.Components.Authorization.AuthenticationState>?
ChildContent { get; set; }

我来了:

您不能使用普通(空的、新创建的)组件执行此操作:

//Parent.razor consumes MyComp
<MyComp>Some markup as content for MyComp</MyComp>

FRAMEWORK 约定是,您需要在组件 (class) 定义中使用以下代码,以便能够在标签之间编写标记(当它被使用时)。然后标记作为 RenderFragment 类型的 ChildComponent 属性 传递:

//MyComp.razor
[Parameter]
public RenderFragment ChildContent { get; set; }

您还可以将 properties/fields 类型的 RenderFragment 或 RenderFragment 与其他 property-names 一起使用,但它们不会像上面那样从父级自动填充。但是,您可以根据需要在组件定义中使用它们。要在任何 RenderFragment 的消费父级中设置内容,您可以使用其 属性 名称(如 html 标记)并在其中写入剃须刀内容。这里的例外是,如果您只想填充 ChildContent 而不想使用其他 RenderFragment,则可以像上面那样省略它(因为这是一个特殊的名称)。

//MyComp.razor

<p>Fragment1:</p>
@SomeOtherRenderFragment

<p>Fragment2:</p>
@SomeGenericRenderFragment(_instanceT)
@code {
    [Parameter]
    public RenderFragment SomeOtherRenderFragment{ get; set; } =
        @<h1>My Render Fragment Example</h1>;

    [Parameter]
    public RenderFragment<T> SomeGenericRenderFragment{ get; set; } // not set here in this example

    private T _instanceT = null // T must be an explicit type, it's a placeholder in this example... for instance change it to 'string'. You can get the instance from where ever you want, probably through some service that you inject with DI
}


//Parent.razor consumes MyComp
// Implicit setting ChildContent
<MyComp>Some markup as content for MyComp</MyComp>

// Explicit setting ChildContent
<MyComp><ChildContent>Some markup as content for MyComp</ChildContent></MyComp>

// Explicit setting various RenderFragments
<MyComp>
    <ChildContent>Some markup as content for MyComp</ChildContent>
    <SomeOtherRenderFragment>SomeContent</SomeOtherRenderFragment>
    <SomeGenericRenderFragment>SomeContent with @context</SomeGenericRenderFragment>
</MyComp>

现在把它们放在一起。您还可以使用通用 RenderFragment 作为 convention-property ChildContent 的类型。但是,您有责任在组件定义中提供通用 class 的实例。

//MyComp.razor

<p>Content passed from parent:</p>
@ChildContent(_instanceT1)
<p>Content passed from parent a second time:</p>
@ChildContent(_instanceT2)
@code {

    [Parameter]
    public RenderFragment<T> ChildContent{ get; set; }

    private string _instanceT1 = "Hello World!";
    private string _instanceT2 = "Good Night!";
}

家长:

//Parent.razor
<h1>Parent:</h1>
<MyComp>
<p>I can type content here and now comes the value of T: @context</p>
<p>and again again: @context</p>
</MyComp>

最终 Html 看起来像这样:

<h1>Parent:</h1>
<p>Content passed from parent:</p>
<p>I can type content here and now comes the value of T: Hello World!</p>
<p>and again again: Hello World!</p>
<p>Content passed from parent a second time:</p>
<p>I can type content here and now comes the value of T: Good Night!</p>
<p>and again again: Good Night!</p>

注意:@context 仅在组件的标签内可用(使用时),如果它在其定义中具有此通用 RenderFragment 属性:

    //MyComp.razor
    [Parameter]
    public RenderFragment<T> ChildContent{ get; set; }

AuthorizeView 可以使用 RenderFragments:

  • ChildContent(最常使用隐式,也可能是显式)
  • 授权(明确)
  • 未授权(明确)
  • 然而它是这样实现的,如果两者都指定则抛出异常:未处理的异常渲染组件:不要同时指定'Authorized'和'ChildContent'。基本上授权替代了 ChildContent,或者换句话说,当没有明确设置任何 RenderFragment 时,ChildContent 被视为授权,如 MrC aka Shaun Curtis 的答案所示。

最后的话:我希望这会有所帮助,我希望我将错别字控制在最低限度:)