将 HTTP 处理程序和模块迁移到 ASP.NET Core中间件

这篇文章演示如何迁移现有的 ASP.NET HTTP 模块和处理程序 system.webserver到 ASP.NET Core 中间件

模块和处理程序被

在继续之前到 ASP.NET Core 中间件,让我们首先会扼要重述 HTTP 模块和处理程序的工作原理:

模块处理程序

处理程序是:

  • 实现IHttpHandler的类

  • 用于处理具有给定文件名或扩展名的请求,如 . report

  • 在 web.config配置

模块为:

  • 实现IHttpModule的类

  • 为每个请求调用

  • 能够短路(停止进一步处理请求)

  • 可以添加到 HTTP 响应,或创建自己的响应

  • 在 web.config配置

模块处理传入请求的顺序由确定:

  1. 应用程序生命周期,是 ASP.NET 激发的一系列事件:BeginRequestAuthenticateRequest等。每个模块都可以为一个或多个事件创建处理程序。

  2. 对于同一事件,为在 web.config 中配置它们的顺序。

除了模块外,还可以将生命周期事件的处理程序添加到Global.asax.cs文件。 这些处理程序在配置的模块中的处理程序之后运行。

从处理程序和模块到中间件

中间件比 HTTP 模块和处理程序更简单:

  • "模块"、"处理程序"、" Global.asax.cs"、 "WEB.CONFIG" (IIS配置除外)和 "应用程序生命周期" 消失

  • 中间件已使用模块和处理程序的角色

  • 中间件使用代码而不是在 web.config 中进行配置

  • 通过管道分支,你可以将请求发送到特定的中间件,不仅可以基于 URL,还可以发送到请求标头、查询字符串等。

中间件非常类似于模块:

中间件和模块按不同的顺序进行处理:

中间件

请注意,在上图中,身份验证中间件与请求短路。

将模块代码迁移到中间件

现有 HTTP 模块如下所示:

// ASP.NET 4 module

using System;
using System.Web;

namespace MyApp.Modules
{
    public class MyModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication application)
        {
            application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
            application.EndRequest += (new EventHandler(this.Application_EndRequest));
        }

        private void Application_BeginRequest(Object source, EventArgs e)
        {
            HttpContext context = ((HttpApplication)source).Context;

            // Do something with context near the beginning of request processing.
        }

        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpContext context = ((HttpApplication)source).Context;

            // Do something with context near the end of request processing.
        }
    }
}

中所示中间件页上,ASP.NET Core 中间件是公开的类Invoke方法拍摄HttpContext并返回Task 新的中间件将如下所示:

// ASP.NET Core middleware

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MyApp.Middleware
{
    public class MyMiddleware
    {
        private readonly RequestDelegate _next;

        public MyMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // Do something with context near the beginning of request processing.

            await _next.Invoke(context);

            // Clean up.
        }
    }

    public static class MyMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddleware>();
        }
    }
}

前面的中间件模板取自编写中间件的部分。

MyMiddlewareExtensions helper 类使你可以更轻松地在Startup类中配置中间件。 UseMyMiddleware方法将中间件类添加到请求管道。 中间件的构造函数中插入了中间件所需的服务。

如果用户未获得授权,则模块可能会终止请求:

// ASP.NET 4 module that may terminate the request

private void Application_BeginRequest(Object source, EventArgs e)
{
    HttpContext context = ((HttpApplication)source).Context;

    // Do something with context near the beginning of request processing.

    if (TerminateRequest())
    {
        context.Response.End();
        return;
    }
}

中间件通过不调用Invoke管道中的下一个中间件来处理这种情况。 请记住,这并不完全终止请求,因为当响应通过管道返回以前的中间件时,仍然会调用以前的。

// ASP.NET Core middleware that may terminate the request

public async Task Invoke(HttpContext context)
{
    // Do something with context near the beginning of request processing.

    if (!TerminateRequest())
        await _next.Invoke(context);

    // Clean up.
}

当你迁移到新中间件模块的功能时,你可能会发现你的代码不会编译,因为HttpContext类中 ASP.NET Core 已显著更改。 更高版本上,你将了解如何将迁移到新的 ASP.NET Core HttpContext。

将模块插入迁移到请求管道中

HTTP 模块通常使用 web.config添加到请求管道:

<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
  <system.webServer>
    <modules>
      <add name="MyModule" type="MyApp.Modules.MyModule"/>
    </modules>
  </system.webServer>
</configuration>

通过在Startup类中将新的中间件添加到请求管道来转换此项:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseMyMiddleware();

    app.UseMyMiddlewareWithParams();

    var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
    var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
    app.UseMyMiddlewareWithParams(myMiddlewareOptions);
    app.UseMyMiddlewareWithParams(myMiddlewareOptions2);

    app.UseMyTerminatingMiddleware();

    // Create branch to the MyHandlerMiddleware. 
    // All requests ending in .report will follow this branch.
    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".report"),
        appBranch => {
            // ... optionally add more middleware to this branch
            appBranch.UseMyHandler();
        });

    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".context"),
        appBranch => {
            appBranch.UseHttpContextDemoMiddleware();
        });

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

插入新中间件的管道中的确切位置取决于它在web.config 中的模块列表中处理为模块BeginRequestEndRequest、等)的事件及其顺序。

如前面所述,没有任何应用程序生命周期中 ASP.NET Core,中间件处理响应的顺序不同于使用模块的顺序。 这可能会使你的排序决策更具挑战性。

如果排序会成为一个问题,则可以将模块拆分为多个中间件组件,这些组件可以独立排序。

将处理程序代码迁移到中间件

HTTP 处理程序如下所示:

// ASP.NET 4 handler

using System.Web;

namespace MyApp.HttpHandlers
{
    public class MyHandler : IHttpHandler
    {
        public bool IsReusable { get { return true; } }

        public void ProcessRequest(HttpContext context)
        {
            string response = GenerateResponse(context);

            context.Response.ContentType = GetContentType();
            context.Response.Output.Write(response);
        }

        // ...

        private string GenerateResponse(HttpContext context)
        {
            string title = context.Request.QueryString["title"];
            return string.Format("Title of the report: {0}", title);
        }

        private string GetContentType()
        {
            return "text/plain";
        }
    }
}

在 ASP.NET Core 项目中,你将翻译以下到中间件类似于以下内容:

// ASP.NET Core middleware migrated from a handler

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MyApp.Middleware
{
    public class MyHandlerMiddleware
    {

        // Must have constructor with this signature, otherwise exception at run time
        public MyHandlerMiddleware(RequestDelegate next)
        {
            // This is an HTTP Handler, so no need to store next
        }

        public async Task Invoke(HttpContext context)
        {
            string response = GenerateResponse(context);

            context.Response.ContentType = GetContentType();
            await context.Response.WriteAsync(response);
        }

        // ...

        private string GenerateResponse(HttpContext context)
        {
            string title = context.Request.Query["title"];
            return string.Format("Title of the report: {0}", title);
        }

        private string GetContentType()
        {
            return "text/plain";
        }
    }

    public static class MyHandlerExtensions
    {
        public static IApplicationBuilder UseMyHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyHandlerMiddleware>();
        }
    }
}

此中间件与与模块对应的中间件非常类似。 唯一的区别在于,这里不会调用_next.Invoke(context) 这样做很有意义,因为处理程序位于请求管道的末尾,因此没有要调用的下一个中间件。

将处理程序插入迁移到请求管道中

配置 HTTP 处理程序是在 web.config 中完成的, 如下所示:

<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
  <system.webServer>
    <handlers>
      <add name="MyHandler" verb="*" path="*.report" type="MyApp.HttpHandlers.MyHandler" resourceType="Unspecified" preCondition="integratedMode"/>
    </handlers>
  </system.webServer>
</configuration>

可以通过将新的处理程序中间件添加到Startup类中的请求管道来转换此转换,类似于从模块转换的中间件。 此方法的问题是,它会将所有请求发送到新的处理程序中间件。 但是,只需要具有给定扩展的请求来访问中间件。 这将为你提供与 HTTP 处理程序相同的功能。

一种解决方法是使用MapWhen扩展方法分支具有给定扩展的请求的管道。 可在添加其他中间件Configure的相同方法中执行此操作:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseMyMiddleware();

    app.UseMyMiddlewareWithParams();

    var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
    var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
    app.UseMyMiddlewareWithParams(myMiddlewareOptions);
    app.UseMyMiddlewareWithParams(myMiddlewareOptions2);

    app.UseMyTerminatingMiddleware();

    // Create branch to the MyHandlerMiddleware. 
    // All requests ending in .report will follow this branch.
    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".report"),
        appBranch => {
            // ... optionally add more middleware to this branch
            appBranch.UseMyHandler();
        });

    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".context"),
        appBranch => {
            appBranch.UseHttpContextDemoMiddleware();
        });

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

MapWhen采用以下参数:

  1. 一个采用的HttpContext lambda,如果请求true应向下分支,则返回。 这意味着,不仅可以根据请求的扩展来分支请求,还可以处理请求标头、查询字符串参数等。

  2. 一个采用IApplicationBuilder并添加分支的所有中间件的 lambda。 这意味着,可以将其他中间件添加到处理程序中间件前面的分支。

将在所有请求上调用分支之前添加到管道的中间件;该分支不会对它们产生任何影响。

使用 options 模式加载中间件选项

某些模块和处理程序具有存储在 web.config 中的配置选项。但是,在 ASP.NET Core 中新的配置模型使用代替了Web.config

配置系统提供以下选项来解决此类情况:

  1. 创建用于保存中间件选项的类,例如:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. 存储选项值

    配置系统允许您将选项值存储在任何所需的位置。 但是,大多数站点都使用appsettings,因此我们将采取这种方法:

    {
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    

    MyMiddlewareOptionsSection是部分名称。 它不必与 options 类的名称相同。

  3. 将选项值与 options 类相关联

    选项模式使用 ASP.NET Core 依赖关系注入框架将选项类型相关联 (如MyMiddlewareOptions) 与MyMiddlewareOptions具有实际选项对象。

    更新你Startup的类:

    1. 如果使用的是appsettings,请将其添加到Startup构造函数中的配置生成器:

      public Startup(IHostingEnvironment env)
      {
          var builder = new ConfigurationBuilder()
              .SetBasePath(env.ContentRootPath)
              .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
              .AddEnvironmentVariables();
          Configuration = builder.Build();
      }
      
    2. 配置 options 服务:

      public void ConfigureServices(IServiceCollection services)
      {
          // Setup options service
          services.AddOptions();
      
          // Load options from section "MyMiddlewareOptionsSection"
          services.Configure<MyMiddlewareOptions>(
              Configuration.GetSection("MyMiddlewareOptionsSection"));
      
          // Add framework services.
          services.AddMvc();
      }
      
    3. 将选项与 options 类相关联:

      public void ConfigureServices(IServiceCollection services)
      {
          // Setup options service
          services.AddOptions();
      
          // Load options from section "MyMiddlewareOptionsSection"
          services.Configure<MyMiddlewareOptions>(
              Configuration.GetSection("MyMiddlewareOptionsSection"));
      
          // Add framework services.
          services.AddMvc();
      }
      
  4. 将选项注入中间件构造函数。 这类似于将选项注入控制器。

    public class MyMiddlewareWithParams
    {
        private readonly RequestDelegate _next;
        private readonly MyMiddlewareOptions _myMiddlewareOptions;
    
        public MyMiddlewareWithParams(RequestDelegate next,
            IOptions<MyMiddlewareOptions> optionsAccessor)
        {
            _next = next;
            _myMiddlewareOptions = optionsAccessor.Value;
        }
    
        public async Task Invoke(HttpContext context)
        {
            // Do something with context near the beginning of request processing
            // using configuration in _myMiddlewareOptions
    
            await _next.Invoke(context);
    
            // Do something with context near the end of request processing
            // using configuration in _myMiddlewareOptions
        }
    }
    

    中间件添加到中的 UseMiddleware 扩展方法IApplicationBuilder会处理依赖关系注入。

    这并不限于IOptions对象。 中间件所需的任何其他对象都可以通过这种方式注入。

通过直接注入加载中间件选项

Options 模式的优点在于,它在选项值与其使用者之间产生松散耦合。 将选项类与实际选项值相关联后,任何其他类都可以通过依赖关系注入框架访问这些选项。 无需围绕选项值进行传递。

如果要使用不同的选项两次使用同一中间件,则会出现这种情况。 例如,在不同的分支中使用的授权中间件允许不同角色。 不能将两个不同的选项对象与一个 options 类相关联。

解决方法是在Startup类中获取选项对象,并将其直接传递给中间件的每个实例。

  1. 将第二个键添加到appsettings

    若要将第二组选项添加到appsettings文件,请使用新密钥来唯一标识它:

    {
      "MyMiddlewareOptionsSection2": {
        "Param1": "Param1Value2",
        "Param2": "Param2Value2"
      },
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    
  2. 检索选项值并将其传递给中间件。 Use...扩展方法(将中间件添加到管道)是要传入选项值的逻辑位置:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
    
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
    
        app.UseMyMiddleware();
    
        app.UseMyMiddlewareWithParams();
    
        var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
        var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
        app.UseMyMiddlewareWithParams(myMiddlewareOptions);
        app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
    
        app.UseMyTerminatingMiddleware();
    
        // Create branch to the MyHandlerMiddleware. 
        // All requests ending in .report will follow this branch.
        app.MapWhen(
            context => context.Request.Path.ToString().EndsWith(".report"),
            appBranch => {
                // ... optionally add more middleware to this branch
                appBranch.UseMyHandler();
            });
    
        app.MapWhen(
            context => context.Request.Path.ToString().EndsWith(".context"),
            appBranch => {
                appBranch.UseHttpContextDemoMiddleware();
            });
    
        app.UseStaticFiles();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    
  3. 启用中间件以采用 options 参数。 提供Use...扩展方法(采用 options 参数并将其传递给UseMiddleware)的重载。 UseMiddleware用参数调用时,它会在实例化中间件对象时将参数传递给中间件构造函数。

    public static class MyMiddlewareWithParamsExtensions
    {
        public static IApplicationBuilder UseMyMiddlewareWithParams(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddlewareWithParams>();
        }
    
        public static IApplicationBuilder UseMyMiddlewareWithParams(
            this IApplicationBuilder builder, MyMiddlewareOptions myMiddlewareOptions)
        {
            return builder.UseMiddleware<MyMiddlewareWithParams>(
                new OptionsWrapper<MyMiddlewareOptions>(myMiddlewareOptions));
        }
    }
    

    请注意这如何包装OptionsWrapper对象中的选项对象。 这实现IOptions了中间件构造函数的预期。

迁移到新的 HttpContext

你以前看到,中间Invoke件中的方法采用类型HttpContext为的参数:

public async Task Invoke(HttpContext context)

HttpContext 已显著更改 ASP.NET Core 中。 本部分演示如何将 system.servicemodel 最常用的属性转换为新 Microsoft.AspNetCore.Http.HttpContext的。

HttpContext

HttpContext会转换为:

IDictionary<object, object> items = httpContext.Items;

唯一的请求 ID (不含 System.web)

提供每个请求的唯一 id。 在日志中包含非常有用。

string requestId = httpContext.TraceIdentifier;

HttpContext.Request

HttpMethod转换为:

string httpMethod = httpContext.Request.Method;

HttpContext转换为:

IQueryCollection queryParameters = httpContext.Request.Query;

// If no query parameter "key" used, values will have 0 items
// If single value used for a key (...?key=v1), values will have 1 item ("v1")
// If key has multiple values (...?key=v1&key=v2), values will have 2 items ("v1" and "v2")
IList<string> values = queryParameters["key"];

// If no query parameter "key" used, value will be ""
// If single value used for a key (...?key=v1), value will be "v1"
// If key has multiple values (...?key=v1&key=v2), value will be "v1,v2"
string value = queryParameters["key"].ToString();

Httpcontext.current转换为( RawUrl ):

// using Microsoft.AspNetCore.Http.Extensions;
var url = httpContext.Request.GetDisplayUrl();

IsSecureConnection转换为:

var isSecureConnection = httpContext.Request.IsHttps;

UserHostAddress转换为:

var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();

Httpcontext.current转换为:

IRequestCookieCollection cookies = httpContext.Request.Cookies;
string unknownCookieValue = cookies["unknownCookie"]; // will be null (no exception)
string knownCookieValue = cookies["cookie1name"];     // will be actual value

RequestContext RouteData转换为:

var routeValue = httpContext.GetRouteValue("key");

Httpcontext.current转换为:

// using Microsoft.AspNetCore.Http.Headers;
// using Microsoft.Net.Http.Headers;

IHeaderDictionary headersDictionary = httpContext.Request.Headers;

// GetTypedHeaders extension method provides strongly typed access to many headers
var requestHeaders = httpContext.Request.GetTypedHeaders();
CacheControlHeaderValue cacheControlHeaderValue = requestHeaders.CacheControl;

// For unknown header, unknownheaderValues has zero items and unknownheaderValue is ""
IList<string> unknownheaderValues = headersDictionary["unknownheader"];
string unknownheaderValue = headersDictionary["unknownheader"].ToString();

// For known header, knownheaderValues has 1 item and knownheaderValue is the value
IList<string> knownheaderValues = headersDictionary[HeaderNames.AcceptLanguage];
string knownheaderValue = headersDictionary[HeaderNames.AcceptLanguage].ToString();

UserAgent转换为:

string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();

UrlReferrer转换为:

string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();

HttpContext转换为:

// using Microsoft.Net.Http.Headers;

MediaTypeHeaderValue mediaHeaderValue = requestHeaders.ContentType;
string contentType = mediaHeaderValue?.MediaType.ToString();   // ex. application/x-www-form-urlencoded
string contentMainType = mediaHeaderValue?.Type.ToString();    // ex. application
string contentSubType = mediaHeaderValue?.SubType.ToString();  // ex. x-www-form-urlencoded

System.Text.Encoding requestEncoding = mediaHeaderValue?.Encoding;

Httpcontext.current转换为:

if (httpContext.Request.HasFormContentType)
{
    IFormCollection form;

    form = httpContext.Request.Form; // sync
    // Or
    form = await httpContext.Request.ReadFormAsync(); // async

    string firstName = form["firstname"];
    string lastName = form["lastname"];
}

警告

仅当 content 子类型为x-www-url 编码窗体数据时才读取窗体值。

InputStream转换为:

string inputBody;
using (var reader = new System.IO.StreamReader(
    httpContext.Request.Body, System.Text.Encoding.UTF8))
{
    inputBody = reader.ReadToEnd();
}

警告

仅在管道末尾的处理程序类型中间件中使用此代码。

对于每个请求,可以读取上面所示的原始主体。 第一次读取后尝试读取正文的中间件将读取空正文。

这并不适用于读取如上所示的窗体,因为这是从缓冲区中完成的。

HttpContext.Response

Httpcontext.current转换为( StatusDescription ):

// using Microsoft.AspNetCore.Http;
httpContext.Response.StatusCode = StatusCodes.Status200OK;

ContentEncodinghttpcontext转换为以下内容:

// using Microsoft.Net.Http.Headers;
var mediaType = new MediaTypeHeaderValue("application/json");
mediaType.Encoding = System.Text.Encoding.UTF8;
httpContext.Response.ContentType = mediaType.ToString();

Httpcontext.current还会转换为:

httpContext.Response.ContentType = "text/html";

Httpcontext.current转换为:

string responseContent = GetResponseContent();
await httpContext.Response.WriteAsync(responseContent);

HttpContext.Response.TransmitFile

此处讨论了如何提供文件。

HttpContext.Response.Headers

发送响应标头比较复杂,因为如果在将任何内容写入响应正文后设置这些标头,则不会发送这些标头。

解决方法是设置一个回调方法,该方法将在开始写入响应之前被调用。 最好在中间件中的Invoke方法的开头完成此操作。 这是设置响应标头的此回调方法。

下面的代码设置一个名SetHeaders为的回调方法:

public async Task Invoke(HttpContext httpContext)
{
    // ...
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);

SetHeaders回调方法将如下所示:

// using Microsoft.AspNet.Http.Headers;
// using Microsoft.Net.Http.Headers;

private Task SetHeaders(object context)
{
    var httpContext = (HttpContext)context;

    // Set header with single value
    httpContext.Response.Headers["ResponseHeaderName"] = "headerValue";

    // Set header with multiple values
    string[] responseHeaderValues = new string[] { "headerValue1", "headerValue1" };
    httpContext.Response.Headers["ResponseHeaderName"] = responseHeaderValues;

    // Translating ASP.NET 4's HttpContext.Response.RedirectLocation  
    httpContext.Response.Headers[HeaderNames.Location] = "http://www.example.com";
    // Or
    httpContext.Response.Redirect("http://www.example.com");

    // GetTypedHeaders extension method provides strongly typed access to many headers
    var responseHeaders = httpContext.Response.GetTypedHeaders();

    // Translating ASP.NET 4's HttpContext.Response.CacheControl 
    responseHeaders.CacheControl = new CacheControlHeaderValue
    {
        MaxAge = new System.TimeSpan(365, 0, 0, 0)
        // Many more properties available 
    };

    // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
    return Task.FromResult(0);
}

HttpContext.Response.Cookies

Cookie 在设置 cookie响应标头中传递到浏览器。 因此,发送 cookie 需要与用于发送响应标头的回调相同:

public async Task Invoke(HttpContext httpContext)
{
    // ...
    httpContext.Response.OnStarting(SetCookies, state: httpContext);
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);

SetCookies回调方法将如下所示:

private Task SetCookies(object context)
{
    var httpContext = (HttpContext)context;

    IResponseCookies responseCookies = httpContext.Response.Cookies;

    responseCookies.Append("cookie1name", "cookie1value");
    responseCookies.Append("cookie2name", "cookie2value",
        new CookieOptions { Expires = System.DateTime.Now.AddDays(5), HttpOnly = true });

    // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
    return Task.FromResult(0); 
}

其他资源

上一篇:ASP.NET 成员资格身份验证从迁移到 ASP.NET Core 2.0 标识

下一篇:没有了

关注微信小程序
程序员编程王-随时随地学编程

扫描二维码
程序员编程王

扫一扫关注最新编程教程