ASP.NET Core MVC 快速入门
官网文档
ASP.NET Core MVC 入门 | Microsoft Learn
ASP.NET Core MVC 概述 | Microsoft Learn
启用MVC程序模型
ASP.NET MVC(模型-视图-控制器)应用程序模型。在这个模型中,传入请求的URL被解析为一个控制器/操作项对。分别对应控制器以及里面的方法。因此处理请求就是执行给定 Controller 类上的给定操作方法。
在 ASP.NET Core 中要想应用MVC,必须通过代码显式启用MVC应用程序模型。
ASP.NET Core 3.1 中,比较简单的是在 startup 类中加上services.AddControllersWithViews();
这句代码,如下所示:
public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); }
类似的在 .net 6 中,在 Program.cs 类中,加上下面这句代码即可:
builder.Services.AddControllersWithViews();
如果是webapi项目,直接 services.AddControllers();
即可。
路由
路由就是应用程序中能够识别和处理的URL模板,它最终将被映射到 某Controller 类中的某个方法。
与路由相关的 EndpointRoutingMiddleware 和 EndpointMiddleware 是两个最为重要的中间件。
MVC应用以Controller为核心,所有的请求总是指向定义在某个Controller类型中的某个Action方法。当应用接收到请求之后,会激活对应的Controller对象,并通过执行对应的Action方法来处理该请求。
一个Web应用本质上体现为一组终结点(EndPoint)的集合.所谓的终结点对于客户端来说就是可以远程调用的服务。路由的作用就是建立一个请求URL模式与对应终结点之间的映射关系,本质上就是将请求导向对应终结点的过程。
Endpoint(终结点)
HTTP 终结点是 Web 应用程序中可定向的 URL。所谓的终结点对于客户端来说就是可以远程调用的服务。
对于一个MVC应用程序来说,我们可以将定义在Contrller类型中的Action方法视为一个终结点(EndPoint),那么路由映射最终体现在HTTP请求与目标Action方法的映射上。
MVC应用的三个关键点:
定义控制器
相关服务注册和终结点映射:
IServiceCollection对象的AddControllers扩展方法注册了与Controller相关服务的注册,注意,这并没有涉及到视图,请求的响应内容是由Action方法直接提供的,这通常是只想开发WebAPI时的做法。要想涉及到视图,需调用IServiceCollection接口的 AddControllersWithViews 方法,而不是 AddControllers。
在WebApplication对象被构建出来后,我们调用了它的MapControllers扩展方法将定义在所有Controller类型中的Action方法映射为对应的终结点。ASP.NET Core MVC采用Razior视图引擎,视图被定义成一个后缀名为.cshtml的文件,这是一个按照Razor语法编写的静态HTML和动态C#代码动态交织的文本文件。
不管是MVC控制器还是API控制器,如果操作方法的返回类型是ActionResult<T>
,ASP.NET Core 自动将被返回的对象序列化为 JSON,并将 JSON 写入响应消息的正文中。
启用和配置路由
启用和配置路由,只需在startup类的Configure方法中加上如下两个方法调用:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); }); }
名为controller的路由参数指出了控制器类,名为action的路由参数指出了要调用的方法。
下面展示如何自定义路由模板:
app.UseEndpoints(endpoints => { //配置自定义路由 endpoints.MapControllerRoute( name: "test1", pattern: "mytest/{userID?}", defaults: new { controller = "Home", action = "T2" }); //配置默认路由 endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
大多情况下,程序中只使用默认路由就够了,如果想添加自定义路由,通常是用 app.Mapxxx方法来实现的。其中的pattern参数即路由模板字符串,里面大括号部分是路由参数名,其中controller和action这两个名字表示控制器的名称和方法名称。路由参数名就相当于一个变量名,表示这里的值是可变的。
上面我配置了一条名为“test1”的路由规则后:
"……/mytest","……/mytest/199",这两个路由都将对应Home控制器的T2方法
路由模板
如:“weather/{city}/{days?}”,可以包含静态的字符(如 weather),也可以包含动态的参数(如{city}),后者被称为
路由参数
。路由参数后面加一个问号或者直接设置初始默认值,则表明该路由参数是非必须的,但这样设置的路由参数必须出现在路由模板的末尾。比如:
endpoints.MapControllerRoute( name: "test", pattern: "test/{controller=Home}/{action=Index}/{id?}" );
路由顺序
路由服务会从上往下(即配置路由规则代码的上下顺序)扫描路由表。遇到第一个匹配的路由时,就停止扫描。所以在路由表中,非常具体的路由应该出现在“更高”的位置,以便优先于更通用的路由被先匹配到。
路由约束
当想对路由参数的值进行一些约束的时候,这是个解决办法,它能让路由参数代表的值满足某种类型或满足多个条件。如果不满足就不匹配,返回404 。且框架自定义了许多约束类型,具体可网上搜索使用。
比如 “weather/{city}/{days?}”,这里当相对days 的值限制为1~7时,可以这样搞:“weather/{city}/{days?:int:range(1,7)”
特性路由
程序基本上通过配置的默认路由就够用了,当然也可以通过对控制器或方法使用特性来指定其路由规则。
通过特性指定的路由仍将进入应用程序的全局路由表,且特性路由比默认路由的优先级更高。
常用的特性路由使用示例:[Route("UserDetail")]、[Route("api/{controller}/{action}")]
注意:Route特性定义了URL模板,用于调用给定的方法。可把这个特性放到控制器类级别或方法级别。如果在这两个级别都出现了Route特性,那么两个URL将会连接起来。
以/或~/开头的URL模板指定了绝对路径。
[NonAction]
控制器中的方法,只要是public的,其名称就成为可在外部使用任何http动词(如GET、POST)请求匹配。
如果用了[NonAction]
特性修饰方法,则它就不会被http请求访问到,只能作为控制器类内部可调用的方法。
在web上,当点击链接或者在地址栏中输入URL时,是在执行HTTP GET 命令。当提交HTML表单的内容时,默认是在执行http post 命令。其他http命令只能通过发送请求给asp.net core应用程序的客户端代码执行(比如Ajax)。
环境变量:ASPNETCORE_ENVIRONMENT
无论 ASPNETCORE_ENVIRONMENT设置为Development、Staging、Production,只要项目中有appsettings.Production.json,那项目发布后运行时默认会读取Production的配置。
无论 ASPNETCORE_ENVIRONMENT设置为Development、Staging、Production,只要项目中没有appsettings.Production.json那项目发布后运行时就会仅读取appsettings.json的配置。
若想在发布后动态设置环境变量,可以通过修改web.config的方式来实现,Core项目中默认是没有web.config文件的,但是发布后会生成一个web.config文件,我们可以通过添加<environmentVariable >
节点来设置环境变量:
控制器中的方法
ps:我们接下来,将控制器中可被路由匹配到的方法叫做操作方法。
方法参数解析
首先:如果在方法有参数,那么 ASP.NET Core 将通过模型绑定器组件提供自动参数解析。
控制器操作方法能够访问http请求传递的任何输入数据。输入数据的来源有很多种,包括:
- 查询字符串
- 表单数据
- cookies
- 路由值和提交的文件
从 HttpContext.Request 对象获取输入数据:
//从查询字符串获取数据,不区分大小写 var user_name = HttpContext.Request.Query["user_name"].FirstOrDefault<string>(); //从路由参数取值,不区分大小写,RouteData.Values字典是一个string/Object字典,需要自行处理类型转换 var user_id = (int)RouteData.Values["user_id"]; //从请求头获取数据 string strToken = HttpContext.Request.Headers["token"].FirstOrDefault<string>();
模型绑定
模型绑定器按精确的顺序将参数与传入的数据匹配起来。
- 它首先检查是否能够在路由参数上找到匹配,
- 然后检查表单提交的数据,
- 最后检查查询字符串数据。
注意如果方法参数类型和实际绑定匹配到的值是兼容的,映射就会成功,如果不能转换,则会对参数赋予其类型的默认值。
强制从给定来源绑定数据:
在asp.net core中,通过强制特定的参数使用某个数据源,可以改变模型绑定数据源的固定顺序。为此可以使用下面的任意一个新特性(应用在方法参数上):FromQuery、FromRoute、FromForm、FromHeader、FromBody。这些特性分别强制模型绑定层映射查询字符串、路由数据、提交的表单数据的值、请求头、请求body。
如:public IActionResult T2([FromQuery] int userID,double xx){……}
从请求header获取数据
如:public IActionResult T2(int userID, [FromHeader(Name = "token")] string xx){……}
绑定复杂类型:
方法参数可以是一个自定义的c#类,如UserInfo类。该类中的每个public属性,模型绑定器会寻找键名称与属性名称匹配的提交值。这种匹配是不区分大小写的。
操作结果
操作方法通常返回一个实现了IActionResult的类型的实例。IActionResult用于代表操作方法来执行一些进一步的操作。所有这些操作都与为发出请求的浏览器生成一些响应有关。ASP.NET Core 提供了多种实现了 IActionResult接口的具体类型:
操作筛选器
基类Controller
提供了一对可重写的方法:OnActionExcuting
和 OnActionExectuted
。这意味着每个控制器类都提供了一个机会,用来决定再调用给定方法之前、之后做些什么,只需要重写基类的方法就能实现这种功能。
.net core 有很多种筛选器类型,比如操作筛选器,异常筛选器,等等。
编写操作筛选器时,一般需要继承ActionFilterAttribute,然后添加自己的行为。行为的可重用性,是决定是否编写操作筛选器的因素之一。
Startup类
在大部分真实的开发场景中,我们一般会将中间件及依赖服务的注册定义在一个单独的类中,按照约定,通常将这个类型命名为Startup。随着对ASP.NET Core框架认识的加深,我们会发现这种“约定优于配置”的设计广泛地应用在整个框架之中。按照约定,服务注册和中间件注册分别实现在startup类的ConfigureServices和Configure方法中,它们的第一个参数类型分别为IServiceCollection和IApplicationBuilder接口。
-
ConfigureServices 方法以配置应用的服务 。 服务是一个提供应用功能的可重用组件。 在 ConfigureServices 中注册服务,并通过依赖关系注入 (DI) 或 ApplicationServices 在整个应用中使用服务 。
服务的生命周期
singleton:所有请求都一样
scoped:对象在一个客户端请求中是相同的,但在多个不同客户端请求中是不同的。
transient:对象始终不同
服务
ASP.NET Core应用在启动以及后续针对请求的处理过程中,它会依赖各种的组件提供服务。为了便于定制,这些组件一般会以接口的形式进行“标准化”,我们将这些标准化的组件统一称为“服务(Service)”。
在依赖项注入术语中,服务:
- 通常是向其他对象提供服务的对象
- 与 Web 服务无关,尽管服务可能使用 Web 服务。
-
Configure 方法用于指定应用响应 HTTP 请求的方式。 可通过将中间件组件添加到 IApplicationBuilder 实例来配置请求管道,请求处理管道由一系列中间件组件组成,故Configure 方法用来创建应用的请求处理管道 。 每个中间件在 HttpContext 上执行异步操作,然后调用管道中的下一个中间件或终止请求。 将配置请求处理管道的代码添加到 Startup.Configure 方法中。按照惯例,通过在 Startup.Configure 方法中调用其 Use... 扩展方法,向管道添加中间件组件。 例如,要启用静态文件的呈现,请调用 UseStaticFiles。
在应用启动时,ASP.NET Core 运行时会调用 ConfigureServices 和 Configure
视图
视图引擎和视图文件
视图引擎是MVC应用程序模型的核心组件,负责从视图创建HTML。视图通常是混合在一起的HTML元素和C#代码段。
总的来说,视图文件会在服务端生成最终在浏览器呈现出来的HTML,我们可以在这个文件中直接提供原样输出的HTML标签,也可以内嵌一段动态执行的C#代码。虽然Razor引擎对View文件的编写制定了严格的语法,但是我个人觉得没有必要在Razor语法上花太多的精力,因为Razor语法的目的就是让我们很“自然”地将动态C#代码和静态HTML标签结合起来,并最终生成一份完整的HTML文档。
在 Razor (.cshtml ) 文件中,波浪号斜杠 (~/) 指向 Web 根目录。 以 ~/ 开头的路径称为虚拟路径 。
如果多个控制器调用了同一个视图,则需要将对应的视图模板文件放到Shared文件夹下。
区域(Area)不是技术属性或者功能性的;相反,他们主要与项目和代码的设计和组织有关。
可以使用ViewBag,ViewData,c#类这三种方式来向Razor视图传递数据。在ASP.NET Core中,还有第4中方式:通过@inject 指令实现的依赖注入。
通过对HTML标记应用asp-append-version="true"
这一句代码,可以是使标签在引用文件的url中追加一个字符串(?v=xxxxxx),使浏览器不缓存该文件。每当文件变化时,会生成一个新的版本字符串,使浏览器的缓存失效。
ASP.NET Core 中的捆绑和缩小静态资产:
绑定和缩减是可以在 web 应用中应用的两个不同的性能优化。 绑定和缩减一起使用,可减少服务器请求数并减小请求的静态资产的大小,从而提高性能,最佳做法是,应在生产环境中使用应用的捆绑文件和缩小文件。 在开发过程中,原始文件可简化应用程序的调试。详见:https://docs.microsoft.com/zh-cn/aspnet/core/client-side/bundling-and-minification?view=aspnetcore-3.1&tabs=visual-studio
更新于:2023.5.22
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析