我正在使用JWT身份验证和Dapper ORM开发ASP Core 2项目。
与所有ASP项目一样,我有很多控制器,每个控制器都实例化其关联的数据对象。每个数据对象都继承自提供数据库访问服务的抽象DbObject类。我还有一个AuthenticatedUser对象,它抽象JWT使它的属性更容易使用。
我想要做的是在DbObject的构造函数中创建AuthenticatedUser对象。当然,一种方法是在控制器中创建它并将其传递给每个具体的数据对象,但这很麻烦,因为它必须传递数百次(而且感觉不对)。
有没有办法在身份验证后使用ASP Core中间件获取令牌,并通过DbObject中的依赖注入使其可用?
编辑 希望这能澄清我的意图。我希望控制器创建数据对象并使用它们的属性和方法而不考虑实现(即DbObject)。但是,DbObject执行的查询将被登录用户的令牌中的信息过滤。
public class ManufacturerController : Controller {
[HttpGet]
public async Task<IActionResult> Get() {
var manufacturers = await new Manufacturer().SelectMany();
return Ok(manufacturers);
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(int id) {
var manufacturer = await new Manufacturer().SelectOne(id);
return Ok(manufacturer);
}...
public class Manufacturer : DbObject<Manufacturer> {
protected override string QrySelectOne => @"
Select *
From org.fn_Manufacturers ({0})
Where Id = {1}";
protected override string QrySelectMany => @"
Select *
From org.fn_Manufacturers ({0})";
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }...
public abstract class DbObject<T> {
protected readonly AuthenticatedUser authenticatedUser;
public DbObject(IHttpContextAccessor contextAccessor) {
authenticatedUser = new
AuthenticatedUser(contextAccessor.HttpContext.User);
}
protected abstract string QrySelectOne { get; }
protected abstract string QrySelectMany { get; }
public async Task<T> SelectOne (int id) {...}
public async Task<T> SelectOne(params object[] ids) {...}
public async Task<IEnumerable<T>> SelectMany () {...}
public async Task<IEnumerable<T>> SelectMany (params object[] ids) {...}
我想一个解决方案可能是创建一个注入了IHttpContextAccessor
的静态数据对象工厂?
答案 0 :(得分:0)
ASP.NET Core提供了IHttpContextAccessor
接口,用于从非控制器对象访问HttpContext
。
用法很简单。将IHttpContextAccessor
注入DbObject
并通过调用HttpContext
访问IHttpContextAccessor.HttpContext
:
public abstract class DbObject
{
protected DbObject(IHttpContextAccessor contextAccessor)
{
var context = contextAccessor.HttpContext;
// Create instance of AuthenticatedUser based on context.User or other request data
}
}
修改强>
您的控制器直接实例化数据对象(使用new
运算符),这就是您不能开箱即用IHttpContextAccessor
的原因。这是可能的解决方案。我按照我的偏好(从最好到最差)列出它们。
如果每个控制器只使用一种(或几种)数据对象,最好的选择是避免直接实例化并转向正常的依赖注入。
因此,如果ManufacturerController
只需要样本中的Manufacturer
,那么最好将Manufacturer
实例注入控制器,而不是在内部创建:
public class Manufacturer1Controller : Controller
{
private readonly Manufacturer manufacturer;
public Manufacturer1Controller(Manufacturer manufacturer)
{
this.manufacturer = manufacturer ?? throw new ArgumentNullException(nameof(manufacturer));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
// ...
}
IHttpContextAccessor
会被注入Manufacturer
并传递给基地DbObject
:
public class Manufacturer : DbObject<Manufacturer>
{
public Manufacturer(IHttpContextAccessor contextAccessor) : base(contextAccessor)
{
}
}
这是清单中最干净的解决方案。您以经典方式使用DI并使用所有benefits DI provides。
如果一个控制器可以使用许多不同的数据对象,则可以注入创建数据对象实例的工厂对象。它可以是基于IServiceProvider
的简单实现:
public interface IDbObjectFactory
{
TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>;
}
public class DbObjectFactory : IDbObjectFactory
{
private readonly IServiceProvider serviceProvider;
public DbObjectFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
}
public TDbObject Create<TDbObject>() where TDbObject : DbObject<TDbObject>
{
return serviceProvider.GetRequiredService<TDbObject>();
}
}
public class Manufacturer2Controller : Controller
{
private readonly IDbObjectFactory dbObjectFactory;
public Manufacturer2Controller(IDbObjectFactory dbObjectFactory)
{
this.dbObjectFactory = dbObjectFactory ?? throw new ArgumentNullException(nameof(dbObjectFactory));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = dbObjectFactory.Create<Manufacturer>();
var manufacturers = await manufacturer.SelectMany();
return Ok(manufacturers);
}
}
Manufacturer
和DbObject
的代码与第一个选项相比没有变化。
我认为没有任何理由不使用选项#1或#2。然而,为了完成图片,我将描述另外两个选项。
将IHttpContextAccessor
注入到conroller中,并将此实例(或IHttpContextAccessor.HttpContext.User
)传递给使用运算符new
调用的数据对象构造函数:
public class Manufacturer3Controller : Controller
{
private readonly IHttpContextAccessor contextAccessor;
public Manufacturer3Controller(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
[HttpGet]
public async Task<IActionResult> Get()
{
var manufacturer = await new Manufacturer(contextAccessor).SelectMany();
// or
// var manufacturer = await new Manufacturer(contextAccessor.HttpContext.User).SelectMany();
return Ok(manufacturer);
}
}
这是一个糟糕的解决方案,因为您不在Manufacturer
使用依赖注入,而是松散了许多advantages that DI provides。
最糟糕的选择是使用注入IHttpContextAccessor
的静态对象工厂。通过这种方法,您也可以放弃DI的好处。此外,您在Startup
中的某个地方获得了丑陋的代码,用于初始化IHttpContextAccessor
的静态实例。当你采用这种方法时,你会发现这不是很优雅的方法。
我的建议:使用选项#1,直到你有充分的理由反对它。然后使用选项#2。
以下是Sample Project on GitHub,其中包含方法## 1-3的样本。