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

ASP.NET Core/Web API各层间用户/请求数据共享最优方案问询

在ASP.NET Core/Web API中传递用户/请求数据与OData参数的最佳实践

一、用户/请求上下文传递:避免直接依赖IHttpContextAccessor

核心思路是将请求上下文封装为业务友好的独立对象,通过Scoped生命周期服务在请求流程中传递,替代对IHttpContextAccessor的直接依赖,同时保证服务层/仓储层的可测试性与解耦。

1. 定义业务上下文类

创建包含用户信息、请求元数据的上下文类:

public class RequestContext
{
    public string UserId { get; set; }
    public string UserName { get; set; }
    public string TraceId { get; set; }
    public Dictionary<string, object> ExtraData { get; set; } = new();
}

2. 注册Scoped服务

在Program.cs中将上下文注册为Scoped(每个请求对应一个实例):

builder.Services.AddScoped<RequestContext>();

3. 通过中间件自动填充上下文

编写中间件从HttpContext中提取数据并填充上下文,放在认证中间件之后:

public class RequestContextMiddleware
{
    private readonly RequestDelegate _next;

    public RequestContextMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context, RequestContext requestContext)
    {
        // 从Claims中获取用户信息
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var userName = context.User.FindFirst(ClaimTypes.Name)?.Value;

        requestContext.UserId = userId;
        requestContext.UserName = userName;
        requestContext.TraceId = context.TraceIdentifier;

        await _next(context);
    }
}

// 注册中间件
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<RequestContextMiddleware>();

4. 在服务/仓储层直接注入使用

无需显式传递,直接注入RequestContext即可获取请求上下文:

public class UserService
{
    private readonly RequestContext _requestContext;
    private readonly IUserRepository _userRepo;

    public UserService(RequestContext requestContext, IUserRepository userRepo)
    {
        _requestContext = requestContext;
        _userRepo = userRepo;
    }

    public async Task<User> GetCurrentUserProfile()
    {
        return await _userRepo.GetById(_requestContext.UserId);
    }
}

二、OData参数传递:无需显式传递的方案

同样采用Scoped上下文封装OData参数,通过过滤器自动捕获并填充,实现跨层共享。

1. 定义OData查询上下文类

public class ODataQueryContext
{
    public ODataQueryOptions RawOptions { get; set; }
    public string Filter { get; set; }
    public string OrderBy { get; set; }
    public int? Skip { get; set; }
    public int? Top { get; set; }
}

2. 注册Scoped服务与自动填充过滤器

在Program.cs中注册上下文与动作过滤器:

builder.Services.AddScoped<ODataQueryContext>();
builder.Services.AddScoped<ODataQueryCaptureFilter>();

builder.Services.AddControllers(options =>
{
    options.Filters.AddService<ODataQueryCaptureFilter>();
}).AddOData(opt => opt.Select().Filter().OrderBy().Expand().Count().SetMaxTop(100));

3. 编写过滤器自动捕获OData参数

过滤器从控制器动作参数中提取ODataQueryOptions并填充上下文:

public class ODataQueryCaptureFilter : IActionFilter
{
    private readonly ODataQueryContext _odataContext;

    public ODataQueryCaptureFilter(ODataQueryContext odataContext) => _odataContext = odataContext;

    public void OnActionExecuting(ActionExecutingContext context)
    {
        var odataOptions = context.ActionArguments.Values.OfType<ODataQueryOptions>().FirstOrDefault();
        if (odataOptions == null) return;

        _odataContext.RawOptions = odataOptions;
        _odataContext.Filter = odataOptions.Filter?.RawValue;
        _odataContext.OrderBy = odataOptions.OrderBy?.RawValue;
        _odataContext.Skip = odataOptions.Skip?.Value;
        _odataContext.Top = odataOptions.Top?.Value;
    }

    public void OnActionExecuted(ActionExecutedContext context) { }
}

4. 在服务/仓储层使用OData参数

直接注入ODataQueryContext即可应用查询规则:

public class UserRepository : IUserRepository
{
    private readonly AppDbContext _dbContext;
    private readonly ODataQueryContext _odataContext;

    public UserRepository(AppDbContext dbContext, ODataQueryContext odataContext)
    {
        _dbContext = dbContext;
        _odataContext = odataContext;
    }

    public async Task<IQueryable<User>> GetFilteredUsers()
    {
        var query = _dbContext.Users.AsQueryable();
        if (_odataContext.RawOptions != null)
        {
            query = _odataContext.RawOptions.ApplyTo(query) as IQueryable<User>;
        }
        return query;
    }
}

可选方案:AsyncLocal静态上下文(适合轻量场景)

如果不想依赖注入,可使用AsyncLocal实现请求级别的上下文传递,但需注意测试时的模拟成本:

public static class RequestContextHolder
{
    private static readonly AsyncLocal<RequestContext> _current = new();
    public static RequestContext Current
    {
        get => _current.Value;
        set => _current.Value = value;
    }
}

// 中间件中设置
public async Task InvokeAsync(HttpContext context)
{
    var ctx = new RequestContext { /* 填充数据 */ };
    RequestContextHolder.Current = ctx;
    try { await _next(context); }
    finally { RequestContextHolder.Current = null; }
}

// 服务层直接访问
var userId = RequestContextHolder.Current.UserId;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 18:35:41