簡體   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