Как запустить веб-браузер после запуска приложения ASP.NET Core?

У меня есть приложение ASP.NET Core, которое будет использоваться как клиент несколькими пользователями. Другими словами, он не будет размещен на центральном сервере, и они будут запускать опубликованный исполняемый файл в любое время, когда им нужно будет использовать приложение.

В файле Program.cs есть следующее:

var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

host.Run();

Я бы хотел, чтобы веб-браузер по умолчанию автоматически открывался, чтобы избежать избыточного шага пользователя, который должен открыть браузер и вручную ввести адрес http://localhost:5000.

Каким будет лучший способ достичь этого? Вызов Program.Start после вызова Run() не будет работать, потому что Run блокирует поток.

Ответ 1

У вас есть две разные проблемы здесь:

Блокировка тем

host.Run() действительно блокирует основной поток. Поэтому используйте host.Start() (или await StartAsync в 2.x) вместо host.Run().

Как запустить веб-браузер

Если вы используете ASP.NET Core поверх .NET Framework 4.x, Microsoft говорит, что вы можете просто использовать:

Process.Start("http://localhost:5000");

Но если вы ориентируетесь на мультиплатформенный .NET Core, приведенная выше строка не будет выполнена. Не существует единого решения, использующего .NET Standard, которое работает на любой платформе. Решение только для Windows:

System.Diagnostics.Process.Start("cmd", "/C start http://google.com");

Изменение: я создал тикет, и разработчик MS ответил, что на сегодняшний день, если вам нужна многоплатформенная версия, вы должны сделать это вручную, например:

public static void OpenBrowser(string url)
{
    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        Process.Start(new ProcessStartInfo("cmd", $"/c start {url.Replace("&", "^&")}")); // Works ok on windows and escape need for cmd.exe
    }
    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
    {
        Process.Start("xdg-open", url);  // Works ok on linux
    }
    else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
    {
        Process.Start("open", url); // Not tested
    }
    else
    {
        ...
    }
}

Теперь все вместе:

using System.Threading;

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Start();
        OpenBrowser("http://localhost:5000/");
    }

    public static void OpenBrowser(string url)
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
        {
            Process.Start(new ProcessStartInfo("cmd", $"/c start {url}"));
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
        {
            Process.Start("xdg-open", url);
        }
        else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
        {
            Process.Start("open", url);
        }
        else
        {
            // throw 
        }
    }
}

Ответ 2

Вы можете запустить процесс веб-браузера прямо перед host.Run(). Это работает с Chrome и, возможно, с другими браузерами:

public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .Build();

    var browserExecutable = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
    Process.Start(browserExecutable, "http://localhost:5000");

    host.Run();
}

В случае Chrome новое окно будет ждать, пока сервер запустится, а затем подключится и отобразит приложение.

В зависимости от конфигурации вашей системы вы можете сделать Process.Start("http://localhost:5000"), чтобы запустить браузер по умолчанию, а не hardcoding путь к исполняемому файлу. Почему-то это не сработало для меня. Вы также можете вытащить путь к браузеру из реестра или из файла конфигурации.

Ответ 3

Еще один вариант здесь - разрешить объект IApplicationLifetime в Startup.Configure и зарегистрировать обратный вызов на ApplicationStarted. Это событие запускается, когда хост развернулся и прослушивает.

public void Configure(IApplicationBuilder app, IApplicationLifetime appLifetime)
{
    appLifetime.ApplicationStarted.Register(() => OpenBrowser(
        app.ServerFeatures.Get<IServerAddressesFeature>().Addresses.First()));
}

private static void OpenBrowser(string url)
{
    Process.Start(
        new ProcessStartInfo("cmd", $"/c start {url}") 
        {
            CreateNoWindow = true 
        });
}

Ответ 4

Принятый ответ хорош, но из-за отсутствия блокировки программа сразу закончится, остановив сервер. Здесь отвечает версия, адаптированная от Херардо и Ивана.

Он создаст сервер, запустит браузер, когда сервер начнет прослушивать, и блокируется до тех пор, пока сервер не закончится:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using System.Diagnostics;
using Microsoft.AspNetCore.Builder;
using static System.Runtime.InteropServices.RuntimeInformation;
using static System.Runtime.InteropServices.OSPlatform;

class Program
{
    static void Main(string[] args)
    {
        string url = "http://localhost:54321/";

        using (var server = CreateServer(args, url))
        {
            StartBrowserWhenServerStarts(server, url);
            server.Run(); //blocks
        }
    }

    /// <summary>
    /// Create the kestrel server, but don't start it
    /// </summary>
    private static IWebHost CreateServer(string[] args, string url) => WebHost
        .CreateDefaultBuilder(args)
        .UseStartup<Startup>()
        .UseUrls(url)
        .Build();

    /// <summary>
    /// Register a browser to launch when the server is listening
    /// </summary>
    private static void StartBrowserWhenServerStarts(IWebHost server, string url)
    {
        var serverLifetime = server.Services.GetService(typeof(IApplicationLifetime)) as IApplicationLifetime;
        serverLifetime.ApplicationStarted.Register(() =>
        {
            var browser =
                IsOSPlatform(Windows) ? new ProcessStartInfo("cmd", $"/c start {url}") :
                IsOSPlatform(OSX) ? new ProcessStartInfo("open", url) :
                new ProcessStartInfo("xdg-open", url); //linux, unix-like

            Process.Start(browser);
        });
    }
}