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

.NET 8 Web API:无HttpContext时如何解决自定义服务存储请求数据空值问题

问题解决:ASP.NET Core 8中跨层共享请求级数据(无需HttpContext)

问题根源

你遇到的GetTrustedPartnerDetails()返回null的核心原因是中间件生命周期与Scoped服务不匹配:
传统约定式中间件默认是单例生命周期,如果在中间件构造函数中注入Scoped服务,ASP.NET Core会强制将该服务提升为单例,导致中间件使用的服务实例和控制器请求级的Scoped实例不是同一个,自然拿不到设置的数据。

解决方案

方案1:使用IMiddleware实现Scoped中间件

通过IMiddleware接口实现中间件,该类型会以Scoped生命周期注册,确保中间件和控制器使用同一个ISharedDataService实例:

1.1 实现Scoped中间件

public class TrustedPartnerMiddleware : IMiddleware
{
    private readonly ISharedDataService _sharedDataService;

    public TrustedPartnerMiddleware(ISharedDataService sharedDataService)
    {
        _sharedDataService = sharedDataService;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        // 模拟获取并设置可信伙伴数据
        var partnerData = new { PartnerId = "TP-001", Name = "Global Trusted Partner" };
        _sharedDataService.SetTrustedPartnerDetails(partnerData);

        await next(context);
    }
}

1.2 注册中间件

在Program.cs中注册Scoped中间件:

// 注册Scoped中间件
services.AddScoped<TrustedPartnerMiddleware>();
// 启用中间件
app.UseMiddleware<TrustedPartnerMiddleware>();

方案2:用AsyncLocal实现请求级数据隔离(推荐,无需依赖生命周期)

借助AsyncLocal<T>特性,它会跟随异步上下文自动隔离请求级数据,即使服务注册为Singleton也能保证每个请求的数据独立,且完全无需依赖HttpContext:

2.1 重构SharedDataService(增加类型安全)

public interface ISharedDataService
{
    void SetTrustedPartnerDetails<T>(T trustedPartnerDetails);
    T? GetTrustedPartnerDetails<T>();
}

public class SharedDataService : ISharedDataService
{
    // AsyncLocal保证异步上下文内的数据隔离
    private readonly AsyncLocal<object?> _trustedPartnerData = new();

    public void SetTrustedPartnerDetails<T>(T trustedPartnerDetails)
    {
        _trustedPartnerData.Value = trustedPartnerDetails;
    }

    public T? GetTrustedPartnerDetails<T>()
    {
        return _trustedPartnerData.Value is T data ? data : default;
    }
}

2.2 注册服务(可注册为Singleton)

// 注册为Singleton即可,AsyncLocal已保证请求隔离
services.AddSingleton<ISharedDataService, SharedDataService>();

2.3 中间件使用示例(单例中间件也可正常工作)

public class TrustedPartnerMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ISharedDataService _sharedDataService;

    // 单例中间件构造函数注入Singleton服务,无生命周期冲突
    public TrustedPartnerMiddleware(RequestDelegate next, ISharedDataService sharedDataService)
    {
        _next = next;
        _sharedDataService = sharedDataService;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var partnerData = new { PartnerId = "TP-001", Name = "Global Trusted Partner" };
        _sharedDataService.SetTrustedPartnerDetails(partnerData);

        await _next(context);
    }
}

// 注册单例中间件
app.UseMiddleware<TrustedPartnerMiddleware>();

控制器代码调整(适配类型安全)

如果用了方案2的泛型方法,控制器可以直接获取强类型数据:

[ApiController]
[Route("[controller]")]
public class ExampleController : ControllerBase
{
    private readonly ISharedDataService _sharedDataService;

    public ExampleController(ISharedDataService sharedDataService)
    {
        _sharedDataService = sharedDataService;
    }

    [HttpGet]
    public IActionResult GetTrustedPartnerDetails()
    {
        // 直接获取强类型数据
        var trustedPartnerDetails = _sharedDataService.GetTrustedPartnerDetails<dynamic>();
        // 或者定义具体的DTO类型,比如TrustedPartnerDto
        // var trustedPartnerDetails = _sharedDataService.GetTrustedPartnerDetails<TrustedPartnerDto>();

        if (trustedPartnerDetails == null)
        {
            return NotFound("Trusted Partner details not found.");
        }

        return Ok(trustedPartnerDetails);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:25:23