繁体   English   中英

ASP.net 核心 3.1,添加对 controller 动作自动绑定 [FromBody] 参数的依赖注入支持

[英]ASP.net core 3.1, add dependency injection support for controller actions auto bound [FromBody] parameters

I know how to inject into controller actions and the controller directly, by adding the service to the IServiceprovider and then the framework just handles it for me and in the case of controller actions I could add [Microsoft.AspNetCore.Mvc.FromServices] and it将服务注入特定操作。

但这需要我的 controller 明确知道底层参数需要什么,这是我认为不必要且可能有害的耦合。

我想知道是否有可能接近以下内容:

[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(); }
}

我只在文档中找到了一些关于自定义 model 活页夹的信息。 https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding?view=aspnetcore-3.1#custom-model-binder-sample

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

如果这是唯一的选择,我希望你能给我指出一个更好的方向,或者把我的任务放在 rest 上。

截图

如果内容类型是 JSON 并且您使用的是 Newtonsoft.Json,您可以使用自定义合同解析器通过依赖注入反序列化您的 model

Model 绑定

否则,如果您需要支持其他内容类型等,则需要以复杂的方式 go。

对于特定 model 或仅型号FromBody

使用 model binder 并进行属性注入。 几周前见我的回答

对于通用 model 或其他来源的型号:

您需要一个自定义 model 活页夹提供程序。 In the model binder provider, you could iterate through existing model binder providers, find the model binder for the current model binding context, then decorate it with your own model binder decorator which can do DI for models.

例如:

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());
});

这适用于属性注入。

对于构造函数注入,由于 model 的创建仍然发生在内部 model 绑定程序中,因此您肯定需要比此示例更多的代码,并且我没有尝试在这种情况下使构造函数注入工作。

我后来选择做以下事情。

[HttpPost]
public async Task<ActionResult> PostThings([FromBody]ParameterClassWithInjection parameter, [FromServices] MyService) {
await MyService.DoStuff(parameter);  
...}

将服务注入 api 操作的位置。

然后我选择了非常小的服务,每个请求一个,以保持它非常分散。 如果这些服务需要一些共享代码,比如说来自存储库,那么我只需将其注入到这些较小的服务中。

好处包括; 在单元测试中模拟和测试非常容易,并且在不影响其他操作的情况下保持简单的更改,因为它非常明确地声明此“服务”/请求仅使用一次。

缺点是你必须创建很多类。 通过良好的文件夹结构,您可以减轻一些概览负担。

 - MyControllerFolder
    -Controller
    -Requests
     - MyFirstRequsetFolder
       - Parameter.cs
       - RequestService.cs
     ```

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM