c# - ASP.net core 3.1,添加对控制器动作自动绑定[FromBody]参数的依赖注入支持
问题描述
我知道如何直接注入控制器动作和控制器,方法是将服务添加到 IServiceprovider,然后框架为我处理它,在控制器动作的情况下,我可以添加[Microsoft.AspNetCore.Mvc.FromServices]
它并将服务注入特定动作。
但这需要我的控制器具体知道底层参数需要什么,这是我认为不必要且可能有害的耦合。
我想知道是否有可能接近以下内容:
[HttpPost]
public async Task<ActionResult> PostThings([FromBody]ParameterClassWithInjection parameter) {
parameter.DoStuff();
...}
public class ParameterClassWithInjection{
public readonly MyService _myService;
public ParameterClassWithInjection(IMyService service){ _myService = service;}
public void DoStuff(){ _myService.DoStuff(); }
}
我只在文档中找到了一些关于自定义模型绑定器的信息。 https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-3.1#custom-model-binder-sample
这显示了如何创建自定义绑定器并让自定义提供程序提供注入。似乎我需要从自动绑定中实现很多样板代码(在每种情况下对我来说都非常好),以便获得一些依赖注入。
如果这是唯一的选择,我希望你能给我指出一个更好的方向,或者让我的追求停下来。
解决方案
镜头切
如果内容类型是 JSON 并且您使用的是 Newtonsoft.Json,您可以使用自定义合约解析器通过依赖注入反序列化您的模型。
模型绑定
否则,如果您需要支持其他内容类型等,则需要采用复杂的方式。
对于特定型号,或仅型号FromBody
:
使用模型绑定器并进行属性注入。几周前见我的回答。
对于通用模型或其他来源的模型:
您需要一个自定义模型绑定器提供程序。在模型绑定器提供程序中,您可以遍历现有的模型绑定器提供程序,找到当前模型绑定上下文的模型绑定器,然后使用您自己的模型绑定器装饰器来装饰它,该装饰器可以对模型进行 DI。
例如:
public class DependencyInjectionModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(ModelBinderProviderContext context)
{
// Get MVC options.
var mvcOptions = context.Services.GetRequiredService<IOptions<MvcOptions>>();
// Find model binder provider for the current context.
IModelBinder binder = null;
foreach (var modelBinderProvider in mvcOptions.Value.ModelBinderProviders)
{
if (modelBinderProvider == this)
{
continue;
}
binder = modelBinderProvider.GetBinder(context);
if (binder != null)
{
break;
}
}
return binder == null
? null
: new DependencyInjectionModelBinder(binder);
}
}
// Model binder decorator.
public class DependencyInjectionModelBinder : IModelBinder
{
private readonly IModelBinder _innerBinder;
public DependencyInjectionModelBinder(IModelBinder innerBinder)
{
_innerBinder = innerBinder;
}
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
await _innerBinder.BindModelAsync(bindingContext);
if (bindingContext.Result.IsModelSet)
{
var serviceProvider = bindingContext.HttpContext.RequestServices;
// Do DI stuff.
}
}
}
// Register your model binder provider
services.AddControllers(opt =>
{
opt.ModelBinderProviders.Insert(
0, new DependencyInjectionModelBinderProvider());
});
这适用于属性注入。
对于构造函数注入,因为模型的创建仍然发生在内部模型绑定器中,所以您肯定需要比这个示例更多的代码,而且我没有尝试在这种情况下让构造函数注入工作。
推荐阅读
- typescript - 如何让 VScode 识别 d.ts 文件而不直接引用它们
- linux - 使用 IP 对文件进行排序
- ruby - 如何在 Ruby 中检查用户的 root 权限?
- php - 更改多维数组值
- javascript - 带有来自 HTML 表的数据的 Highchart(饼图)不显示图例
- php - 尝试使用 Azure SQL 服务器时 Laravel 出现问题;count():参数必须是数组或者实现了Countable的对象
- php - 尝试创建图像表时出现 CSS 问题
- javascript - 两个定时 jQuery 事件
- spring - Spring 5:没有 Spring Boot 的 Restful API
- php - 变量产品的 Laravel 雄辩关系