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

image-20210122135122357

无论 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 >节点来设置环境变量:

image-20210122134300731

控制器中的方法

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>();

模型绑定

模型绑定器按精确的顺序将参数与传入的数据匹配起来。

  1. 它首先检查是否能够在路由参数上找到匹配,
  2. 然后检查表单提交的数据,
  3. 最后检查查询字符串数据。

注意如果方法参数类型和实际绑定匹配到的值是兼容的,映射就会成功,如果不能转换,则会对参数赋予其类型的默认值。

强制从给定来源绑定数据:

在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接口的具体类型:

image-20210424154314264

image-20210424154242749

操作筛选器

基类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

posted @ 2023-05-22 09:39  AI大胜  阅读(485)  评论(0编辑  收藏  举报