Blazor服务器-登录后的本地存储



我正在使用默认的Microsoft身份平台开发Blazor Server项目。我的目标是在我的数据库中创建一个用户,并在microsoft登录完成后将其保存到本地存储

在启动时,我成功地使用OnTokenValidated事件在登录后执行一些操作。但是,在此阶段不可能写入本地存储,因为页面尚未呈现。我想做这样的事情,这是可能与Webassembly。

<RemoteAuthenticatorView Action="@Action" OnLogInSucceeded="SomeCode" />

有没有人知道一种方法来做到这一点,而不使用像在MainLayout中添加OnAfterRenderAsync这样的解决方案,它将在每次页面加载时触发。我想调用一个方法后,身份登录重定向回我的网站在一个状态下,LocalStorage是可访问的。

你确实需要把一些代码放在OnAfterRenderAsync中,但在这个演示中,我把它放在App中,并检查组件是否已经渲染。

下面是一个演示和一些与LocalStorage交互的代码。你应该能够使它适应你的需要。

第一个服务封装获取和设置本地存储

using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
public class LocalStorageService
{
    private readonly ProtectedLocalStorage _storage;
    public LocalStorageService(ProtectedLocalStorage storage)
        => _storage = storage;
    public async ValueTask<CommandResult> SaveAsync<TRecord>(CommandRequest<TRecord> request)
    {
        if (request.Record is not null)
            await _storage.SetAsync(request.StorageName, request.Record);
        // No return so we return success!
        return CommandResult.Success();
    }
    public async ValueTask<RecordQueryResult<TRecord>> ReadAsync<TRecord>(RecordQueryRequest<TRecord> request)
    {
        // We need to cover the situation were the component calling this is in the initial page
        // and Blazor server is trying to statically render the page
        try
        {
            var result = await _storage.GetAsync<TRecord>(request.StorageName);
            return new RecordQueryResult<TRecord> { Successful = result.Success, Record = result.Value, Message = $"Failed to retrieve a value for {request.StorageName}" };
        }
        catch
        {
            return new RecordQueryResult<TRecord> { Successful = false, Message = $"Failed to retrieve a value for {request.StorageName}" };
        }
    }
}

CQS Request和Result对象:

public record CommandRequest<TRecord>(string StorageName, TRecord Record);
public record RecordQueryRequest<TRecord>(string StorageName);
public record CommandResult
{
    public bool Successful { get; init; }
    public string Message { get; init; } = string.Empty;
    public static CommandResult Success()
        => new CommandResult { Successful = true };
    public static CommandResult Failure(string message)
        => new CommandResult { Successful = false };
}
public record RecordQueryResult<TRecord>
{
    public TRecord? Record { get; init; }
    public bool Successful { get; init; }
    public string Message { get; init; } = string.Empty;
    public static RecordQueryResult<TRecord> Success(TRecord record)
        => new RecordQueryResult<TRecord> { Record = record, Successful = true };
    public static RecordQueryResult<TRecord> Failure(string message)
        => new RecordQueryResult<TRecord> { Successful = false };
}

注册如下:

builder.Services.AddScoped<LocalStorageService>();

My simple Data:

public record TestData( string LastSaved);

添加代码到App设置,如果你在登录后获得数据。这实现了一个自定义的渲染后处理程序。

@inject LocalStorageService Service
@implements IHandleAfterRender
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
            <FocusOnNavigate RouteData="@routeData" Selector="h1" />
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, theres nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>
@code {
    private bool _hasCalledOnAfterRender;
    // implements a custom IHandleAfterRender handler
    async Task IHandleAfterRender.OnAfterRenderAsync()
    {
        // Only do if first render and the data in local storage is empty
        if (!_hasCalledOnAfterRender && !await GetData())
        {
            var newData = new TestData($"Saved at {DateTime.Now.ToLongTimeString()}");
            var result = await this.Service.SaveAsync<TestData>(new CommandRequest<TestData>("TestData", newData));
            _hasCalledOnAfterRender = true;
        }
    }
    private async Task<bool> GetData()
    {
        var result = await this.Service.ReadAsync<TestData>(new RecordQueryRequest<TestData>("TestData"));
        return result?.Successful ?? false;
    }
}

和我的测试路线/页面显示的数据。

@page "/"
@inject LocalStorageService Service
@implements IDisposable
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
<div class="bg-black text-white m-3">
    Test Data Last Saved at : @this.data.LastSaved
</div>
<div class="m-3">
    <button class="btn btn-primary" @onclick=SaveToLocal>Save Data to Local</button>
</div>
@code {
    private TestData data = new TestData(string.Empty);
    protected override async Task OnInitializedAsync()
    {
        await this.GetData();
        this.Service.StorageChanged += this.DataChanged;
    }
    private async void DataChanged(object? sender, EventArgs e)
    {
        await this.GetData();
        await this.InvokeAsync(StateHasChanged);
    }
    private async Task<bool> GetData()
    {
        var result = await this.Service.ReadAsync<TestData>(new RecordQueryRequest<TestData>("TestData"));
        data = result?.Record ?? new TestData(string.Empty);
        return result?.Successful ?? false;
    }
    private async Task SaveToLocal()
    {
        var newData = new TestData($"Saved at {DateTime.Now.ToLongTimeString()}");
        var result = await this.Service.SaveAsync<TestData>(new CommandRequest<TestData>("TestData", newData));
        await this.GetData();
    }
    public void Dispose()
        => this.Service.StorageChanged -= this.DataChanged;
}

最新更新