Dotnet Core 3.1控制台应用程序托管用于控制的可选Web API

时间:2020-07-03 01:13:31

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

我正在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服务器,该服务器允许我发出简单的命令并获取状态。请问有什么指示可以实现这一目标吗?谢谢。

2 个答案:

答案 0 :(得分:0)

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

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

我认为,由于asp.core已经完成了相同的事情(Startup.cs配置服务),因此无需再次创建服务ServiceProvider。如果您不希望使用剃刀等其他服务,则不能仅使用services.AddControllers();方法在“启动配置服务”方法中添加剃刀服务,也可以创建可用于Web api的自定义api服务不包含任何与剃刀相关的结果。

以下是网络托管服务商的部分源代码。

Web构建器源代码:

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

答案 1 :(得分:0)

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

第二,您没有遵循服务注册的标准模式;不需要自己实例化服务集合,WebHostBuilder已经首先完成了它。仅在ApiStartup类中注册服务。是的,您正在两个地方注册。参见示例,该示例具有记录配置演示的额外好处:

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