ASP.NET Core 核心特性--宿主、启动、中间件

时间:2021-05-25 15:17:21

宿主

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

CreateDefaultBuilder 创建 IHostBuilder 对象时所包含的主要默认选项包括:

  • 配置 Kestrel 服务器作为默认的 Web 服务器来负责处理 Web 的请求与响应
  • 使用当前目录作为应用程序的内容目录(ContentRoot),该目录决定了 ASP.NET Core 查找内容文件(如 MVC 视图等)的位置
  • 从以 ASPNETCORE_ 开头的环境变量(如 ASPNETCORE_ENVIRONMENT)中以及命令行参数中加载配置项
  • 从 appsettings.json、appsettings.{Environment}.json、用户机密(仅开发环境)、环境变量和命令行参数等位置加载应用设置
  • 配置日志功能,默认添加控制台输出和调式输出
  • 如果应用程序被托管在 IIS 中,启动 IIS 集成,它会配置应用程序的主机地址和端口,并允许捕获启动错误等

启动

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }
    }
  • ConfigureServices 用于向 ASP.NET Core 的依赖注入容器添加服务
  • Configure 用于添加中间件,配置请求管道

这两个方法都会在运行时被调用,且在应用程序的整个生命周期内,只执行一次。其中 ConfigureServices 方法是可选的,而 Configure 方法则是必须的。在程序启动时,它会执行 ConfigureServices 方法(如果有),将指定的服务放入应用程序的依赖注入容器中,然后再执行 Configure 方法,向请求管道中添加中间件。

中间件

所谓中间件,就是处理 HTTP 请求和响应的组件,它本质上是一段用来处理请求与响应的代码。多个中间件之间的链式关系使之形成了管道(Pipeline)或者请求管道。管道意味着请求将从一端进入,并按照一定的顺序由每一个中间件处理,最后由另一端出来。

添加中间件:

Run & Use

// Use 方法在处理完成后还会将请求传入下一个中间件,并由它继续处理
app.Use(async (context, next) =>
{
    Console.WriteLine("中间件 A:开始");
    await next();// 下一个中间件
    Console.WriteLine("中间件 A:结束");
});

app.Run(async (context) =>
{
    Console.WriteLine("中间件 B");
    await context.Response.WriteAsync("Hello, world");
});

启动程序,输出如下:

中间件 A:开始
中间件 B
中间件 A:结束

Map

app.Use(async (context, next) =>
{
    Console.WriteLine("中间件 A:开始");
    await next();// 下一个中间件
    Console.WriteLine("中间件 A:结束");
});

// 根据是否配置指定的请求路径来决定是否在一个新分支上继续执行后续的中间件
// 并且在新分支上执行完后,不再回到原来的管道上
app.Map(new PathString("/maptest"),
    a => a.Use(async (context, next) =>
{
    Console.WriteLine("中间件 B:开始");
    await next(); // 下一个中间件
    Console.WriteLine("中间件 B:结束");
}));

app.Run(async context =>
{
    Console.WriteLine("中间件 C");
    await context.Response.WriteAsync("Hello, world");
});

访问 https://localhost:5001/maptest

中间件 A:开始
中间件 B:开始
中间件 B:结束
中间件 A:结束

UseWhen

// Use 方法在处理完成后还会将请求传入下一个中间件,并由它继续处理
app.Use(async (context, next) =>
{
    Console.WriteLine("中间件 A:开始");
    await next();// 下一个中间件
    Console.WriteLine("中间件 A:结束");
});

// 所有添加的中间件都会执行
app.UseWhen(context => context.Request.Path.Value == "/maptest",
    a => a.Use(async (context, next) =>
{
    Console.WriteLine("中间件 B:开始");
    await next(); // 下一个中间件
    Console.WriteLine("中间件 B:结束");
}));

app.Run(async context =>
{
    Console.WriteLine("中间件 C");
    await context.Response.WriteAsync("Hello, world");
});

访问 https://localhost:5001/maptest

中间件 A:开始
中间件 B:开始
中间件 C
中间件 B:结束
中间件 A:结束

自定义中间件

public class HttpMethodCheckMiddleware
{
    // 在管道中的下一个中间件
    private readonly RequestDelegate _next;

    /// <summary>
    /// 构造函数中可以得到下一个中间件,并且还可以注入需要的服务,比如 IHostEnvironment
    /// </summary>
    /// <param name="requestDelegate"></param>
    /// <param name="environment"></param>
    public HttpMethodCheckMiddleware(RequestDelegate requestDelegate, IHostEnvironment environment)
    {
        this._next = requestDelegate;
    }

    /// <summary>
    /// 对 HTTP 请求方法进行判断,如果符合条件则继续执行下一个中间件
    /// 否则返回 400 Bad Request 错误,并在响应中添加自定义消息头用于说明错误原因
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public Task Invoke(HttpContext context)
    {
        var requestMethod = context.Request.Method.ToUpper();
        if (requestMethod == HttpMethods.Get || requestMethod == HttpMethods.Head)
        {
            return _next(context);
        }
        else
        {
            context.Response.StatusCode = 400;
            context.Response.Headers.Add("X-AllowHTTPWeb", new[] {"GET,HEAD"});
            context.Response.WriteAsync("只支持 GET、HEAD 方法");
            return Task.CompletedTask;
        }
    }
}

添加自定义中间件

app.UseMiddleware<HttpMethodCheckMiddleware>();

创建扩展方法

public static class CustomMiddlewareExtensions
{
    public static IApplicationBuilder UseHttpMethodCheckMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<HttpMethodCheckMiddleware>();
    }
}

调用扩展方法添加中间件

app.UseHttpMethodCheckMiddleware();

参考资料

《ASP.ENT Core 与 RESTful API 开发实战》

如果阅读文章后有所收获,希望您可以点一个推荐,感觉不尽!!!

ASP.NET Core 核心特性--宿主、启动、中间件

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。