.net core程序启动与中间件

1,015 阅读3分钟

宿主和启动

应用程序启动流程

注意点

  • 从appsettings.josn、appsettings.{Environment}.json、用户机密(仅开发环境)、环境变量和命令行参数等位置加载应用配置。
  • 从以ASPNETCORE_开头的环境变量(如ASPNETCORE_ENVIRONMENT)中以及命令行参数中加载配置项。
  • 三个环境名称
    • Development:开发。
    • Staging:预演。
    • Production:生产。

Startup类

向应用程序提供配置启动的类,其中有两个方法很重要,分别是ConfigureServices方法和Configure方法,

在程序启动时,会多次执行ConfigureServices方法,不断向容器中添加服务,而Configure方法只会只从一次,如果多次调用Configure方法只有最后一次有效。

ConfigureServices

向容器中添加服务。

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

Configure

向请求管道中添加中间件。

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

  //添加中间件
  app.Run(async (context) =>
          {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("-----------");
            stringBuilder.AppendLine($"Host: { context.Request.Host}");
            stringBuilder.AppendLine($"Method: { context.Request.Method}");
            stringBuilder.AppendLine($"Path: {context.Request.Path}");
            stringBuilder.AppendLine($"Protocol: {context.Request.Protocol}");
            foreach(var item in context.Request.Headers)
            {
              stringBuilder.AppendLine("------");
              stringBuilder.AppendLine($"{item.Key} : {item.Value}");
            }

            await context.Response.WriteAsync(stringBuilder.ToString());
          });

  app.UseHttpsRedirection();
  app.UseMvc();
}

中间件

ASP.NET Core中内置了多个中间件,它们主要包含MVC、认证、错误、静态文件、HTTPS重定向和跨域资源共享(Cross-Origin Resource Sharing,CORS)等,ASP.NET Core也允许向管道添加自定义中间件。

添加中间件

主要有两种方法,UseRun,两个方法接收的参数如下:

  • Run方法
public delegate Task RequestDelegate(HttpContext context);
  • Use方法参数类型
Func<HttpContext, Func<Task>, Task>

例子: 使用run方法添加中间件

在startup类的Configure方法中田间如下代码,注意添加中间件的顺序,中间件的配置即请求管道的执行顺序

app.Run(async (context) =>
{
  StringBuilder stringBuilder = new StringBuilder();
  stringBuilder.AppendLine("-----------");
  stringBuilder.AppendLine($"Host: { context.Request.Host}");
  stringBuilder.AppendLine($"Method: { context.Request.Method}");
  stringBuilder.AppendLine($"Path: {context.Request.Path}");
  stringBuilder.AppendLine($"Protocol: {context.Request.Protocol}");
  foreach(var item in context.Request.Headers)
  {
    stringBuilder.AppendLine("------");
    stringBuilder.AppendLine($"{item.Key} : {item.Value}");
  }

  await context.Response.WriteAsync(stringBuilder.ToString());
});

Postman输出的结果为:

其他使用方法

Map方法

Map会根据是否匹配指定的请求路径来决定是否在一个新的分支上继续执行后续的中间件,并且在新分支上执行完后,不再回到原来的管道上。

MapWhen方法

MapWhen则可以满足更复杂的条件,它接收Func<HttpContext, bool>类型的参数,并以该参数作为判断条件,因此,它会对HttpContext对象进行更细致的判断(如是否包含指定的请求消息头等),然后决定是否进入新的分支继续执行指定的中间件。

UseWhen方法

UseWhen与MapWhen尽管接受的参数完全一致,但它不像Map和MapWhen一样,由它创建的分支在执行完后会继续回到原来的管道上。

自定义中间件

构造函数

构造函数中应包含一个RequestDelegate类型的参数,该参数表述在管道中要调用的下一个中间件。

private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate requestDelegate,IHostingEnvironment environment)
{
  this._next = requestDelegate;
}

Invoke方法

Invoke方法要求返回值为Task类型。并且因该包含一个HttpContext类型的参数用于处理请求。

在Invoke方法中,对HTTP请求方法进行判断,如果符合条件,则继续执行下一个中间件;否则返回400 Bad Request错误,并在响应中添加了自定义消息头,用于说明错误原因。

public Task Invoke(HttpContext context)
{
  var requestMethod = context.Request.Method.ToUpper();
  if(requestMethod == HttpMethod.Get || 
    request.Method == HttpMethod.Head)
  {
  	return _next(context);
  }else
  {
  	context.Response.StatusCode = 400;
    context.Response.Headers.Add("X-AllowHTTPVerb",new[] {"GET","HEAD"});
    context.Response.WriteAsync("至支持Get、Head方法");
    return Task.CompletedTask;
  }
}

添加自定义中间件

  • 使用泛型

在Startup类的Configure方法中添加中间件:

app.UseMiddleware<MyMiddleWare>();
  • 使用扩展方法

    • 定义扩展方法
    public static class MyMiddleWareExtensions
    {
    	public static IApplicationBuilder UseMyMiddleWare(this IApplicationBuilder builder)
        {
      		return builder.UseMiddleware<MyMiddleWare>();
    	}
    }
    
    • 在配置中添加中间件
    app.UseMyMiddleWare();
    

完整示例代码

中间件类MyMiddleWare:

public class MyMiddleWare
{
  //构造器,注意调用下一个中间件
  private readonly RequestDelegate _next;
  public MyMiddleWare(RequestDelegate requestDelegate,IHostingEnvironment environment)
  {
    this._next = requestDelegate;
  }
  //Invoke方法,注意参数和返回值类型
  public Task Invoke(HttpContext context)
  {
    var requestMethod = context.Request.Method.ToUpper();
    if(requestMethod == HttpMethod.Get || 
      request.Method == HttpMethod.Head)
    {
      return _next(context);
    }else
    {
      context.Response.StatusCode = 400;
      context.Response.Headers.Add("X-AllowHTTPVerb",new[] {"GET","HEAD"});
      context.Response.WriteAsync("至支持Get、Head方法");
      return Task.CompletedTask;
    }
  }
}

扩展方法MyMiddleWareExtensions类:

public static class MyMiddleWareExtensions
{
	public static IApplicationBuilder UseMyMiddleWare(this IApplicationBuilder builder)
    {
  		return builder.UseMiddleware<MyMiddleWare>();
	}
}

启动配置类Startup:

app.UseMyMiddleWare();