【问题标题】:Dotnet Core 3.1 console app hosting optional Web API for controlDotnet Core 3.1 控制台应用程序托管可选的 Web API 以进行控制
【发布时间】:2020-10-23 16:29:42
【问题描述】:

我正在用 Dotnet Core 3.1 编写一个控制台应用程序。它已配置为使用 Microsoft.Extensions.DependencyInjection 以下列方式使用依赖注入:

public static class Program
{
  public static IServiceProvider ServiceProvider { get; private set; }

  public static int Main(string[] args)
  {
    // ...
    ServiceProvider = ConfigureServices().BuildServiceProvider();
    // ...
  }

  public static IServiceCollection ConfigureServices()
  {
    return new ServiceCollection()
      .AddLogging(cfg =>
      {
        // ...
      }
      // ...
  }
}

我正在尝试设置一个简单的 HTTP API 来提供对应用程序的一些基本控制。我想避免使用 ASP.Net MVC 或任何太重的东西。我只需要能够发出简单的指令并获得基本状态。它将全部是 JSON - 不需要 Razor 或类似的东西。

我还有两个(未完成的)课程:

public class ApiRunner
{
  public IWebHost WebHost { get; }

  public ApiRunner()
  {
    WebHost = new WebHostBuilder()
      .UseKestrel()
      .UseUrls("http://*:5000")
      .UseStartup<ApiStartup>()
      .Build();
  }

  public void Start()
  {
    Task.Run(() => WebHost.Run());
  }

  public void Stop()
  {
    WebHost.StopAsync();
  }
}

public class ApiStartup
{
  public void Configure(IApplicationBuilder app)
  {
    app.UseRouter(r =>
    {
      r.MapGet("/", async (request, response, routeData) =>
      {
        response.Headers["content-type"] = "text/plan";
        response.WriteAsync("Hello World!");
      });
    }
  }
}

除非我添加到我的ApiStartup 类,否则上述方法不起作用:

public void ConfigureServices(IServiceCollection services)
{
  services.AddRouting();
}

但这似乎有两个 DI 堆栈在彼此之上运行:一个用于主程序,一个用于 API。我确实尝试将services.AddRouting(); 添加到Program.cs 中的主DI 配置中,但是(1)这不起作用 - 我得到了与根本没有它时相同的异常,这让我相信API 想要使用自己的 DI,并且 (2) 我不一定想用我认为是一个独立模块的 API 特定服务来污染我的主要 DI。

我只需要一个在我的控制台应用程序中运行的轻量级 HTTP 服务器,它允许我发出简单的命令并获取状态。我可以请一些指示如何实现这一目标吗?谢谢。

【问题讨论】:

    标签: c# api asp.net-core .net-core kestrel-http-server


    【解决方案1】:

    首先,每个 ASP.NET Core 应用程序都是控制台应用程序,并且只会成为注册了 DI 和相关服务的 Web 应用程序。

    其次,您没有遵循服务注册的标准模式;无需自己实例化服务集合,WebHostBuilder 已经先完成了。只在 ApiStartup 类中注册服务。所以是的,你在两个地方注册。查看示例以及日志配置演示的额外好处:

    https://github.com/akovac35/Logging.Samples/tree/master/WebApp

    【讨论】:

      【解决方案2】:

      据我所知,如果您使用了 WebHostBuilder,它会为您的应用程序添加一些常用服务。

      WebHostBuilder 构建方法将通过调用 BuildCommonServices 方法() 注册公共服务服务,如记录器、路由或其他。

      在我看来,没有必要再次创建服务ServiceProvider,因为asp.core已经做了同样的事情(Startup.cs配置服务。)。如果您不想要其他服务,如 razor 或其他,您不能在 Startup 配置服务方法中添加 razor 服务,只需使用 services.AddControllers(); 方法,或者您可以创建一个自定义 api 服务,您可以将其用于您的 web api,它不会'不包含任何剃刀相关的结果。

      以下是虚拟主机的部分源代码。

      网页生成器源代码:

      public IWebHost Build()
          {
            if (this._webHostBuilt)
              throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
            this._webHostBuilt = true;
            AggregateException hostingStartupErrors;
            IServiceCollection serviceCollection1 = this.BuildCommonServices(out hostingStartupErrors);
            IServiceCollection serviceCollection2 = serviceCollection1.Clone();
            IServiceProvider providerFromFactory = GetProviderFromFactory(serviceCollection1);
            .....
      
          WebHost webHost = new WebHost(serviceCollection2, providerFromFactory, this._options, this._config, hostingStartupErrors);
            try
            {
              webHost.Initialize();
              return (IWebHost) webHost;
            }
            catch
            {
              webHost.Dispose();
              throw;
            }
      
          IServiceProvider GetProviderFromFactory(IServiceCollection collection)
            {
              ServiceProvider serviceProvider = collection.BuildServiceProvider();
              IServiceProviderFactory<IServiceCollection> service = ((IServiceProvider) serviceProvider).GetService<IServiceProviderFactory<IServiceCollection>>();
              if (service == null)
                return (IServiceProvider) serviceProvider;
              using (serviceProvider)
                return service.CreateServiceProvider(service.CreateBuilder(collection));
            }
          }
      

      BuildCommonServices:

      private IServiceCollection BuildCommonServices(
            out AggregateException hostingStartupErrors)
          {
              .....
           ServiceCollection services = new ServiceCollection();
              services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
              services.AddTransient<IHttpContextFactory, HttpContextFactory>();
              services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
              services.AddOptions();
              services.AddLogging();
              services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
              services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
      
          .....
      
            foreach (Action<WebHostBuilderContext, IServiceCollection> servicesDelegate in this._configureServicesDelegates)
              servicesDelegate(this._context, (IServiceCollection) services);
            return (IServiceCollection) services;
          }
      

      如何注册startup.cs:

      /// <summary>Specify the startup type to be used by the web host.</summary>
          /// <param name="hostBuilder">The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" /> to configure.</param>
          /// <param name="startupType">The <see cref="T:System.Type" /> to be used.</param>
          /// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
          public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder,Type startupType)
          {
            string name = startupType.GetTypeInfo().Assembly.GetName().Name;
            return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action<IServiceCollection>) (services =>
            {
              if (typeof (IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
                ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), startupType);
              else
                ServiceCollectionServiceExtensions.AddSingleton(services, typeof (IStartup), (Func<IServiceProvider, object>) (sp =>
                {
                  IHostingEnvironment requiredService = sp.GetRequiredService<IHostingEnvironment>();
                  return (object) new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.EnvironmentName));
                }));
            }));
          }
      
          /// <summary>Specify the startup type to be used by the web host.</summary>
          /// <param name="hostBuilder">The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" /> to configure.</param>
          /// <typeparam name="TStartup">The type containing the startup methods for the application.</typeparam>
          /// <returns>The <see cref="T:Microsoft.AspNetCore.Hosting.IWebHostBuilder" />.</returns>
          public static IWebHostBuilder UseStartup<TStartup>(this IWebHostBuilder hostBuilder)
            where TStartup : class
          {
            return hostBuilder.UseStartup(typeof (TStartup));
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-09
        • 1970-01-01
        • 2018-02-16
        • 1970-01-01
        • 2012-09-03
        • 1970-01-01
        • 2011-01-26
        • 2018-12-24
        相关资源
        最近更新 更多