首页 > 解决方案 > 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

这显示了如何创建自定义绑定器并让自定义提供程序提供注入。似乎我需要从自动绑定中实现很多样板代码(在每种情况下对我来说都非常好),以便获得一些依赖注入。

如果这是唯一的选择,我希望你能给我指出一个更好的方向,或者让我的追求停下来。

标签: c#asp.net-coredependency-injectioncontrollermodel-binding

解决方案


镜头切

如果内容类型是 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());
});

这适用于属性注入。

对于构造函数注入,因为模型的创建仍然发生在内部模型绑定器中,所以您肯定需要比这个示例更多的代码,而且我没有尝试在这种情况下让构造函数注入工作。


推荐阅读