将 HTTP 处理程序和模块迁移到 ASP.NET Core中间件Migrate HTTP handlers and modules to ASP.NET Core middleware

本文内容

本文介绍如何将现有的 ASP.NET HTTP 模块和处理程序从 system.webserver迁移到 ASP.NET Core中间件

模块和处理程序被Modules and handlers revisited

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

模块处理程序

处理程序是:

  • 实现IHttpHandler的类

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

  • 在 web.config配置

模块为:

  • 实现IHttpModule的类

  • 为每个请求调用

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

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

  • 在 web.config配置

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

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

从处理程序和模块到中间件From handlers and modules to middleware

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

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

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

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

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

中间件非常类似于模块:

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

中间件

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

将模块代码迁移到中间件Migrating module code to middleware

现有 HTTP 模块如下所示:

  1. // ASP.NET 4 module
  2. using System;
  3. using System.Web;
  4. namespace MyApp.Modules
  5. {
  6. public class MyModule : IHttpModule
  7. {
  8. public void Dispose()
  9. {
  10. }
  11. public void Init(HttpApplication application)
  12. {
  13. application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
  14. application.EndRequest += (new EventHandler(this.Application_EndRequest));
  15. }
  16. private void Application_BeginRequest(Object source, EventArgs e)
  17. {
  18. HttpContext context = ((HttpApplication)source).Context;
  19. // Do something with context near the beginning of request processing.
  20. }
  21. private void Application_EndRequest(Object source, EventArgs e)
  22. {
  23. HttpContext context = ((HttpApplication)source).Context;
  24. // Do something with context near the end of request processing.
  25. }
  26. }
  27. }

中间件页中所示,ASP.NET Core 中间件是一个类,该类公开采用 HttpContext 并返回 TaskInvoke 方法。新的中间件将如下所示:

  1. // ASP.NET Core middleware
  2. using Microsoft.AspNetCore.Builder;
  3. using Microsoft.AspNetCore.Http;
  4. using System.Threading.Tasks;
  5. namespace MyApp.Middleware
  6. {
  7. public class MyMiddleware
  8. {
  9. private readonly RequestDelegate _next;
  10. public MyMiddleware(RequestDelegate next)
  11. {
  12. _next = next;
  13. }
  14. public async Task Invoke(HttpContext context)
  15. {
  16. // Do something with context near the beginning of request processing.
  17. await _next.Invoke(context);
  18. // Clean up.
  19. }
  20. }
  21. public static class MyMiddlewareExtensions
  22. {
  23. public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
  24. {
  25. return builder.UseMiddleware<MyMiddleware>();
  26. }
  27. }
  28. }

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

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

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

  1. // ASP.NET 4 module that may terminate the request
  2. private void Application_BeginRequest(Object source, EventArgs e)
  3. {
  4. HttpContext context = ((HttpApplication)source).Context;
  5. // Do something with context near the beginning of request processing.
  6. if (TerminateRequest())
  7. {
  8. context.Response.End();
  9. return;
  10. }
  11. }

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

  1. // ASP.NET Core middleware that may terminate the request
  2. public async Task Invoke(HttpContext context)
  3. {
  4. // Do something with context near the beginning of request processing.
  5. if (!TerminateRequest())
  6. await _next.Invoke(context);
  7. // Clean up.
  8. }

当您将模块的功能迁移到新的中间件时,您可能会发现您的代码不会进行编译,因为 HttpContext 类在 ASP.NET Core 中发生了重大更改。稍后,你将了解如何迁移到新的 ASP.NET Core HttpContext。

将模块插入迁移到请求管道中Migrating module insertion into the request pipeline

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

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

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

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3. loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  4. loggerFactory.AddDebug();
  5. if (env.IsDevelopment())
  6. {
  7. app.UseDeveloperExceptionPage();
  8. app.UseBrowserLink();
  9. }
  10. else
  11. {
  12. app.UseExceptionHandler("/Home/Error");
  13. }
  14. app.UseMyMiddleware();
  15. app.UseMyMiddlewareWithParams();
  16. var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
  17. var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
  18. app.UseMyMiddlewareWithParams(myMiddlewareOptions);
  19. app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
  20. app.UseMyTerminatingMiddleware();
  21. // Create branch to the MyHandlerMiddleware.
  22. // All requests ending in .report will follow this branch.
  23. app.MapWhen(
  24. context => context.Request.Path.ToString().EndsWith(".report"),
  25. appBranch => {
  26. // ... optionally add more middleware to this branch
  27. appBranch.UseMyHandler();
  28. });
  29. app.MapWhen(
  30. context => context.Request.Path.ToString().EndsWith(".context"),
  31. appBranch => {
  32. appBranch.UseHttpContextDemoMiddleware();
  33. });
  34. app.UseStaticFiles();
  35. app.UseMvc(routes =>
  36. {
  37. routes.MapRoute(
  38. name: "default",
  39. template: "{controller=Home}/{action=Index}/{id?}");
  40. });
  41. }

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

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

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

将处理程序代码迁移到中间件Migrating handler code to middleware

HTTP 处理程序如下所示:

  1. // ASP.NET 4 handler
  2. using System.Web;
  3. namespace MyApp.HttpHandlers
  4. {
  5. public class MyHandler : IHttpHandler
  6. {
  7. public bool IsReusable { get { return true; } }
  8. public void ProcessRequest(HttpContext context)
  9. {
  10. string response = GenerateResponse(context);
  11. context.Response.ContentType = GetContentType();
  12. context.Response.Output.Write(response);
  13. }
  14. // ...
  15. private string GenerateResponse(HttpContext context)
  16. {
  17. string title = context.Request.QueryString["title"];
  18. return string.Format("Title of the report: {0}", title);
  19. }
  20. private string GetContentType()
  21. {
  22. return "text/plain";
  23. }
  24. }
  25. }

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

  1. // ASP.NET Core middleware migrated from a handler
  2. using Microsoft.AspNetCore.Builder;
  3. using Microsoft.AspNetCore.Http;
  4. using System.Threading.Tasks;
  5. namespace MyApp.Middleware
  6. {
  7. public class MyHandlerMiddleware
  8. {
  9. // Must have constructor with this signature, otherwise exception at run time
  10. public MyHandlerMiddleware(RequestDelegate next)
  11. {
  12. // This is an HTTP Handler, so no need to store next
  13. }
  14. public async Task Invoke(HttpContext context)
  15. {
  16. string response = GenerateResponse(context);
  17. context.Response.ContentType = GetContentType();
  18. await context.Response.WriteAsync(response);
  19. }
  20. // ...
  21. private string GenerateResponse(HttpContext context)
  22. {
  23. string title = context.Request.Query["title"];
  24. return string.Format("Title of the report: {0}", title);
  25. }
  26. private string GetContentType()
  27. {
  28. return "text/plain";
  29. }
  30. }
  31. public static class MyHandlerExtensions
  32. {
  33. public static IApplicationBuilder UseMyHandler(this IApplicationBuilder builder)
  34. {
  35. return builder.UseMiddleware<MyHandlerMiddleware>();
  36. }
  37. }
  38. }

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

将处理程序插入迁移到请求管道中Migrating handler insertion into the request pipeline

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

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

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

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

  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3. loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  4. loggerFactory.AddDebug();
  5. if (env.IsDevelopment())
  6. {
  7. app.UseDeveloperExceptionPage();
  8. app.UseBrowserLink();
  9. }
  10. else
  11. {
  12. app.UseExceptionHandler("/Home/Error");
  13. }
  14. app.UseMyMiddleware();
  15. app.UseMyMiddlewareWithParams();
  16. var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
  17. var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
  18. app.UseMyMiddlewareWithParams(myMiddlewareOptions);
  19. app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
  20. app.UseMyTerminatingMiddleware();
  21. // Create branch to the MyHandlerMiddleware.
  22. // All requests ending in .report will follow this branch.
  23. app.MapWhen(
  24. context => context.Request.Path.ToString().EndsWith(".report"),
  25. appBranch => {
  26. // ... optionally add more middleware to this branch
  27. appBranch.UseMyHandler();
  28. });
  29. app.MapWhen(
  30. context => context.Request.Path.ToString().EndsWith(".context"),
  31. appBranch => {
  32. appBranch.UseHttpContextDemoMiddleware();
  33. });
  34. app.UseStaticFiles();
  35. app.UseMvc(routes =>
  36. {
  37. routes.MapRoute(
  38. name: "default",
  39. template: "{controller=Home}/{action=Index}/{id?}");
  40. });
  41. }

MapWhen 采用以下参数:

  • 一个 lambda,它采用 HttpContext 并在请求应向下移动时返回 true。这意味着,不仅可以根据请求的扩展来分支请求,还可以处理请求标头、查询字符串参数等。

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

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

使用 options 模式加载中间件选项Loading middleware options using the options pattern

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

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

  • 创建用于保存中间件选项的类,例如:
  1. public class MyMiddlewareOptions
  2. {
  3. public string Param1 { get; set; }
  4. public string Param2 { get; set; }
  5. }
  • 存储选项值

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

  1. {
  2. "MyMiddlewareOptionsSection": {
  3. "Param1": "Param1Value",
  4. "Param2": "Param2Value"
  5. }
  6. }

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

  • 将选项值与 options 类相关联

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

更新 Startup 类:

  • 如果使用的是appsettings,请将其添加到 Startup 构造函数中的配置生成器:
  1. public Startup(IHostingEnvironment env)
  2. {
  3. var builder = new ConfigurationBuilder()
  4. .SetBasePath(env.ContentRootPath)
  5. .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
  6. .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
  7. .AddEnvironmentVariables();
  8. Configuration = builder.Build();
  9. }
  • 配置 options 服务:
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. // Setup options service
  4. services.AddOptions();
  5. // Load options from section "MyMiddlewareOptionsSection"
  6. services.Configure<MyMiddlewareOptions>(
  7. Configuration.GetSection("MyMiddlewareOptionsSection"));
  8. // Add framework services.
  9. services.AddMvc();
  10. }
  • 将选项与 options 类相关联:
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. // Setup options service
  4. services.AddOptions();
  5. // Load options from section "MyMiddlewareOptionsSection"
  6. services.Configure<MyMiddlewareOptions>(
  7. Configuration.GetSection("MyMiddlewareOptionsSection"));
  8. // Add framework services.
  9. services.AddMvc();
  10. }
  • 将选项注入中间件构造函数。这类似于将选项注入控制器。
  1. public class MyMiddlewareWithParams
  2. {
  3. private readonly RequestDelegate _next;
  4. private readonly MyMiddlewareOptions _myMiddlewareOptions;
  5. public MyMiddlewareWithParams(RequestDelegate next,
  6. IOptions<MyMiddlewareOptions> optionsAccessor)
  7. {
  8. _next = next;
  9. _myMiddlewareOptions = optionsAccessor.Value;
  10. }
  11. public async Task Invoke(HttpContext context)
  12. {
  13. // Do something with context near the beginning of request processing
  14. // using configuration in _myMiddlewareOptions
  15. await _next.Invoke(context);
  16. // Do something with context near the end of request processing
  17. // using configuration in _myMiddlewareOptions
  18. }
  19. }

UseMiddleware扩展方法,用于将中间件添加到 IApplicationBuilder 处理依赖关系注入。

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

通过直接注入加载中间件选项Loading middleware options through direct injection

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

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

解决方法是在 Startup 类中获取 options 对象以及实际选项值,并将这些选项直接传递给中间件的每个实例。

  • 将第二个键添加到appsettings

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

  1. {
  2. "MyMiddlewareOptionsSection2": {
  3. "Param1": "Param1Value2",
  4. "Param2": "Param2Value2"
  5. },
  6. "MyMiddlewareOptionsSection": {
  7. "Param1": "Param1Value",
  8. "Param2": "Param2Value"
  9. }
  10. }
  • 检索选项值并将其传递给中间件。Use… 扩展方法(该方法将中间件添加到管道)是要传入选项值的逻辑位置:
  1. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
  2. {
  3. loggerFactory.AddConsole(Configuration.GetSection("Logging"));
  4. loggerFactory.AddDebug();
  5. if (env.IsDevelopment())
  6. {
  7. app.UseDeveloperExceptionPage();
  8. app.UseBrowserLink();
  9. }
  10. else
  11. {
  12. app.UseExceptionHandler("/Home/Error");
  13. }
  14. app.UseMyMiddleware();
  15. app.UseMyMiddlewareWithParams();
  16. var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
  17. var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
  18. app.UseMyMiddlewareWithParams(myMiddlewareOptions);
  19. app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
  20. app.UseMyTerminatingMiddleware();
  21. // Create branch to the MyHandlerMiddleware.
  22. // All requests ending in .report will follow this branch.
  23. app.MapWhen(
  24. context => context.Request.Path.ToString().EndsWith(".report"),
  25. appBranch => {
  26. // ... optionally add more middleware to this branch
  27. appBranch.UseMyHandler();
  28. });
  29. app.MapWhen(
  30. context => context.Request.Path.ToString().EndsWith(".context"),
  31. appBranch => {
  32. appBranch.UseHttpContextDemoMiddleware();
  33. });
  34. app.UseStaticFiles();
  35. app.UseMvc(routes =>
  36. {
  37. routes.MapRoute(
  38. name: "default",
  39. template: "{controller=Home}/{action=Index}/{id?}");
  40. });
  41. }
  • 启用中间件以采用 options 参数。提供 Use… 扩展方法的重载(该方法采用 options 参数,并将其传递到 UseMiddleware)。如果调用带参数的 UseMiddleware,则在实例化中间件对象时,它会将参数传递给中间件构造函数。
  1. public static class MyMiddlewareWithParamsExtensions
  2. {
  3. public static IApplicationBuilder UseMyMiddlewareWithParams(
  4. this IApplicationBuilder builder)
  5. {
  6. return builder.UseMiddleware<MyMiddlewareWithParams>();
  7. }
  8. public static IApplicationBuilder UseMyMiddlewareWithParams(
  9. this IApplicationBuilder builder, MyMiddlewareOptions myMiddlewareOptions)
  10. {
  11. return builder.UseMiddleware<MyMiddlewareWithParams>(
  12. new OptionsWrapper<MyMiddlewareOptions>(myMiddlewareOptions));
  13. }
  14. }

请注意这如何包装 OptionsWrapper 对象中的 options 对象。这会按照中间件构造函数的预期实现 IOptions

迁移到新的 HttpContextMigrating to the new HttpContext

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

  1. public async Task Invoke(HttpContext context)

HttpContext 在 ASP.NET Core 中发生了重大更改。本部分演示如何将system.web的最常用属性转换为新 Microsoft.AspNetCore.Http.HttpContext

HttpContextHttpContext

HttpContext会转换为:

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

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

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

  1. string requestId = httpContext.TraceIdentifier;

HttpContext.RequestHttpContext.Request

HttpMethod转换为:

  1. string httpMethod = httpContext.Request.Method;

HttpContext转换为:

  1. IQueryCollection queryParameters = httpContext.Request.Query;
  2. // If no query parameter "key" used, values will have 0 items
  3. // If single value used for a key (...?key=v1), values will have 1 item ("v1")
  4. // If key has multiple values (...?key=v1&key=v2), values will have 2 items ("v1" and "v2")
  5. IList<string> values = queryParameters["key"];
  6. // If no query parameter "key" used, value will be ""
  7. // If single value used for a key (...?key=v1), value will be "v1"
  8. // If key has multiple values (...?key=v1&key=v2), value will be "v1,v2"
  9. string value = queryParameters["key"].ToString();

Httpcontext.current转换为( RawUrl ):

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

IsSecureConnection转换为:

  1. var isSecureConnection = httpContext.Request.IsHttps;

UserHostAddress转换为:

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

Httpcontext.current转换为:

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

RequestContext RouteData转换为:

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

Httpcontext.current转换为:

  1. // using Microsoft.AspNetCore.Http.Headers;
  2. // using Microsoft.Net.Http.Headers;
  3. IHeaderDictionary headersDictionary = httpContext.Request.Headers;
  4. // GetTypedHeaders extension method provides strongly typed access to many headers
  5. var requestHeaders = httpContext.Request.GetTypedHeaders();
  6. CacheControlHeaderValue cacheControlHeaderValue = requestHeaders.CacheControl;
  7. // For unknown header, unknownheaderValues has zero items and unknownheaderValue is ""
  8. IList<string> unknownheaderValues = headersDictionary["unknownheader"];
  9. string unknownheaderValue = headersDictionary["unknownheader"].ToString();
  10. // For known header, knownheaderValues has 1 item and knownheaderValue is the value
  11. IList<string> knownheaderValues = headersDictionary[HeaderNames.AcceptLanguage];
  12. string knownheaderValue = headersDictionary[HeaderNames.AcceptLanguage].ToString();

UserAgent转换为:

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

UrlReferrer转换为:

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

HttpContext转换为:

  1. // using Microsoft.Net.Http.Headers;
  2. MediaTypeHeaderValue mediaHeaderValue = requestHeaders.ContentType;
  3. string contentType = mediaHeaderValue?.MediaType.ToString(); // ex. application/x-www-form-urlencoded
  4. string contentMainType = mediaHeaderValue?.Type.ToString(); // ex. application
  5. string contentSubType = mediaHeaderValue?.SubType.ToString(); // ex. x-www-form-urlencoded
  6. System.Text.Encoding requestEncoding = mediaHeaderValue?.Encoding;

Httpcontext.current转换为:

  1. if (httpContext.Request.HasFormContentType)
  2. {
  3. IFormCollection form;
  4. form = httpContext.Request.Form; // sync
  5. // Or
  6. form = await httpContext.Request.ReadFormAsync(); // async
  7. string firstName = form["firstname"];
  8. string lastName = form["lastname"];
  9. }

警告

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

InputStream转换为:

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

警告

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

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

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

HttpContext.ResponseHttpContext.Response

Httpcontext.current转换为( StatusDescription ):

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

ContentEncodinghttpcontext转换为以下内容:

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

Httpcontext.current还会转换为:

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

Httpcontext.current转换为:

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

TransmitFile

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

Httpcontext.current 标头

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

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

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

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

SetHeaders 回调方法如下所示:

  1. // using Microsoft.AspNet.Http.Headers;
  2. // using Microsoft.Net.Http.Headers;
  3. private Task SetHeaders(object context)
  4. {
  5. var httpContext = (HttpContext)context;
  6. // Set header with single value
  7. httpContext.Response.Headers["ResponseHeaderName"] = "headerValue";
  8. // Set header with multiple values
  9. string[] responseHeaderValues = new string[] { "headerValue1", "headerValue1" };
  10. httpContext.Response.Headers["ResponseHeaderName"] = responseHeaderValues;
  11. // Translating ASP.NET 4's HttpContext.Response.RedirectLocation
  12. httpContext.Response.Headers[HeaderNames.Location] = "http://www.example.com";
  13. // Or
  14. httpContext.Response.Redirect("http://www.example.com");
  15. // GetTypedHeaders extension method provides strongly typed access to many headers
  16. var responseHeaders = httpContext.Response.GetTypedHeaders();
  17. // Translating ASP.NET 4's HttpContext.Response.CacheControl
  18. responseHeaders.CacheControl = new CacheControlHeaderValue
  19. {
  20. MaxAge = new System.TimeSpan(365, 0, 0, 0)
  21. // Many more properties available
  22. };
  23. // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
  24. return Task.FromResult(0);
  25. }

HttpContext. Cookie

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

  1. public async Task Invoke(HttpContext httpContext)
  2. {
  3. // ...
  4. httpContext.Response.OnStarting(SetCookies, state: httpContext);
  5. 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); 
}

其他资源Additional resources