• .NET Web 宿主(Web Host)【下】


    此为系列文章,对MSDN ASP.NET Core 的官方文档进行系统学习与翻译。其中或许会添加本人对 ASP.NET Core 的浅显理解。

    重载配置

           使用Configuration来配置一个Web 宿主。在下面的示例中,宿主配置以可选的形式在hostsettings.json 文件中指定。从hostsettings.json文件中加载的任何配置都可能被命令行参数重写。内置的配置(config 文件)被UseConfiguration 用来配置宿主。IWebHostBuilder 配置被添加到app的配置中,然而反过来却是不正确的。ConfigureAppConfiguration 不会影响IWebHostBuilder 配置。

          首先我们重载由UseUrls 以hostsettings.json 文件形式提供的配置,然后是命令行参数:

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }
    
        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("hostsettings.json", optional: true)
                .AddCommandLine(args)
                .Build();
    
            return WebHost.CreateDefaultBuilder(args)
                .UseUrls("http://*:5000")
                .UseConfiguration(config)
                .Configure(app =>
                {
                    app.Run(context => 
                        context.Response.WriteAsync("Hello, World!"));
                });
        }
    }

            hostsettings.json

    {
        urls: "http://*:5005"
    }

           注意:UseConfiguration 只会从提供的IConfiguration中拷贝键值到宿主构造器配置中。因此,为JSON, INI, and XML设置文件设置reloadOnChange: true是没有任何效果的。

          为了指定宿主运行在一个特定的URL上,当执行dotnet run 命令时,所需要的值可以从命令提示符中传递进来。命令行参数重载了从hostsettings.json文件获取到的urls值,使得服务会监听8080端口:

    dotnet run --urls "http://*:8080"

    管理宿主

    Run

         Run方法启动了web app,并且阻塞了调用线程直到宿主关闭。

    host.Run();

    Start

          通过调用Start方法来使宿主以非阻塞的方式运行。

    using (host)
    {
        host.Start();
        Console.ReadLine();
    }

           如果一个URLs列表被传递给Start方法,它便会监听指定的URLs。

    var urls = new List<string>()
    {
        "http://*:5000",
        "http://localhost:5001"
    };
    
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseStartup<Startup>()
        .Start(urls.ToArray());
    
    using (host)
    {
        Console.ReadLine();
    }

            使用一个静态的便捷方法,app可以初始化并且开启一个宿主,其使用了CreateDefaultBuilder的预配置的默认值。这些方法启动了服务并且没有控制台输出,并使用WaitForShutdown 来等待一个中断(Ctrl-C/SIGINT 或 SIGTERM)。

    Start(RequestDelegate app)

           使用一个RequestDelegate来启动。

    using (var host = WebHost.Start(app => app.Response.WriteAsync("Hello, World!")))
    {
        Console.WriteLine("Use Ctrl-C to shutdown the host...");
        host.WaitForShutdown();
    }
    

           在浏览器中请求http://localhost:5000,会收到一个响应:“Hello World”。WaitForShutdown方法会被阻塞,直到一个中断(Ctrl-C/SIGINT 或SIGTERM)发出。app显示了一个Console.WriteLine方法并等待任何一个按键来退出。

    Start(string url, RequestDelegate app)

          以一个URL和一个RequestDelegate来启动。

    using (var host = WebHost.Start("http://localhost:8080", app => app.Response.WriteAsync("Hello, World!")))
    {
        Console.WriteLine("Use Ctrl-C to shutdown the host...");
        host.WaitForShutdown();
    }

           和Start(RequestDelegate app) 产生一样的结果,唯一不同之处在于app响应这个请求:http://localhost:8080

    Start(Action<IRouteBuilder> routeBuilder)

           使用一个IRouteBuilder(Microsoft.AspNetCore.Routing的实例来使用路由中间件。

    using (var host = WebHost.Start(router => router
        .MapGet("hello/{name}", (req, res, data) => 
            res.WriteAsync($"Hello, {data.Values["name"]}!"))
        .MapGet("buenosdias/{name}", (req, res, data) => 
            res.WriteAsync($"Buenos dias, {data.Values["name"]}!"))
        .MapGet("throw/{message?}", (req, res, data) => 
            throw new Exception((string)data.Values["message"] ?? "Uh oh!"))
        .MapGet("{greeting}/{name}", (req, res, data) => 
            res.WriteAsync($"{data.Values["greeting"]}, {data.Values["name"]}!"))
        .MapGet("", (req, res, data) => res.WriteAsync("Hello, World!"))))
    {
        Console.WriteLine("Use Ctrl-C to shutdown the host...");
        host.WaitForShutdown();
    }

            以示例代码来使用如下的浏览器请求:

                                    

    RequestResponse
    http://localhost:5000/hello/Martin Hello, Martin!
    http://localhost:5000/buenosdias/Catrina Buenos dias, Catrina!
    http://localhost:5000/throw/ooops! Throws an exception with string "ooops!"
    http://localhost:5000/throw Throws an exception with string "Uh oh!"
    http://localhost:5000/Sante/Kevin Sante, Kevin!
    http://localhost:5000 Hello World!

             WaitForShutdown方法会被阻塞,直到一个中断(Ctrl-C/SIGINT 或SIGTERM)发出。app显示了一个Console.WriteLine方法并等待任何一个按键来退出。

    Start(string url, Action<IRouteBuilder> routeBuilder)

             使用一个URL和一个IRouteBuilder实例:

    using (var host = WebHost.Start("http://localhost:8080", router => router
        .MapGet("hello/{name}", (req, res, data) => 
            res.WriteAsync($"Hello, {data.Values["name"]}!"))
        .MapGet("buenosdias/{name}", (req, res, data) => 
            res.WriteAsync($"Buenos dias, {data.Values["name"]}!"))
        .MapGet("throw/{message?}", (req, res, data) => 
            throw new Exception((string)data.Values["message"] ?? "Uh oh!"))
        .MapGet("{greeting}/{name}", (req, res, data) => 
            res.WriteAsync($"{data.Values["greeting"]}, {data.Values["name"]}!"))
        .MapGet("", (req, res, data) => res.WriteAsync("Hello, World!"))))
    {
        Console.WriteLine("Use Ctrl-C to shut down the host...");
        host.WaitForShutdown();
    }

            和Start(Action<IRouteBuilder> routeBuilder) 产生一样的结果,唯一不同之处在于app响应来自 http://localhost:8080的请求。

    StartWith(Action<IApplicationBuilder> app)

            提供一个委托来配置IApplicationBuilder

    using (var host = WebHost.StartWith(app => 
        app.Use(next => 
        {
            return async context => 
            {
                await context.Response.WriteAsync("Hello World!");
            };
        })))
    {
        Console.WriteLine("Use Ctrl-C to shut down the host...");
        host.WaitForShutdown();
    }

              在浏览器中请求http://localhost:5000,会收到一个响应:“Hello World”。WaitForShutdown方法会被阻塞,直到一个中断(Ctrl-C/SIGINT 或SIGTERM)发出。app显示了一个Console.WriteLine方法并等待任何一个按键来退出。

    StartWith(string url, Action<IApplicationBuilder> app)

            提供了一个URL 和一个委托来配置IApplicationBuilder

    using (var host = WebHost.StartWith("http://localhost:8080", app => 
        app.Use(next => 
        {
            return async context => 
            {
                await context.Response.WriteAsync("Hello World!");
            };
        })))
    {
        Console.WriteLine("Use Ctrl-C to shut down the host...");
        host.WaitForShutdown();
    }

            与StartWith(Action<IApplicationBuilder> app)产生一样的结果,唯一不同之处在于app响应来自http://localhost:8080的请求。

    接口IWebHostEnvironment

             接口IWebHostEnvironment提供了关于app寄宿环境的信息。我们可以使用构造函数注入来获取IWebHostEnvironment对象,这样便可以使用它的属性和扩展方法。

    public class CustomFileReader
    {
        private readonly IWebHostEnvironment _env;
    
        public CustomFileReader(IWebHostEnvironment env)
        {
            _env = env;
        }
    
        public string ReadFile(string filePath)
        {
            var fileProvider = _env.WebRootFileProvider;
            // Process the file here
        }
    }

             convention-based approach 可以用来在启动时候基于环境配置app。或者,将IWebHostEnvironment注入到Startup构造函数中,之后便可以在ConfigureServices中进行使用。

    public class Startup
    {
        public Startup(IWebHostEnvironment env)
        {
            HostingEnvironment = env;
        }
    
        public IWebHostEnvironment HostingEnvironment { get; }
    
        public void ConfigureServices(IServiceCollection services)
        {
            if (HostingEnvironment.IsDevelopment())
            {
                // Development configuration
            }
            else
            {
                // Staging/Production configuration
            }
    
            var contentRootPath = HostingEnvironment.ContentRootPath;
        }
    }

            注意:除了IsDevelopment扩展方法,IWebHostEnvironment 还提供了IsStaging,IsProduction以及IsEnvironment(string environmentName)方法。更多信息,请参考Use multiple environments in ASP.NET Core

           IWebHostEnvironment 服务也可以直接注入到Configure 方法,以用来建立请求处理管道:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            // In Development, use the Developer Exception Page
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // In Staging/Production, route exceptions to /error
            app.UseExceptionHandler("/error");
        }
    
        var contentRootPath = env.ContentRootPath;
    }

            当创建自定义中间件时,IWebHostEnvironment 也可以被注入到Invoke 方法:

    public async Task Invoke(HttpContext context, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            // Configure middleware for Development
        }
        else
        {
            // Configure middleware for Staging/Production
        }
    
        var contentRootPath = env.ContentRootPath;
    }

    接口IHostApplicationLifetime

           接口IHostApplicationLifetime运行启动后和关闭活动。接口上的三个属性是取消令牌,它们用来注册定义了开始和结束事件的Action方法。

             

    Cancellation TokenTriggered when…
    ApplicationStarted The host has fully started.
    ApplicationStopped The host is completing a graceful shutdown. All requests should be processed. Shutdown blocks until this event completes.
    ApplicationStopping The host is performing a graceful shutdown. Requests may still be processing. Shutdown blocks until this event completes.
    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostApplicationLifetime appLifetime)
        {
            appLifetime.ApplicationStarted.Register(OnStarted);
            appLifetime.ApplicationStopping.Register(OnStopping);
            appLifetime.ApplicationStopped.Register(OnStopped);
    
            Console.CancelKeyPress += (sender, eventArgs) =>
            {
                appLifetime.StopApplication();
                // Don't terminate the process immediately, wait for the Main thread to exit gracefully.
                eventArgs.Cancel = true;
            };
        }
    
        private void OnStarted()
        {
            // Perform post-startup activities here
        }
    
        private void OnStopping()
        {
            // Perform on-stopping activities here
        }
    
        private void OnStopped()
        {
            // Perform post-stopped activities here
        }
    }

            StopApplication 请求了app的终结。以下的类使用了StopApplication,当类的Shutdown方法被调用时,来优雅的关闭一个app:

    public class MyClass
    {
        private readonly IHostApplicationLifetime _appLifetime;
    
        public MyClass(IHostApplicationLifetime appLifetime)
        {
            _appLifetime = appLifetime;
        }
    
        public void Shutdown()
        {
            _appLifetime.StopApplication();
        }
    }

    域验证

           如果app处于开发环境,那么CreateDefaultBuilder 会将ServiceProviderOptions.ValidateScopes 设置为true。

           当ValidateScopes 设置为true时,默认的服务提供器会执行检查以验证:

    • scoped 服务不会直接或者间接从根服务提供器来解析。
    • scoped服务不会直接或者间接注入到单例服务中。

          当调用BuildServiceProvider 时会创建根服务提供器。根服务提供器的生命周期与app的生命周期保持一致。随着app的开始而启动,随着app的关闭而销毁。

          scoped 服务被创建它们的容器所销毁。如果一个scoped服务在根容器中创建,那么这个服务的生命周期便会被提升为单例,因为它仅会在app关闭时被根容器所销毁。服务域验证会在BuildServiceProvider 被调用时捕获这些情形并执行检查。

          为了总是执行域验证,包括在生产环境中,在宿主 构造器上用UseDefaultServiceProvider 来配置 ServiceProviderOptions

    WebHost.CreateDefaultBuilder(args)
        .UseDefaultServiceProvider((context, options) => {
            options.ValidateScopes = true;
        })

    额外资源    

  • 相关阅读:
    LBS 经纬度定位
    LBS 经纬度定位
    GPS定位基本原理
    GPS定位基本原理
    Android Studio 之 启动和停止服务
    Android Studio 之 启动和停止服务
    【算法】最短路——两点最短总权和
    【算法】最短路——两点最短总权和
    【郑轻】[1743]解方程
    【郑轻】[1743]解方程
  • 原文地址:https://www.cnblogs.com/qianxingmu/p/12455008.html
Copyright © 2020-2023  润新知