You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor自动渲染模式下API请求处理优化方案问询

Blazor自动渲染模式下的API访问解决方案

针对Blazor自动渲染(SSR+CSR混合)模式中API访问的环境适配、身份验证问题,以下是一套简洁可落地的实现方案:

一、HttpClient环境适配的简化实现

1. 注册环境专属的HttpClient实例

在Program.cs中分别为服务器端(SSR)和客户端(CSR)配置独立的HttpClient,避免环境冲突:

// 服务器端API客户端(SSR时使用,可直接访问内部API,无需跨域)
builder.Services.AddHttpClient("ServerApi", client =>
{
    client.BaseAddress = new Uri("https://your-api-domain.com/");
})
.AddHttpMessageHandler<ServerTokenHandler>(); // 自动附加Bearer令牌

// 客户端API客户端(CSR时使用,需符合API的CORS规则)
builder.Services.AddHttpClient("ClientApi", client =>
{
    client.BaseAddress = new Uri("https://your-api-domain.com/");
})
.AddHttpMessageHandler<ClientTokenHandler>();

2. 封装环境感知的API服务

无需自定义IHttpClientFactory,直接封装IApiClient接口统一API调用逻辑,内部自动判断环境切换HttpClient:

public interface IApiClient
{
    Task<TResult> GetAsync<TResult>(string endpoint);
    Task PostAsync<TInput>(string endpoint, TInput data);
    // 根据需求扩展PUT/DELETE等方法
}

public class ApiClient : IApiClient
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ApiClient(IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor)
    {
        _httpClientFactory = httpClientFactory;
        _httpContextAccessor = httpContextAccessor;
    }

    // 自动判断当前环境,返回对应HttpClient
    private HttpClient GetTargetClient()
    {
        return _httpContextAccessor.HttpContext != null 
            ? _httpClientFactory.CreateClient("ServerApi") 
            : _httpClientFactory.CreateClient("ClientApi");
    }

    public async Task<TResult> GetAsync<TResult>(string endpoint)
    {
        var client = GetTargetClient();
        return await client.GetFromJsonAsync<TResult>(endpoint);
    }

    public async Task PostAsync<TInput>(string endpoint, TInput data)
    {
        var client = GetTargetClient();
        await client.PostAsJsonAsync(endpoint, data);
    }
}

注册服务时添加:

builder.Services.AddScoped<IApiClient, ApiClient>();
builder.Services.AddHttpContextAccessor(); // 服务器端获取HttpContext必需

二、身份声明与Bearer令牌自动处理

1. 服务器端令牌与声明获取

SSR时用户身份信息存储在HttpContext中,通过自定义消息处理器自动为请求附加Bearer令牌:

public class ServerTokenHandler : DelegatingHandler
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public ServerTokenHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 从HttpContext获取当前用户的访问令牌
        var accessToken = await _httpContextAccessor.HttpContext.GetTokenAsync("access_token");
        if (!string.IsNullOrEmpty(accessToken))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        }
        return await base.SendAsync(request, cancellationToken);
    }
}

注册处理器:

builder.Services.AddScoped<ServerTokenHandler>();

服务器端获取用户声明直接通过HttpContext.User,示例:

var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

2. 客户端令牌与声明获取

CSR时令牌通常存在浏览器本地存储,通过JS互操作获取并自动附加到请求:

public class ClientTokenHandler : DelegatingHandler
{
    private readonly IJSInProcessRuntime _jsRuntime;

    public ClientTokenHandler(IJSInProcessRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // 从localStorage读取访问令牌
        var accessToken = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "access_token");
        if (!string.IsNullOrEmpty(accessToken))
        {
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        }
        return await base.SendAsync(request, cancellationToken);
    }
}

注册客户端处理器:

builder.Services.AddScoped<ClientTokenHandler>();

客户端获取身份声明可通过AuthenticationStateProvider:

@inject AuthenticationStateProvider AuthStateProvider

@code {
    protected override async Task OnInitializedAsync()
    {
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        var userName = user.FindFirst(ClaimTypes.Name)?.Value;
    }
}

三、组件中的实际使用示例

在自动渲染模式的组件中,直接注入IApiClient即可,无需手动判断环境:

@page "/user-profile"
@inject IApiClient ApiClient
@inject AuthenticationStateProvider AuthStateProvider

<h2>个人资料</h2>

@if (profile != null)
{
    <p>用户名:@profile.UserName</p>
    <p>邮箱:@profile.Email</p>
}

@code {
    private UserProfile profile;

    protected override async Task OnInitializedAsync()
    {
        // 自动适配SSR/CSR环境发起API请求
        profile = await ApiClient.GetAsync<UserProfile>("api/users/profile");
        
        // 获取当前用户身份信息
        var authState = await AuthStateProvider.GetAuthenticationStateAsync();
        var userId = authState.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    }

    public class UserProfile
    {
        public string UserName { get; set; }
        public string Email { get; set; }
    }
}

四、关键问题处理

  • SSR跨域问题:服务器端发起的请求属于后端调用,无需配置CORS,只需确保API允许服务器IP或域名访问。
  • 令牌过期处理:在消息处理器中添加令牌过期校验,服务器端可通过刷新令牌自动获取新令牌;客户端可调用登录接口刷新,并更新本地存储的令牌。
  • 身份状态同步:客户端登录/登出后,调用AuthStateProvider.NotifyAuthenticationStateChanged()触发组件重新渲染,同步身份状态。

内容的提问来源于stack exchange,提问作者AMB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 15:27:03