在.net核心应用程序中自托管HTTP(s)端点而不使用asp.net?

Posted

技术标签:

【中文标题】在.net核心应用程序中自托管HTTP(s)端点而不使用asp.net?【英文标题】:Self hosting HTTP(s) endpoints in .net core app without using asp.net? 【发布时间】:2020-07-14 06:21:15 【问题描述】:

我还有一个在 Windows 和 Linux 上运行的 .net 核心应用程序(我使用 .net 核心运行时 >= 2.1)。为了获得更好的见解,我想公开一个指标端点(简单的 HTTP GET 端点),以便 Prometheus 发布我的应用程序的一些内部统计信息。

搜索万维网,所以我总是最终使用 asp.net 核心。由于我只想向现有的 .net 核心应用程序添加一个非常简单的 HTTP GET 端点,因此将整个应用程序移植到 asp.net 似乎有点过头了。

我已经考虑过的替代方案是基于 HttpListener 编写我自己的处理程序。当涉及到一个简单的 HTTP 端点时,这是非常直接的,但由于我找到的有关 SSL 和 Linux 的所有信息都是,所以目前不支持,我应该使用 asp.net。 (https://github.com/dotnet/runtime/issues/33288#issuecomment-595812935)

所以我想知道我误解了什么!我是唯一一个? 是否已经有一个很好的库为 .net 核心提供简单的 http(s) 服务器?

编辑:[已解决] 正如@ADyson 在下面的 cmets 中提到的,现有应用程序不需要移植到 asp.net core!

2.1版本自动添加dotnet new web生成的项目文件 对"Microsoft.AspNetCore.App""Microsoft.AspNetCore.Razor.Design" 的引用

当我从 .net 核心项目中引用我的 asp.net 核心项目并执行托管 Web 服务的代码时,我最终得到了一个 System.IO.FileNotFoundException 声明它 "Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Core'"

Microsoft.AspNetCore.App 是一个元包,也引用了Microsoft.AspNetCore.MVC!因此,执行程序集也必须引用此元包。这个观察误导了我,使用 asp.net core 会使我的整个应用程序围绕Microsoft.AspNetCore.App 构建。

删除这些引用并仅添加对 "Microsoft.AspNetCore" 的引用后,一切正常。

在 3.1 版中检查了从 dotnet new web 生成的项目文件后,这些引用没有被添加。对于使用较新版本 dotnet 的人来说,这不是问题!

【问题讨论】:

您不需要将整个应用程序移植到 asp.net。您是否尝试过使用 OWIN 在另一个应用程序中自托管?最终,尽管您编写的为 Web 应用程序服务的部分几乎肯定会依赖 asp.net 库——这是 .NET 提供 HTTP 功能的方式。你不需要创建一个完整的 MVC 设置或类似的东西。 他们添加了最新的 .net 核心 WorkerServices 尚未使用它们,但它们看起来像您需要的。例如Host ASP.NET Core in a Windows Service。我建议检查一下related answer @ADyson 我看过 OWIN。虽然 conecerns 的概念和分离在其设计中听起来很有希望,但我找不到在 .net 核心应用程序中使用它的方法,因为它仅适用于完整的 .net 框架 (github.com/aspnet/AspNetKatana/blob/dev/src/…) @pocketbroadcast 这不是真的,as far as I can tell。这是使用 .NET Core / .NET Standard 进行自托管的working example。请记住,OWIN 只是一个标准,而 ASP.NET Core 本身就实现了很多 OWIN 标准,而不需要那么多额外的库。 @ADyson 是的,你完全正确!这都是我的错。我将 wearg nuget 包添加到我的项目中,从而将我带到了一个 asp.net 存储库,而不是适当的 asp.net 核心存储库。应该更好地仔细检查。感谢您指出这一点。 【参考方案1】:

正如@ADyson 所提到的,OWIN 是要走的路。您可以轻松地在现有应用程序中自托管 HTTP 端点。这是一个在 .Net Core 3.1 控制台应用程序中自托管它的示例。它公开了一个简单的端点,使用控制器在端口 5000 上侦听 GET 请求。您只需要安装Microsoft.AspNetCore.Owin Nuget 包即可。

代码文件结构如下:

.
├── Program.cs
├── Startup.cs
├── Controllers
    ├── SayHi.cs

程序.cs

using Microsoft.AspNetCore.Hosting;

namespace WebApp

    class Program
    
        static void Main(string[] args)
        
            var host = new WebHostBuilder()
                .UseKestrel()
                .UseUrls("http://*:5000")
                .UseStartup<Startup>()
                .Build();

            host.Run();
        
    

Startup.cs

using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace WebApp

    public class Startup
    
        public void ConfigureServices(IServiceCollection services)
        
            services.AddControllers();
        

        public void Configure(IApplicationBuilder app)
        
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            
                endpoints.MapControllers();
            );
        
    

SayHi.cs

using Microsoft.AspNetCore.Mvc;

namespace WebApp.Controllers

    public class SayHi : ControllerBase
    
        [Route("sayhi/name")]
        public IActionResult Get(string name)
        
            return Ok($"Hello name");
        
    

然后一个简单的dotnet WebApp.dll 将启动应用程序和网络服务器。 如您所见,该示例使用 Kestrel。默认网络服务器。你可以查看Microsoft的相关文档。

有关更多配置和路由选项,您可以查看Microsoft 的文档。

【讨论】:

感谢您为托管在 .net 核心控制台应用程序中的 Web 应用程序发布此 PoC,并强调除了 Microsoft.AspNetCore.Owin 之外不需要其他 NuGet 包。由于在 .net core 2.1 中安装此软件包失败,它让我检查了 dotnet 2.1 和 dotnet 3.1 中生成的项目文件,这最终导致了我的问题的 EDIT 部分中所述的解决方案。 很高兴为您提供帮助,很高兴您已经弄清楚了 :) 这很好用,您也可以在 WinForms 应用程序中执行此操作,因为现在.NET 5.0 支持。只需确保在单独的线程中执行上述代码中的host.Run() 调用,然后在主线程调用Application.Run(new Form1()) 以运行您的主窗体。您不能在同一线程上同时运行 Application.Run()host.Run(),因为它们会阻塞调用。 @Anouar 请包括您添加到控制台应用程序的所有包... 太烦人了,我无法让 vs 配置文件使用 IIS express,然后在另一个线程中执行 WebHostBuilder,并使用 Kestrel,所有端口都不同,太烦人了【参考方案2】:

一种选择是使用 EmbeddIo

https://unosquare.github.io/embedio/

我发现文档并不总是最好的,尤其是它们最近升级并且许多示例等无效。但你可以到达那里!

您可以像这样自行托管 REST API:

 WebServer ws = new WebServer(o => o
        .WithUrlPrefix(url)
        .WithMode(HttpListenerMode.EmbedIO))
        .WithWebApi("/api", m => m
        .WithController<ApiController>());

this.Cts = new CancellationTokenSource();
var task = Webserver.RunAsync(Cts.Token);

然后像这样定义你的 API 控制器。

class ApiController : WebApiController
 
        public ApiController() : base()
        

        

        [Route(HttpVerbs.Get, "/hello")]
        public async Task HelloWorld()
        
            string ret;
            try
            
                ret = "Hello from webserver @ " + DateTime.Now.ToLongTimeString();
            
            catch (Exception ex)
            
                //
            
            await HttpContext.SendDataAsync(ret);

        

【讨论】:

感谢您将我指向 NancyFx。框架看起来很棒!尤其是带有.WithController&lt;ApiController&gt; 的部分可以很容易地跟踪哪个控制器处理哪个端点。我一定会看看的!【参考方案3】:

在 2.1 版中使用 dotnet new web 生成的项目文件自动添加了对 "Microsoft.AspNetCore.App""Microsoft.AspNetCore.Razor.Design" 的引用,当被 .net 核心项目引用并执行时,最终以 System.IO.FileNotFoundException 声明它 "Could not load file or assembly 'Microsoft.AspNetCore.Mvc.Core'"

在 3.1 版本中使用 dotnet new web 创建项目不会引用这些,因此可以从 .net 核心应用程序引用和执行该项目。

-> 使用 asp.net core 对我来说是一个可行的解决方案(再次)!

【讨论】:

以上是关于在.net核心应用程序中自托管HTTP(s)端点而不使用asp.net?的主要内容,如果未能解决你的问题,请参考以下文章

连接到 Windows 服务中自托管的 SignalR 的问题

Azure 不显示托管 .net 核心 API 的自定义错误消息

.net 核心 nginx 托管套接字不允许 http post

构建/发布 web .net 核心 api:我无法访问他们从邮递员返回 500 的任何端点

在.NET 6.0中使用不同的托管模型

WCF 托管在 Web 应用程序和兼容模式中