• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

C#6与.NETCore1.0高级编程-40ASP.NETCore(下)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

 译文,个人原创,转载请注明出处(C# 6 与 .NET Core 1.0 高级编程 - 40 章  ASP.NET Core(下)),不对的地方欢迎指出与交流。  

章节出自《Professional C# 6 and .NET Core 1.0》。水平有限,各位阅读时仔细分辨,唯望莫误人子弟。  

附英文版原文:

本章节译文分为上下篇,上篇见:C# 6 与 .NET Core 1.0 高级编程 - 40 ASP.NET Core(上)

---------------------------------------------

通过HTTP协议,客户端向服务器发出请求。该请求通过响应回答。

该请求包括头部,并且在许多情况下,包括到服务器的主体信息。服务器根据客户端的需要通过主体信息定义不同的结果。来看看可以从客户端读取什么信息。

要将HTML格式的输出返回到客户端,GetDiv方法创建一个div元素,其中包含传递的参数key和value(代码文件WebSampleApp/RequestAndResponseSample.cs)的span元素:

public static string GetDiv(string key, string value) =>
  $"<div><span>{key}:</span><span>{value}</span></div>";

因为在以下示例中需要这些HTML div和span标签来包围字符串,所以创建扩展方法来覆盖该功能(代码文件WebSampleApp/HtmlExtensions.cs):

public static class HtmlExtensions
{
  public static string Div(this string value) =>
    $"<div>{value}</div>";
  public static string Span(this string value) =>
    $"<span>{value}</span>";
}

方法 GetRequestInformation 使用 HttpRequest 去对象访问Scheme,Host,Path,QueryString,Method和Protocol属性(代码文件WebSampleApp/RequestAndResponseSample.cs):

public static string GetRequestInformation(HttpRequest request)
{
  var sb = new StringBuilder();
  sb.Append(GetDiv("scheme", request.Scheme));
  sb.Append(GetDiv("host", request.Host.HasValue ? request.Host.Value :
    "no host"));
  sb.Append(GetDiv("path", request.Path));
  sb.Append(GetDiv("query string", request.QueryString.HasValue ?
    request.QueryString.Value :"no query string"));
  sb.Append(GetDiv("method", request.Method));
  sb.Append(GetDiv("protocol", request.Protocol));
  return sb.ToString();
}

Startup类的Configure方法更改为调用GetRequestInformation方法,并通过HttpContext的Request属性传递HttpRequest。 结果写入Response对象(代码文件WebSampleApp/Startup.cs):

app.Run(async (context) =>
{
  await context.Response.WriteAsync(RequestAndResponseSample.GetRequestInformation(context.Request));
});

从Visual Studio启动程序将产生以下信息:

scheme:http
host:localhost:5000
path: /
query string: no query string
method: GET
protocol: HTTP/1.1

添加一个路径到路径值的请求结果,例如 http://localhost:5000/Index,设置如下:

scheme:http
host:localhost:5000
path: /Index
query string: no query string
method: GET
protocol: HTTP/1.1

添加查询字符串,如 http://localhost:5000/Add?x=3&y=5, 查询字符串访问  QueryString,如下所示:

query string: ?x=3&y=5

下一个代码片段中,使用HttpRequest的Path属性来创建轻量级自定义路由。 根据客户端设置的路径,调用不同的方法(代码文件WebSampleApp/Startup.cs):

app.Run(async (context) =>
{
  string result = string.Empty;
  switch (context.Request.Path.Value.ToLower())
  {
    case"/header":
      result = RequestAndResponseSample.GetHeaderInformation(context.Request);
      break;
    case"/add":
      result = RequestAndResponseSample.QueryString(context.Request);
      break;
    case"/content":
      result = RequestAndResponseSample.Content(context.Request);
      break;
    case"/encoded":
      result = RequestAndResponseSample.ContentEncoded(context.Request);
      break;
    case"/form": 
      result = RequestAndResponseSample.GetForm(context.Request);
      break;
    case"/writecookie":
      result = RequestAndResponseSample.WriteCookie(context.Response);
      break;
    case"/readcookie":
      result = RequestAndResponseSample.ReadCookie(context.Request);
      break;
    case"/json":
      result = RequestAndResponseSample.GetJson(context.Response);
      break;
    default:
      result = RequestAndResponseSample.GetRequestInformation(context.Request);
      break;
  }
  await context.Response.WriteAsync(result);
});

以下部分将实现不同的方法来显示请求头信息,查询字符串等。

请求头信息

来看看客户端在HTTP头信息中发送的信息。 为了访问HTTP头信息,HttpRequest对象定义Headers属性。 这是IHeaderDictionary类型,它包含一个头的名称和值的字符串数组的字典。 使用此信息,先前创建的GetDiv方法用于为客户端写入div元素(代码文件WebSampleApp/RequestAndResponseSample.cs):

public static string GetHeaderInformation(HttpRequest request)
{
  var sb = new StringBuilder();
  IHeaderDictionary headers = request.Headers;
  foreach (var header in request.Headers)
  {
    sb.Append(GetDiv(header.Key, string.Join(";", header.Value)));
  }
  return sb.ToString();
}

结果取决于所使用的浏览器。 我们来比较一下他们中的几个。 以下是来自Windows 10触摸设备上的Internet Explorer 11:

Connection: Keep-Alive
Accept: text/html,application/xhtml+xml,image/jxr,*.*
Accept-Encoding: gzip, deflate
Accept-Language: en-Us,en;q=0.8,de-AT;q=0.6,de-DE;q=0.4,de;q=0.2
Host: localhost:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; Touch; 
rv:11.0)
like Gecko 

Google Chrome 47.0版显示此信息,包括来自AppleWebKit,Chrome和Safari的版本号:

Connection: keep-alive
Accept: 
text/html,application/xhtml,application/xml;q=0.9,image/webp,*.*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-Us;en;q=0.8
Host: localhost:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36
(KHTML, like Gecko) Chrome 47.0.2526.80 Safari/537.36

Microsoft Edge提供了此信息,包括来自AppleWebKit,Chrome,Safari和Edge的版本号:

Connection: Keep-Alive
Accept: text/html,application/xhtml+xml,image/jxr,*.*
Accept-Encoding: gzip, deflate
Accept-Language: en-Us,en;q=0.8,de-AT;q=0.6,de-DE;q=0.4,de;q=0.2
Host: localhost:5000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
(KHTML,

从这个头信息中可以得出什么结论?

Connection头是HTTP 1.1协议的增强。有了这个,客户端可以请求保持连接打开。通常使用HTML,客户端发出多个请求,例如以获取图像,CSS和JavaScript文件。服务器可能会满足请求,也可能会忽略该请求以防负载过高,最好是关闭连接。

Accept头定义了浏览器接受的mime格式。该列表按优先格式排序。根据该信息,可能会决定根据客户端的需要以不同的格式返回数据。 IE更适应HTML格式,然后是XHTML和JXR。 Google Chrome则是不同的列表,它更喜欢这些格式:HTML,XHTML,XML和WEBP。利用这些信息中的一些,还定义了量词。用于输出的浏览器在此列表的末尾都有*。*,以接受返回的所有数据。

Accept-Language头信息显示用户已配置的语言。该信息可以返回本地化信息。本地化在第28章“本地化”中讨论。

注意 很久以前,服务器保留了很长的浏览器功能列表。这些列表用于了解浏览器可以使用的功能。要识别浏览器,可以使用来自浏览器的用于映射功能的代理字符串。随着时间的推移,浏览器提供错误的信息,甚至允许用户配置想要的浏览器名称,以便得到一些更多的功能(因为浏览器列表通常没有在服务器上更新)。过去Internet Explorer(IE)通常需要与所有其他浏览器不同的编程。 Microsoft Edge与IE非常不同,并且有更多与其他供应商的浏览器相同的功能。这就是为什么Microsoft Edge在User-Agent字符串中显示Mozilla,AppleWebKit,Chrome,Safari和Edge。最好不要使用此User-Agent字符串来获取可用的功能列表。相反,请检查需要编程的特定功能。

目前为止,通过浏览器发送的头信息是发送到非常简单的网站的信息。通常会有更多的细节,如Cookie,身份验证信息,以及自定义信息。要查看发送到服务器和从服务器发送的所有信息(包括标题信息),可以使用浏览器的开发人员工具并启动网络会话,不仅可以看到发送到服务器的所有请求,而且还会看到头,主体,参数,Cookie和计时信息,如图40.11所示。 

图40.11

查询字符串

可以使用Add方法分析查询字符串。该方法需要 x 和 y 参数,如果这些参数是数字则相加,并以div标记返回计算结果。上一节中显示的方法 GetRequestInformation 演示了如何使用 HttpRequest 对象的 QueryString 属性访问完整的查询字符串。要访问查询字符串的部分,可以使用Query属性。以下代码片段使用Get方法访问 x 和 y 的值。如果在查询字符串中找不到相应的键,此方法将返回null(代码文件WebSampleApp/RequestAndResponseSample.cs):

public static string QueryString(HttpRequest request)
{
  var sb = new StringBuilder();
  string xtext = request.Query["x"];
  string ytext = request.Query["y"];
  if (xtext == null &DoubleVerticalBar; ytext == null)
  {
    return"x and y must be set";
  } 
  int x, y;
  if (!int.TryParse(xtext, out x))
  {
    return $"Error parsing {xtext}";
  }
  if (!int.TryParse(ytext, out y))
  {
    return $"Error parsing {ytext}";
  }
  return $"{x} + {y} = {x + y}".Div();
}

从Query字符串返回的 IQueryCollection 还允许使用Keys属性访问所有键,它还提供了一个 ContainsKey 方法来检查指定的键是否可用。

使用URL  http://localhost:5000/add?x=39&y=3 在浏览器中显示此结果:

39 + 3 = 42

编码

返回用户输入的数据可能很危险。我们可以用Content方法做到这一点。以下方法直接返回通过查询数据字符串传递的数据(代码文件WebSampleApp/RequestAndResponseSample.cs):

public static string Content(HttpRequest request) => request.Query["data"];

使用URL  http://localhost:5000/content?data=sample 调用此方法,只返回字符串"sample"。使用相同的方法,用户还可以传递HTML内容,如 http://localhost:5000/content?data=<h1>Heading 1</h1> 是什么结果?图40.12显示了h1元素由浏览器解释,文本以标题格式显示。在某些情况下,用户希望允许这样做 - 例如,当用户(可能不是匿名用户)正在为网站编写文章时。

 

图40.12

在不检查用户输入的情况下,用户也可以传递诸如  http://localhost:5000/content?data=<script>alert(“hacker”);</script> 。可以使用JavaScript警报功能弹出消息框。将用户重定向到其他网站也很容易。当此用户输入存储在站点中时,一个用户可以输入这样的脚本,并且打开该页面的所有其他用户被相应地重定向。

返回用户输入的数据应始终编码。要结果有没有编码,可以使用 HtmlEncoder 类进行HTML编码,如以下代码段中所示(代码文件WebSampleApp/RequestResponseSample.cs):

public static string ContentEncoded(HttpRequest request) =>
  HtmlEncoder.Default.Encode(request.Query["data"]);

注意 使用 HtmlEncoder 需要NuGet包 System.Text.Encodings.Web。

运行应用程序,使用  http://localhost:5000/encoded?data=<script>alert(“hacker”);</script>  传递具有编码的相同JavaScript代码,客户端只看到JavaScript代码在浏览器中,它没有被解释(见图40.13)。

图40.13

发送的编码字符串类似于以下示例 - 字符引用小于号(<),大于号(>)和引号(“):

<script>alert("hacker");</script>

表单数据

不要用查询字符串将数据从用户传递到服务器,而是使用表单HTML元素。示例使用HTTP POST请求,而不是GET。使用POST请求时用户数据与请求的正文一起传递,而不是以查询字符串方式传递。

使用表单数据定义有两个请求。首先,表单通过GET请求发送到客户端,然后用户填写表单并使用POST请求提交数据。通过传递/ form路径调用的方法依次调用GetForm或ShowForm方法,具体取决于HTTP方法类型(代码文件WebSampleApp/RequestResponseSample.cs):

public static string GetForm(HttpRequest request)
{
  string result = string.Empty;
  switch (request.Method)
  {
    case"GET":
      result = GetForm();
      break;
    case"POST":
      result = ShowForm(request);
      break;
    default:
      break;
  }
  return result;
}

该表单创建 text1的输入元素和 Submit 按钮创建。 单击 Submit 按钮使用方法参数定义的HTTP方法调用表单的 action 方法:

private static string GetForm() =>
  "<form method=\"post\" action=\"form\">" +
    "<input type=\"text\" name=\"text1\" />" +
    "<input type=\"submit\" value=\"Submit\" />" +
  "</form>";

为了读取表单数据,HttpRequest类定义了一个Form属性。 该属性返回一个IFormCollection对象,其中包含发送到服务器的表单中的所有数据:

private static string ShowForm(HttpRequest request)
{
  var sb = new StringBuilder();
  if (request.HasFormContentType)
  {
    IFormCollection coll = request.Form;
    foreach (var key in coll.Keys)
    {
      sb.Append(GetDiv(key, HtmlEncoder.Default.Encode(coll[key])));
    }
    return sb.ToString();
  }
  else return"no form".Div();
}

使用/form 链接,GET请求接收到表单(参见图40.14)。单击提交按钮时,表单与POST请求一起发送,可以看到表单数据的text1 内容(参见图40.15)。 

图40.14

 

图40.15

Cookies

要记住多个请求之间的用户数据,可以使用Cookie。将Cookie添加到 HttpResponse 对象将HTTP头中的cookie从服务器发送到客户端。默认情况下,Cookie是临时的(不存储在客户端上),如果URL是来自Cookie的同一个域,则浏览器将其发送回服务器。可以设置路径来限制浏览器返回Cookie的时间。在这种情况下,只有当它来自同一个域并且使用路径/cookies时才返回Cookie。设置Expires属性时,cookie是一个持久性cookie,因此存储在客户端上。超时后cookie将被移除。然而也无法保证Cookie不被提前删除(代码文件WebSampleApp/RequestResponseSample.cs):

public static string WriteCookie(HttpResponse response)
{
  response.Cookies.Append("color","red",
    new CookieOptions
    {
      Path ="/cookies",
      Expires = DateTime.Now.AddDays(1)
    });
  return"cookie written".Div();
}

通过读取 HttpRequest 对象可以再次读取cookie。 Cookie属性包含浏览器返回的所有Cookie:

public static string ReadCookie(HttpRequest request)
{
  var sb = new StringBuilder(); 
  IRequestCookieCollection cookies = request.Cookies;
  foreach (var key in cookies.Keys)
  {
    sb.Append(GetDiv(key, cookies[key]));
  }
  return sb.ToString();
}

测试Cookie,也可以使用浏览器的开发人员工具。 这些工具显示有关发送和接收的Cookie的所有信息。

发送JSON

服务器返回超过HTML代码,也返回许多不同类型的数据格式,如CSS文件,图像和视频。 客户端知道它在响应头中的MIME类型的帮助下接收什么类型的数据。

方法 GetJson 从具有 Title,Publisher和Author 属性的匿名对象创建JSON字符串。 要使用JSON序列化对象,需要添加NuGet包NewtonSoft.Json,并导入命名空间NewtonSoft.Json。 JSON格式的MIME类型是application/json。 这是通过HttpResponse的ContentType属性设置的(代码文件WebSampleApp/RequestResponseSample.cs):

public static string GetJson(HttpResponse response)
{
  var b = new
  {
    Title ="Professional C# 6",
    Publisher ="Wrox Press",
    Author ="Christian Nagel"
  };
  string json = JsonConvert.SerializeObject(b);
  response.ContentType ="application/json";
  return json;
}

注意 要使用JsonConvert类,需要添加NuGet包Newtonsoft.Json。

以下是返回给客户端的数据。

{"Title":"Professional C# 6","Publisher":"Wrox Press",
 "Author":"Christian Nagel"}

注意 第42章“ASP.NET Web API”中介绍了发送和接收JSON。

依赖注入

依赖注入深深集成在ASP.NET Core中。此设计模式提供松耦合,因为服务仅用于接口。实现接口的具体类型是注入的。使用ASP.NET内置依赖注入机制,注入通过具有注入接口类型的参数的构造函数进行。

依赖注入分离服务契约和服务实现。该服务可以在不知道具体实现的情况下使用 - 只需要一个合同。这允许在单个位置替换所有使用服务的服务(例如日志记录)。

让我们通过创建自定义服务来更详细地了解依赖注入。

定义服务

首先,声明示例服务的合同。通过接口定义合同可以将服务实现与其使用分离 - 例如,使用不同的实现进行单元测试(代码文件WebSampleApp/Services/ISampleService.cs):

public interface ISampleService
{
  IEnumerable<string> GetSampleStrings();
}

类DefaultSampleService实现接口ISampleService(代码文件WebSampleApp/Services/DefaultSampleService.cs):

public class DefaultSampleService : ISampleService
{
  private List<string> _strings = new List<string> {"one","two","three" };
  public IEnumerable<string> GetSampleStrings() => _strings;
}

注册服务

使用 AddTransient 方法(这是程序集 Microsoft.Extensions.DependencyInjection.Abstractions 在命名空间Microsoft.Extensions.DependencyInjection 中定义的 IServiceCollection 的扩展方法),DefaultSampleService 类型映射到ISampleService。 使用ISampleService接口时,DefaultSampleService类型将被实例化(代码文件WebSampleApp/Startup.cs):

public void ConfigureServices(IServiceCollection services)
{
  services.AddTransient<ISampleService, DefaultSampleService>();
  // etc.
} 

内置依赖注入服务定义了几个生存期类型。AddTransient 方法每次注入服务时都会重新实例化服务。

使用AddSingleton方法,服务只被实例化一次。每次注入都使用相同的实例:

services.AddSingleton <ISampleService,DefaultSampleService>();

AddInstance 方法需要实例化一个服务并将实例传递给此方法。这样就定义了服务的生命周期:

var sampleService = new DefaultSampleService();
services.AddInstance<ISampleService>(sampleService);

第四种服务的生存期基于当前上下文。ASP.NET MVC 当前上下文基于HTTP请求。只要调用相同请求的操作,不同注入使用相同的实例。使用新请求,将创建一个新实例。为了定义基于上下文的生命周期,AddScoped 方法将服务契约映射到服务:

services.AddScoped<ISampleService>();

注入服务

服务注册后,可以注入它。在目录Controllers中创建名为HomeController的控制器类型。内置依赖注入框架会使用构造函数注入,因此定义了接收 ISampleService 接口的构造函数。方法Index接收 HttpContext 并且可以使用它来读取请求信息,并返回一个 HTTP 状态值。在实现中,ISampleService 用于从服务获取字符串。控制器添加一些HTML元素将字符串放入列表(代码文件WebSampleApp/Controllers/HomeController.cs):

public class HomeController
{
  private readonly ISampleService _service;
  public HomeController(ISampleService service)
  {
    _service = service;
  }
  public async Task<int> Index(HttpContext context)
  {
    var sb = new StringBuilder();
    sb.Append("<ul>");
    sb.Append(string.Join("", _service.GetSampleStrings().Select(
        s => $"<li>{s}</li>").ToArray()));
    sb.Append("</ul>");
    await context.Response.WriteAsync(sb.ToString());
    return 200;
  } 
}

注意 此示例控制器直接返回HTML代码。 实际上最好将功能与用户界面分开,并从不同的 类 - 视图 创建HTML代码。 这种分离最好使用一个框架:ASP.NET MVC。 这个框架在第41章中解释。

调用控制器

要通过依赖注入来实例化控制器,HomeController 类是用 IServiceCollection 服务注册的。 这一次不使用接口,因此只需要使用 AddTransient 方法调用具体实现服务类型(代码文件WebSampleApp/Startup.cs):

public void ConfigureServices(IServiceCollection services)
{
  services.AddTransient<ISampleService, DefaultSampleService>();
  services.AddTransient<HomeController>();
  // etc.
}

包含路由信息的 Configure 方法现在已更改以检查 /home 路径。 如果表达式返回 true,HomeController 通过依赖注入通过调用注册的应用程序服务上的 GetService 方法来实例化。 IApplicationBuilder 接口定义了一个ApplicationServices 属性,返回实现 IServiceProvider 的对象。 这里可以访问已注册的所有服务。 使用这个控制器,通过传递 HttpContext 来调用Index方法。 状态代码将写入应答对象:

public void Configure(IApplicationBuilder app, ILoggerFactory 
loggerFactory)
{
  app.Run(async (context) =>
  {
    // etc.
    if (context.Request.Path.Value.ToLower() =="/home")
    {
      HomeController controller =
        app.ApplicationServices.GetService<HomeController>();
      int statusCode = await controller.Index(context);
      context.Response.StatusCode = statusCode;
      return;
    }
  });
  // etc.
}

图40.16显示了运行 home 地址URL的应用程序时无序列表的输出

 

图40.16

路由使用映射

前面的代码片段中,当URL的路径是 “/home”时,调用HomeController类。 没有去留意查询字符串或子文件夹。 当然,可以通过只检查字符串的一个子集来做到这一点。 但是,有一个更好的方法。 ASP.NET支持使用IApplicationBuilder 的扩展的子应用程序:Map方法。以下代码片段定义了到 /home2 路径的映射,并运行HomeController的Invoke方法(代码文件WebSampleApp/Startup.cs):

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
  // etc.
  app.Map("/home2", homeApp =>
  {
    homeApp.Run(async context =>
    {
      HomeController controller =
        app.ApplicationServices.GetService<HomeController>();
      int statusCode = await controller.Index(context);
      context.Response.StatusCode = statusCode;
    });
  });
  // etc.
}

不仅可以使用Map方法,也可以使用MapWhen。 使用以下代码段,MapWhen 管理的映射在路径以 /configuration 开头时适用。 剩余的路径写入到剩余的变量,可以用于方法调用的不同:

PathString remaining;
app.MapWhen(context =>
  context.Request.Path.StartsWithSegments("/configuration", out remaining),
    configApp =>
    {
      configApp.Run(async context =>
      {
        // etc.
      }
    });

可以访问 HttpContext 的任何其他信息,例如客户端的主机信息,而不仅仅使用该路径(context.Request.Host)或已认证的用户(context.User.Identity.IsAuthenticated)。

使用中间件

ASP.NET Core可以轻松创建在调用控制器之前调用的模块。它可以用于添加头信息,验证令牌,构建缓存,创建日志跟踪等。一个中间件模块在另一个之后被链接,直到所有连接的中间件类型被调用。

可以使用Visual Studio项目模板中间件类创建中间件类。使用此中间件类型,可以创建接收对下一个中间件类型的引用的构造函数。 RequestDelegate是一个委托,它接收一个HttpContext作为参数并返回一个Task。这正是Invoke方法的签名。在此方法中,您可以访问请求和响应信息。类型HeaderMiddleware向HttpContext的响应添加一个样本头。作为最后一个操作,Invoke方法调用下一个中间件模块(代码文件WebSampleApp/Middleware/HeaderMiddleware.cs):

public class HeaderMiddleware
{
  private readonly RequestDelegate _next;
  public HeaderMiddleware(RequestDelegate next)
  {
    _next = next;
  }
  public Task Invoke(HttpContext httpContext)
  {
    httpContext.Response.Headers.Add("sampleheader",
      new string[] {"addheadermiddleware"});
    return _next(httpContext);
  }
}

为了方便配置中间件类型,扩展方法 UseHeaderMiddleware 扩展了接口 IApplicationBuilder,它调用方法UseMiddleware :

public static class HeaderMiddlewareExtensions
{
  public static IApplicationBuilder UseHeaderMiddleware(
    this IApplicationBuilder builder) =>
      builder.UseMiddleware<HeaderMiddleware>();
}

另一种中间件类型是 Heading1Middleware。 这种类型类似于以前的中间件类型,它只将 heading 1 写入响应(代码文件WebSampleApp/Middleware/Heading1Middleware.cs):

public class Heading1Middleware
{
  private readonly RequestDelegate _next; 
  public Heading1Middleware(RequestDelegate next)
  {
    _next = next;
  }
  public async Task Invoke(HttpContext httpContext)
  {
    await httpContext.Response.WriteAsync("<h1>From Middleware</h1>");
    await _next(httpContext);
  }
}
public static class Heading1MiddlewareExtensions
{
  public static IApplicationBuilder UseHeading1Middleware(
    this IApplicationBuilder builder) =>
      builder.UseMiddleware<Heading1Middleware>();
}

现在轮到Startup类和Cofigure 方法工作,配置所有中间件类型。 扩展方法已经准备好调用(代码文件WebSampleApp/Startup.cs):

public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
  // etc.

  app.UseHeaderMiddleware();
  app.UseHeading1Middleware();
  // etc.
}

运行应用程序时,将看到返回到客户端的标题(使用浏览器的开发人员工具),并且标题会显示在每个页面中,无论先前创建的链接是什么(参见图40.17)。 

图40.17

会话状态

使用中间件实现的服务是会话状态。会话状态允许服务器临时记住来自客户端的数据。会话状态本身被实现为中间件。

会话状态在用户首次从服务器请求页面时启动。当用户在服务器上保持打开页面时,会话继续保持直到超时(通常为10分钟)发生。为了在用户导航到新页面时保持服务器上的状态,可以将状态写入会话。当达到超时时,会话数据将被移除。

为了识别会话,第一次请求会创建有会话标识符的临时cookie。每次请求服务器时 cookie 从客户端返回,直到浏览器关


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
C#抓取页面时候,获取页面跳转后的地址发布时间:2022-07-10
下一篇:
C#批量处理参数化SQL发布时间:2022-07-10
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap