Blazor 服务器端,.razor 页面上的 ExternalRegister 按钮

Blazor Server side, ExternalRegister buttons at .razor page

是否可以将“外部注册”按钮放置在 .razor 页面(服务器端)内?

以下代码来自 ExternalRegister.cshtml,但我希望将这两个注册按钮(Google、Facebook)作为 Start.razor 页面的一部分。这可能吗?

@model Aplication.Areas.Identity.Pages.Account.ExternalRegisterModel
<form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
    <div>
        <p>
            @foreach (var provider in Model.ExternalLogins)
            {
                <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
            }
        </p>
    </div>
</form>

我认为最好的策略是在你的 Razor PageModel 中定义两个 OnPost 方法 (Code-Behind)。例如:

    public void OnPostFaceBook(ExternalLogin provider)
{
    //your code here
}

    public void OnPostGoogle(ExternalLogin provider)
{
    //your code here
}

然后在您的 .cshtml 文件中为每个放置两个单独的表单,并添加参数 asp-page-handler 到每个提交按钮。例如:

  <button type="submit" class="btn btn-primary" value="FaceBook" value="FaceBook" asp-page-handler="FaceBook">Log in using your FaceBook account</button>

以及其他形式:

  <button type="submit" class="btn btn-primary" value="Google" value="Google" asp-page-handler="Google">Log in using your Google account</button>

是的,可以在剃须刀页面中添加按钮。

当然,为此,您需要能够枚举可用的提供程序,这意味着您需要将它们从 _Host.cshtml(或托管 Blazor 应用程序的任何位置)

注意:您不能传递 AuthenticationScheme 的列表,因为 .NET 不会将它们序列化,这就是我将它们转换为 DTO ExternalProvider 的原因

@inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf
@inject SignInManager<IdentityUser> _signInManager
@{
  var state = new InitialApplicationState
  {
      XsrfToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken,
      ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
        .ToList()
        .Select(scheme=>
           new ExternalProvider {Name=scheme.Name, DisplayName=scheme.DisplayName}
         )
  };
}
<component type="typeof(App)" param-InitialState="state" render-mode="ServerPrerendered" />

InitialApplicationStateExternalProvider 是简单的 DTO 类

public class InitialApplicationState
{
    public string XsrfToken { get; set; }
    public IEnumerable<ExternalProvider> ExternalLogins { get; set; }
}
public class ExternalProvider
{
    public string Name { get; set; }
    public string DisplayName { get; set; }
}

现在,您需要在 Blazor 代码中将此数据作为 App.razor 组件上的 Parameter

@inject InitialApplicationState InitialStateService

@code {
  [Parameter] public InitialApplicationState InitialState { get; set; } = default;

  protected override Task OnInitializedAsync()
  {
    InitialStateService.XsrfToken = InitialState.XsrfToken;
    InitialStateService.ExternalLogins = InitialState.ExternalLogins;
    return base.OnInitializedAsync();
  }
}

我们在这里所做的就是声明将接收我们的 InitialApplicationStateParameter InitialState - 然后我们将该状态存储在已配置的服务 InitialStateService 中在 startup.cs 作为 Scoped 依赖项。

builder.Services.AddScoped<InitialApplicationState>();

现在,我们在 Blazor 的 DI 容器中有一项服务,其中包含可用的外部身份验证提供程序列表和我们的伪造保护令牌。

我们可以在 Blazor 中需要的任何地方注入 InitialApplicationState,例如Index.razor 并枚举 ExternalLogins 以呈现按钮

Blazor 中的表单声明略有不同,因为我们没有 asp* 指令:

@inject InitialApplicationState InitialStateService

<form id="external-account" 
      action="/Identity/Account/ExternalLogin" 
      method="post">
  <div>
    <p>
      @foreach (var provider in InitialStateService.ExternalLogins)
      {
        <button type="submit" 
                name="provider" 
                value="@provider.Name" 
                title="Log in using your @provider.DisplayName account">
          @provider.DisplayName
        </button>
      }
    </p>
  </div>
  <input name="__RequestVerificationToken" type="hidden"
        value="@InitialStateService.XsrfToken">
</form>