ASP.NET Core MVC 从入门到精通之HttpContext

随着技术的发展, ASP.NET Core MVC也推出了好长时间,经过不断的版本更新迭代,已经越来越完善,本系列文章主要讲解 ASP.NET Core MVC开发B/S系统过程中所涉及到的相关内容,适用于初学者,在校毕业生,或其他想从事 ASP.NET Core MVC 系统开发的人员。经过前几篇文章的讲解,初步了解 ASP.NET Core MVC项目创建,启动运行,以及命名约定,创建控制器,视图,模型,接收参数,传递数据,路由,页面布局,wwwroot和客户端库,Razor语法,EnityFrameworkCore与数据库等内容,今天继续讲解 ASP.NET Core MVC 中 HttpContext 等相关内容,仅供学习分享使用。


什么是HttpContext?



在B/S模式开发的程序中,客户端是浏览器,服务器端Web服务程序,HttpContext是连接客户端和服务器端程序的桥梁,交代了当前请求的环境信息,它封装了请求[Request]和响应[Response]及其他所有信息,示意图如下所示:
图一 内网访问程序

图二 反向代理访问程序

在示意图中,Kestrel 是一个基于 libuv 的跨平台 ASP.NET Core web服务器。不清楚 Kerstrel 没关系,以后慢慢了解。

注意: HttpContext从客户端发起一个请求开始,到服务器端响应完成结束,每一个新的请求,都会创建一个新的HttpContext对象。


HttpContext属性



在HttpContext中,最常用的属性有3个【Request,Response,Session】具体属性如下表所示:


控制器中应用HttpContext



在控制器中,HttpContext作为控制器父类ControllerBase的属性存在,且Request和Response作为使用频率非常高的常用对象,控制器也声明成了属性,都可以直接使用。如下所示:


控制器外使用HttpContext



在应用程序中,控制器继承了Controller类,所以才能直接使用HttpContext,但是除了控制器,还有其他的程序。那在其他程序中如何使用HttpContext呢?

控制器外使用HttpContext,步骤如下:

首先有一个服务接口IStudentService和服务实现类StudentService,在StudentService中访问HttpContext,如下所示:

namespace DemoCoreMVC.Services{ public interface IStudentService { /// <summary> /// 保存类 /// </summary> void Save(); }} namespace DemoCoreMVC.Services{ public class StudentService : IStudentService { private readonly IHttpContextAccessor contextAccessor; public StudentService(IHttpContextAccessor contextAccessor) { this.contextAccessor = contextAccessor; } public void Save() { var name = this.contextAccessor.HttpContext?.Request.Query["Name"]; Console.WriteLine(name); } }}

在控制器中,通过构造函数的方式将IStudentService注入进去,如下所示:

using DemoCoreMVC.Services;using Microsoft.AspNetCore.Mvc; namespace DemoCoreMVC.Controllers{ public class StudentController : Controller { private readonly IStudentService studentService; public StudentController(IStudentService studentService) { this.studentService = studentService; } public IActionResult Save() { studentService.Save(); return Json("成功"); } public IActionResult Index() { return View(); } }}

在Program.cs中,将服务添加到容器中,如下所示:

//增加一个默认的HttpContextAccessorbuilder.Services.AddHttpContextAccessor();//增加服务builder.Services.AddScoped<IStudentService, StudentService>();

经过以上3步,就可以实现在控制器之外的类中,访问HttpContext,测试示例,如下所示:

注意: ASP.NET Core MVC项目中,对象的创建,优先从容器中获取,这样可以不需要考虑它的创建过程和构造参数。如:创建服务Service,控制器对象Controller,视图对象View,数据访问层Repository等内容。对于模型对象,如视图模型,数据模型等不依赖其他对象的类型,则可以通过New进行创建。


HttpRequest



HttpRequest表示单个请求的传入端,常用的Query用于获取Get请求传递的参数,Form用于获取Post请求传递的参数,如下所示:


HttpRequest示例



在本示例中,以Request.Form为例,获取Post方式传递的参数,客户端将所有需要传递的内容包括在Form表单内容,在服务器端Action中通过Request.Form["Key"]进行获取。如下所示:

Add.cshtml视图中Form表单内容,如下所示:

<form action="~/Hello/Save" method="post"> <div style="margin:10px;"> <span>学号:</span> <input type="text" name="Id" /> </div> <div style="margin:10px;"> <span>姓名:</span> <input type="text" name="Name" /> </div style="margin:10px;"> <div style="margin:10px;"> <span>年龄:</span> <input type="text" name="Age" /> </div> <div style="margin:10px;"> <span>性别:</span> <input type="text" name="Sex" /> </div> <div style="margin:10px;"> <input type="submit" name="submit" value="保存" /> </div></form>

HelloController中Save方法,如下所示:

[HttpPost]public IActionResult Save(){ var id = Request.Form["Id"]; var name = Request.Form["Name"]; var age = Request.Form["Age"]; var sex = Request.Form["Sex"]; var student = new Student() { Id = string.IsNullOrEmpty(id) ? 0 : int.Parse(id), Name = name, Age = string.IsNullOrEmpty(age) ? 0 : int.Parse(age), Sex = sex }; return Json(student);}

运行测试,在浏览器中输入网址【https://localhost:7116/Hello/add】进行测试,如下所示:


HttpRequest其它示例



HttpRequest中的其它示例,如下所示:

public IActionResult Index(){ Console.WriteLine($"Request.Host:{Request.Host}" ); Console.WriteLine($"Request.Path:{Request.Path}"); Console.WriteLine($"Request.Protocol:{Request.Protocol}"); Console.WriteLine($"Request.ContentType:{Request.ContentType}"); Console.WriteLine($"Request.Headers:"); foreach(var header in Request.Headers) { Console.WriteLine($"{header.Key}:{header.Value}"); } Console.WriteLine($"Request.Cookies:"); foreach (var cookie in Request.Cookies) { Console.WriteLine($"{cookie.Key}:{cookie.Value}"); } return View();}

其它属性示例截图,如下所示:

注意: 在Request的Get请求中,默认ContentType为空,Cookies如果没有设置,也为空。

Cookie存放于客户端浏览器中,可以通过浏览器开发者模式F12下进行查看,以 bilibili.com 为例,如下所示:


HttpResponse



HttpResponse表示单个请求的传出内容,


状态码StatusCode



StatusCode是一个int类型,表示当前响应Http请求的状态,可以通过System.Net.HttpStatusCode(枚举)进行转换,常用的有以下几种:

  1. OK = 200,成功,这是最常用的一个响应状态码

  2. NotFound = 404, 未发现,即请求的信息不存在

  3. InternalServerError = 500,服务器内部错误

  4. Redirect = 302, 请求已被重定向

在Controller中,常见的状态码返回值,已被定义为方法,如:Ok(),NotFound()等,可以直接调用。


HttpResponse示例



在响应的Headers中,添加Author信息,如下所示:

public IActionResult Test2(){ Response.Headers.Add("Author", "公子小六"); return Json("ABC");}

在添加Headers时,如果是汉字,则会报下面一个错误,如下所示:

以上错误表示编码错误,汉字无效,需要进行编码转换,如下所示:

public IActionResult Test2(){varauthor=HttpUtility.UrlEncode("公子小六",Encoding.UTF8);Response.Headers.Add("Author",author);returnJson("ABC");}

请求示例如下所示:


会话Session



由于Http请求是无状态的,单次请求完成后,就会进行释放,那么如何在无状态的请求中,保留一些相关的数据呢?这就用到了Session,Session在用户打开浏览器登录系统开始,到关闭浏览器退出系统结束,将用户请求的一些数据,以键值对的形式保存在服务器端的缓存中,可以解决无状态协议模式下数据的频繁传递传递,减少请求数据量,提高性能。 Session一般应用在小型的单体应用程序中,对于大型的分布式程序,则不适用。

每一个用户的浏览器请求都有自己的Session内存块,不会和其他用户的请求相混淆。

要启用Session,首先需要在Program.cs中添加Session服务,和启用Session中间件,如下所示:

using DemoCoreMVC.Services;using Microsoft.AspNetCore.Server.Kestrel.Core;using System.Text.Encodings.Web;using System.Text.Unicode; var builder = WebApplication.CreateBuilder(args); // Add services to the container.builder.Services.AddControllersWithViews().AddJsonOptions(options =>{ options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All);}); builder.Services.Configure<KestrelServerOptions>(options =>{ options.AllowSynchronousIO = true;}); //1. 往容器中添加Session服务,启用Session服务builder.Services.AddSession(); var app = builder.Build(); // Configure the HTTP request pipeline.if (!app.Environment.IsDevelopment()){ app.UseExceptionHandler("/Home/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts();} //2.使用Session中间件,主要用于拦截Http请求app.UseSession();app.UseHttpsRedirection();app.UseStaticFiles(); //1. 添加路由中间件EndpointRoutingMiddlewareapp.UseRouting();app.MapControllers();app.UseAuthorization();