ASP.NET Core中的路由

传统路由

app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");

MapControllerRoute用于创建单个路由。 单个路由命名为 default 路由 。大多数具有控制器和视图的应用都使用类似 default 路由的路由模板。

之所以称为传统路由,是因为它为 URL 路径建立了一个约定:

使用默认路由进行传统路由可以创建应用,而无需为每个操作提出新的 URL 模式。 对于具有 CRUD 样式操作的应用,跨控制器的 URL 保持一致:

  • 第一个路径段 {controller=Home} 映射到控制器名称。
  • 第二段 {action=Index} 映射到操作名称。
  • 第三段 {id?} 用于可选 id。 {id?} 中的 ? 使其成为可选。 id 用于映射到模型实体。
  • 此映射:

  • 仅基于控制器和操作名称。
  • 不基于命名空间、源文件位置或方法参数。
  • 有助于简化代码。
  • 使 UI 更具可预测性。

路由模板 "{controller=Home}/{action=Index}/{id?}"

匹配URL路径,例如 /Products/Details/5。在请求的URL中,如果不提供控制器名称,则使用默认的Home页面,操作默认为Index.例如 http://127.0.0.1:10050/,http://127.0.0.1:10050/Home,http://127.0.0.1:10050/Home/Index 这三个请求返回的都是同一个页面。

简便方法:app.MapDefaultControllerRoute();可替换上一个设置路由方案。

 

大多数应用应选择基本的描述性路由方案,让 URL 有可读性和意义。 默认传统路由 {controller=Home}/{action=Index}/{id?}

  • 支持基本的描述性路由方案。
  • 是基于 UI 的应用的有用起点。
  • 是许多 Web UI 应用所需的唯一路由模板。 对于较大的 Web UI 应用,通常只需要使用区域的另一个路由。

传统路由顺序

常规路由仅匹配应用定义的操作和控制器的组合。 这是为了简化传统路由重叠的情况。 使用 MapControllerRoute、MapDefaultControllerRoute 和 MapAreaControllerRoute 添加路由会根据调用它们的顺序自动为其终结点分配一个顺序值。 来自较早出现的路由的匹配具有更高的优先级。 传统路由依赖于顺序。 一般情况下,具有区域的路由应放在路由表中靠前的位置,因为它们比没有区域的路由更具体。 具有 catch-all 路由参数的专用传统路由(例如 {*article})会使某个路由变得太贪婪,也就是说,它会匹配你想要使用其他路由来匹配的 URL。 将贪婪路由放在路由表中靠后的位置可解决此问题。

属性路由

API 的属性路由REST

REST API 应使用属性路由将应用的功能建模为一组资源,其中操作由 HTTP 谓词表示。

属性路由使用一组属性将操作直接映射到路由模板。如下示例:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("Home/Index")]
    [Route("Home/Index/{id?}")]
    public IActionResult Index(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [Route("Home/About")]
    [Route("Home/About/{id?}")]
    public IActionResult About(int? id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

这里可以看到属性路由需要更多输入才能指定路由。 传统默认路由会更简洁地处理路由。 但是,属性路由允许并需要精确控制应用于每项操作的路由模板。

这里我们注意到控制器和操作名称在操作匹配中不起作用,除非使用标记替换。如下示例:

public class HomeController : Controller
{
    [Route("")]
    [Route("Home")]
    [Route("[controller]/[action]")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [Route("[controller]/[action]")]
    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

也可直接将标记 [Route("[controller]/[action]")] 应用于控制器:

[Route("[controller]/[action]")]
public class HomeController : Controller
{
    [Route("~/")]
    [Route("/Home")]
    [Route("~/Home/Index")]
    public IActionResult Index()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public IActionResult About()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

在前面的代码中,Index 方法模板必须将 / 或 ~/ 预置到路由模板。 应用于操作的以 / 或 ~/ 开头的路由模板不与应用于控制器的路由模板合并。

保留的路由名称

以下关键字是使用控制器或 Razor Pages 时保留的路由参数名称:

  • action
  • area
  • controller
  • handler
  • page
  • 在 Razor 视图或 Razor 页面的上下文中保留以下关键字:

  • page

  • using

  • namespace

  • inject

  • section

  • inherits

  • model

  • addTagHelper

  • removeTagHelper

不应将这些关键字用于链接生成、模型绑定参数或上层属性。

HTTP 谓词模板

ASP.NET Core 具有以下 HTTP 谓词模板:

  • [HttpGet]
  • [HttpPost]
  • [HttpPut]
  • [HttpDelete]
  • [HttpHead]
  • [HttpPatch]

路由模板

ASP.NET Core 具有以下路由模板:

  • 所有HTTP谓词模板都是路由模板。
  • [Route]

使用 Http 谓词属性的属性路由

[Route("api/[controller]")]
[ApiController]
public class Test2Controller : ControllerBase
{
    [HttpGet]   // GET /api/test2
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]   // GET /api/test2/xyz
    public IActionResult GetProduct(string id)
    {
       return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int/{id:int}")] // GET /api/test2/int/3
    public IActionResult GetIntProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }

    [HttpGet("int2/{id}")]  // GET /api/test2/int2/3
    public IActionResult GetInt2Product(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

REST API 应使用属性路由将应用的功能建模为一组资源,其中操作由 HTTP 谓词表示。 也就是说,对同一逻辑资源执行的许多操作(例如,GET 和 POST)都使用相同 URL。 属性路由提供了精心设计 API 的公共终结点布局所需的控制级别。

组合属性路由

若要使属性路由减少重复,可将控制器上的路由属性与各个操作上的路由属性合并。 控制器上定义的所有路由模板均作为操作上路由模板的前缀。 在控制器上放置路由属性会使控制器中的所有操作都使用属性路由。

[ApiController]
[Route("products")]
public class ProductsApiController : ControllerBase
{
    [HttpGet]
    public IActionResult ListProducts()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    [HttpGet("{id}")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

 

属性路由顺序

路由构建树并同时匹配所有终结点:

  • 路由条目的行为就像以理想的顺序排列一样。
  • 最具体的路由有机会在更通用的路由之前执行。

例如,blog/search/{topic} 之类的属性路由比 blog/{*article} 之类的属性路由更具体。 默认情况下,blog/search/{topic} 路由具有更高的优先级,因为它更具体。 使用传统路由时,开发人员负责按所需顺序放置路由。

属性路由可以使用 Order 属性配置顺序。 框架提供的所有路由属性都包括 Order。 路由按 Order 属性的升序进行处理。 默认顺序为 0。 使用 Order = -1 设置的路由在未设置顺序的路由之前运行。 使用 Order = 1 设置的路由在默认路由顺序之后运行。

路由模板 [controller]、[action]、[area] 中的标记替换

为方便起见,属性路由支持标记替换,方法是将标记用方括号([])括起来。 标记 [action][area] 和 [controller] 替换为定义了路由的操作中的操作名称值、区域名称值和控制器名称值。

[Route("[controller]/[action]")]
public class Products0Controller : Controller
{
    [HttpGet]  //匹配Products0/List
    public IActionResult List()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }


    [HttpGet("{id}")]//匹配 /Products0/Edit/{id}
    public IActionResult Edit(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

多个属性路由

属性路由支持定义多个访问同一操作的路由。 此操作最常用于模拟默认传统路由的行为,如以下示例所示:

[Route("Store")]
[Route("[controller]")]
public class Products6Controller : Controller
{
    [HttpPost("Buy")]       // Matches 'Products6/Buy' and 'Store/Buy'
    [HttpPost("Checkout")]  // Matches 'Products6/Checkout' and 'Store/Checkout'
    public IActionResult Buy()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

指定属性路由的可选参数、默认值和约束

属性路由支持使用与传统路由相同的内联语法,来指定可选参数、默认值和约束。

public class Products14Controller : Controller
{
    [HttpPost("product14/{id:int}")]
    public IActionResult ShowProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

在前面的代码中,[HttpPost("product14/{id:int}")] 应用路由约束。 Products14Controller.ShowProduct 操作仅与 /product14/3 等 URL 路径匹配。 路由模板部分 {id:int} 将该段限制为仅整数。

URL 生成和环境值

应用可以使用路由 URL 生成功能来生成指向操作的 URL 链接。 生成 URL 可消除硬编码URL,使代码更稳定、更易维护。 本部分重点介绍 MVC 提供的 URL 生成功能,并且仅涵盖 URL 生成工作原理的基础知识。

IUrlHelper 接口用于生成 URL,是 MVC 与路由之间的基础结构的基础部分。 在控制器、视图和视图组件中,可通过 Url 属性获得 IUrlHelper 的实例。

在以下示例中,将通过 Controller.Url 属性使用 IUrlHelper 接口来生成指向另一项操作的 URL。

public class UrlGenerationController : Controller
{
    public IActionResult Source()
    {
        // Generates /UrlGeneration/Destination
        var url = Url.Action("Destination");
        return ControllerContext.MyDisplayRouteInfo("", $" URL = {url}");
    }

    public IActionResult Destination()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }
}

这里MyDisplayRouteInfo由 Rick.Docs.Samples.RouteInfo NuGet 包提供,会显示路由信息。

如果应用使用的是传统默认路由,则 url 变量的值将为 URL 路径字符串 /UrlGeneration/Destination。 此 URL 路径由路由通过组合以下内容创建:

  • 当前请求的路由值,称为环境值。
  • 传递到 Url.Action 的值,并将这些值替换到路由模板中:

ambient values: { controller = "UrlGeneration", action = "Source" } values passed to Url.Action: { controller = "UrlGeneration", action = "Destination" } route template: {controller}/{action}/{id?} result: /UrlGeneration/Destination

路由模板中的每个路由参数都会通过将名称与这些值和环境值匹配,来替换掉原来的值。 没有值的路由参数可以:

  • 使用默认值(如果有)。
  • 如果可选,则跳过。 例如,路由模板 {controller}/{action}/{id?} 中的 id

Areas

区域是一项 MVC 功能,用于将相关功能作为一个单独的组组织到一个组中:

  • 控制器操作的路由命名空间。
  • 视图的文件夹结构。

通过使用区域,应用可以有多个名称相同的控制器,只要它们具有不同的区域。 通过向 controller 和 action 添加另一个路由参数 area,可使用区域为路由创建层次结构。 本部分讨论路由如何与区域交互。

app.MapAreaControllerRoute("blog_route", "Blog",
        "Manage/{controller}/{action}/{id?}");
app.MapControllerRoute("default_route", "{controller}/{action}/{id?}");

传统路由依赖于顺序。 一般情况下,具有区域的路由应放在路由表中靠前的位置,因为它们比没有区域的路由更具体。

在前面的示例中,路由值 { area = Blog, controller = Users, action = AddUser } 匹配以下操作:

using Microsoft.AspNetCore.Mvc;

namespace MyApp.Namespace1
{
    [Area("Blog")]
    public class UsersController : Controller
    {
        // GET /manage/users/adduser
        public IActionResult AddUser()
        {
            var area = ControllerContext.ActionDescriptor.RouteValues["area"];
            var actionName = ControllerContext.ActionDescriptor.ActionName;
            var controllerName = ControllerContext.ActionDescriptor.ControllerName;

            return Content($"area name:{area}" +
                $" controller:{controllerName}  action name: {actionName}");
        }        
    }
}

[Area] 属性将控制器表示为区域的一部分。 此控制器位于 Blog 区域。 没有属性的[Area]控制器不是任何区域的成员,并且当路由提供路由值时area不匹配

你可能感兴趣的:(MVC,c#,.net)