如何从包含 .Net 5 Web API 中的 HttpContext 数据的通用类创建响应

问题描述

我使用 .Net 5 开始了一个新项目(我之前是 .Net Framework 4.7)。我正在编写一个 Web API 项目,我希望我的所有控制器/动作响应都属于某种类型。这允许我在每个响应中放入一些我想要包含的信息,例如当前用户信息(以及更多内容)。 我的通用响应如下所示(我只留下了相关代码):

public class MyResponse<T>
{
    public T data { get; set; }
    public User user { get; set; }
    public MyResponse(T inputData)    
    {
        data = inputData;
    }
}

我以这种方式设置对控制器操作的响应:

public IActionResult Get()
{
    var response = new MyResponse<string>("Hello");
    return Ok(response);
}

所以这个想法是,响应总是包含一个带有实际数据的“数据”属性,以及一堆带有元数据的其他属性

问题是如何在 .Net 5 中包含有关登录用户的信息。在 .Net 4.x 中,您可以从任何地方访问 HttpContext,因此您只需填充 User 属性。但这在 .Net 5 中是不可能的

试图了解如何在 .Net 5 中实现这一点,我快要疯了。

我尝试的第一件事是 DI(我是新手,所以我可能没有正确理解这一点)。

我尝试的第一件事是让我的 User 类依赖于 IHttpContextAccessor,因为大多数文档都指出:

public class User : IIdentity

{
    private readonly IHttpContextAccessor _httpContextAccessor;
    public User(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
}

并在startup.cs上以这种方式注册

services.AddHttpContextAccessor();
services.AddTransient<User>();

但这并不奏效,因为当我尝试在 MyResponse 类中创建我的 User 类时:

var user = new User(); // This doesn't work,as the constructor requires one argument

所以构造函数需要一个参数,所以我不能创建这样的类。我(相信)我需要从 DI 容器创建用户,但我无法在 MyResponse 类上访问它(或者至少我无法真正理解如何去做或如果可能的话)。

我可以将 HttpContext 从控制器传递给 MyResponse,但这似乎完全错误(另外,可能还有其他人在编写控制器,所以我认为如果他们不需要明确地将其传递给响应,应该透明地处理)

我的具体问题:

关于如何在自定义响应类中获取 HttpContext 的任何想法? 我是否应该寻找替代选项(例如中间件或过滤器)来生成响应?

非常感谢。

解决方法

您可以将工厂与依赖注入一起使用。

创建您的用户类:

using Microsoft.AspNetCore.Http;
using System.Security.Principal;

public class User : IIdentity
{
    private IHttpContextAccessor HttpContextAccessor { get; }
    public User(IHttpContextAccessor httpContextAccessor)
    {
        this.HttpContextAccessor = httpContextAccessor;
    }

    public string AuthenticationType => this.HttpContextAccessor.HttpContext.User.Identity.AuthenticationType;

    public bool IsAuthenticated => this.HttpContextAccessor.HttpContext.User.Identity.IsAuthenticated;

    public string Name => this.HttpContextAccessor.HttpContext.User.Identity.Name;
}

使用 DI 将工厂注入您想要的类型:

services.AddHttpContextAccessor();
services.AddSingleton(a => GetResponse<string>(a));
services.AddSingleton(a => GetResponse<int>(a));
services.AddSingleton(a => GetResponse<decimal>(a));

Func<T,MyResponse<T>> GetResponse<T>(IServiceProvider serviceProvider)
{
    var contextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
    var user = new User(contextAccessor);

    return (data) => new MyResponse<T>(user,data);
}

然后将其注入您想要的位置:

namespace WebAppFiles.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class MyController : ControllerBase
    {
        private Func<int,MyResponse<int>> ResponseFactory { get; }

        public MyController(Func<int,MyResponse<int>> responseFactory)
        {
            this.ResponseFactory = responseFactory;
        }

        [HttpGet]
        public IActionResult Get([FromQuery] int value)
        {        
            return Ok(this.ResponseFactory(value));
        }
    }
}