.NET Core 3.0 IHostedService访问Web服务器URL - 方案、主机、端口等。

6

我的问题很简单,我想在IHostedService中访问服务器URL。

我找不到方法去实现它。因为没有请求,所以我不能使用IHttpContextAccessor

IServer的特性属性没有任何地址,我无计可施。

我不想在配置中硬编码服务器的URL。

我正在运行的.NET Core版本是3.0。


1
我的意思是,你可以将 IHttpContextAccessor 注入到你的类中... 如果你能提供更多关于何时何地需要这些信息的上下文,我认为我们可以更好地帮助你。 - Heretic Monkey
我需要在IHostedService中执行,该服务会在应用程序启动后立即执行。IHttpContextAccessor需要一个请求并且可以在控制器中使用。但是在IHostedService中,我没有请求。 - ppavlov
1
长话短说,你做不到。请参见如何在ASP.NET Core的IHostedService中获取网站URL或访问HttpContext? - Heretic Monkey
还有其他方法吗?不使用HttpContext的方法? - ppavlov
4个回答

4
你可以在 Startup.cs 中使用依赖注入框架注册托管服务:
public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IHostedService, MyHostedService>();
}

然后,您可以将IServer注入到托管服务中,并使用IServerAddressesFeature获取地址:

public class MyHostedService : IHostedService
{
    private readonly IServer _server;

    public MyHostedService(IServer server)
    {
       _server = server;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
       var features = _server.Features;
       var addresses = features.Get<IServerAddressesFeature>();
       var address = addresses.Addresses.FirstOrDefault(); // = http://localhost:5000
    }
}

1
在使用默认的Web服务器构建程序时,即使有lauchSettings.json和IIS Express配置文件,在本地运行IIS Express时,Asp.Net Core 3.1中的IServerAddressesFeature中没有地址。 - gfache
1
当执行此操作时,ASP.NET Core 6 中的地址为空。 - Swimburger

1
在.NET 6中,我找不到一种影响托管服务启动顺序的方法,所以我必须找到另一种方法。 幸运的是,有一个名为IHostApplicationLifetime的对象,它允许您钩入ApplicationStarted生命周期事件。 奇怪的是,ApplicationStarted是一个CancellationToken,而不是C#事件。 您可以使用取消令牌上的Register方法在Web应用程序启动时运行代码。 此时,URL将填充到IServer对象上。
以下是一个使用IHostedService的示例:
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;

public class MyHostedService : IHostedService
{
    private readonly IServer _server;
    private readonly IHostApplicationLifetime _hostApplicationLifetime;

    public MyHostedService(IServer server, IHostApplicationLifetime hostApplicationLifetime)
    {
        _server = server;
        _hostApplicationLifetime = hostApplicationLifetime;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine($"Address before application has started: {GetAddress()}");

        _hostApplicationLifetime.ApplicationStarted.Register(
                () => Console.WriteLine($"Address after application has started: {GetAddress()}"));
        
        return Task.CompletedTask;
    }

    private string GetAddress()
    {
        var features = _server.Features;
        var addresses = features.Get<IServerAddressesFeature>();
        var address = addresses.Addresses.FirstOrDefault();
        return address;
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

当您运行应用程序时,输出将如下所示:
Address before application has started:
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7012
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5012
Address after application has started: https://localhost:7012
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Users\niels\RiderProjects\UrlsInHostedService\UrlsInHostedService\

正如您从输出中看到的那样,在托管服务启动时,IServer 上没有地址,但是随后服务器启动,ApplicationStarted 取消标记被取消,触发回调函数,现在 IServer 上有网址。
在我的情况下,我实际上需要它用于 BackgroundService,在这种情况下,我认为它的效果更好,因为您可以创建自己的任务并等待它。以下是一个示例:
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;

public class MyBackgroundService : BackgroundService
{
    private readonly IServer _server;
    private readonly IHostApplicationLifetime _hostApplicationLifetime;

    public MyBackgroundService(IServer server, IHostApplicationLifetime hostApplicationLifetime)
    {
        _server = server;
        _hostApplicationLifetime = hostApplicationLifetime;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        Console.WriteLine($"Address before application has started: {GetAddress()}");

        await WaitForApplicationStarted();
        
        Console.WriteLine($"Address after application has started: {GetAddress()}");
    }

    private string GetAddress()
    {
        var features = _server.Features;
        var addresses = features.Get<IServerAddressesFeature>();
        var address = addresses.Addresses.FirstOrDefault();
        return address;
    }    
    
    private Task WaitForApplicationStarted()
    {
        var completionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
        _hostApplicationLifetime.ApplicationStarted.Register(() => completionSource.TrySetResult());
        return completionSource.Task;
    }
}

结果将与托管服务相同,但您必须使用BackgroundServiceExecuteAsync,这可能对您的用例没有意义。

1
对于 Asp.Net Core 3.1,您无法在 IHostedService 中从 IServerAddressesFeature 获取地址,因为 IHostedServiceServer 之前启动。请参见此 issue

发生变化的是调用 IHostedService 的顺序。这在 3.0 中被移动了,但仍然在 Configure 之后运行。

为了在您的 IHostedService 中获取地址,您可以使它们在 Startup 之后运行。我发现其中一种方法是在 Program.cs 中注册您的服务,详见 here
public class Program
{
    public static void Main(string[] args)
        => CreateHostBuilder(args).Build().Run();

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => // The GenericWebHostSevice is registered here
            {
                webBuilder.UseStartup<Startup>();
            })
            // Register your HostedService AFTER ConfigureWebHostDefaults
            .ConfigureServices(
                services => services.AddHostedService<ProgramHostedService>());
}

1
在完全启动服务器之前,您无法从服务器获取地址/ URL。
如果您需要本地主机地址,请从“./Properties/launchSettings.json”中查找(以编程或其他方式)。
如果您需要在实时环境中的地址,则必须等待服务器加载。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接