asp.net core 系列 5 路由(上)
一. 概述
介绍asp.net core路由时,我初步想了下,分几篇来说明。 路由的知识点很多,参考了官方文档提取出一些重要的知识点来说。 在asp.net core中是使用路由中间件来匹配传入请求的 url 并将它们映射到操作(action方法)。路由是在程序启动时进行传统路由或属性路由定义。 路由描述如何将 url 路径与操作相匹配。 它还用于在响应中生成送出的 url(用于链接)。
路由操作既支持传统路由,也支持属性路由。也可混合使用。通常传统路由用于为浏览器处理 html 页面的控制器。属性路由用于处理 web api 的控制器。
1.1设置路由中间件
要使用传统路由,必须在usemvc中间件中配置实现iroutebuilder接口,在asp.net core mvc 2.2 框架下,应用程序startup的configure 方法中,默认路由设置如下:
app.usemvc(routes => { routes.maproute( name: "default", template: "{controller=home}/{action=index}/{id?}"); });
在对 usemvc调用中,maproute 用于创建单个路由,亦称 default 路由。 大多数 mvc 应用使用带有模板的路由。对于default路由简便的方法可以使用:
app.usemvcwithdefaultroute();
usemvc 和 usemvcwithdefaultroute 可向中间件管道添加 routermiddleware 的实例。 mvc 不直接与中间件交互,而是使用路由来处理请求。 mvc 通过 mvcroutehandler 实例连接到路由。
usemvc 不直接定义任何路由,它向属性路由的路由集合添加占位符{controller=home}/{action=index}/{id?} 。通过重载 usemvc(action<iroutebuilder>) 则允许用户添加自己的路由,并且还支持属性路由。
1.2 传统路由
传统路由是:具有描述性的路由方案,这样url具有可读性。传统路由格式:{controller=home}/{action=index}/{id?}这样的url路径是设定了一个约定: 第一段映射到控制器名称, 第二段映射到操作名称,第二段映射到可选id。
(1) 使用默认路由:
routes.maproute("default", "{controller=home}/{action=index}/{id?}");
使用此默认路由时: url路径/products/list 将映射到程序productscontroller(控制器).list(action)中。 url路径/blog/article/17将映射到程序blogcontroller(控制器).article(action)中。
(2) 多个路由:
通过添加对 maproute 的多次调用,可以在 usemvc 内添加多个路由。 这样做可以定义多个约定,或添加专用于特定操作的传统路由,比如:
app.usemvc(routes => { routes.maproute("blog", "blog/{*article}", defaults: new { controller = "blog", action = "article" }); routes.maproute("default", "{controller=home}/{action=index}/{id?}"); });
这里的blog路由是一个专用的传统路由,这表示blog使用传统路由系统,但专用于特定的操作,也就是对于blogcontroller控制器的article操作,此专用路由将始终映射。对于多个路由的路由集合会进行排序,并按添加顺序进行处理,因此,在此示例中,将先尝试 blog 路由,再尝试 default 路由。
(3) action操作的区分
在处理url请求时,当通过路由匹配到一个控制器内两项相同的action名称时,mvc必须进行区分,以选择最佳候选项,否则会引发异常(ambiguousactionexception)。
public class productscontroller : controller { public iactionresult edit(int id) { ... } [httppost] public iactionresult edit(int id, product product) { ... } }
此products控制器定义了二项操作,这两项操作均与 url 路径的 /products/edit/17 匹配相同。解决方案是将要提交的action加上 http 谓词为 post。这样post过来时,就会选择edit(int, product)
1.3 属性路由
通过在控制器(controller)或操作(action)上放置路由可实现属性路由。 不能通过传统路由访问定义属性路由的操作,反之亦然。 控制器上的任何路由属性,都会使控制器中的所有操作使用属性路由。
属性路由使用一组属性将action直接映射到路由模板。在下面的示例中,configure 方法使用 app.usemvc();,不传递任何路由。 homecontroller 将匹配一组 url,这组 url 与默认路由 {controller=home}/{action=index}/{id?} 匹配的 url 类似:
当去掉default默认路由模板后,只使用app.usemvc()时。运行程序时,页面报404错误:找不到 localhost 的网页。
app.usemvc();
(1) 属性路由基本使用
如果定义了属性路由的操作,此时就是启动属性路由功能。home控制器的属性路由示例如下:
public class homecontroller : controller { [route("")] [route("home")] [route("home/index")] public iactionresult index() { return view(); } }
在index的action上加[route("")]属性路由。 浏览器可以使用下面三种url来访问,也是程序启动时的默认加载页面:
http://localhost:30081/
http://localhost:30081/home/
http://localhost:30081/home/index
(2) 属性路由精确控制
属性路由需要更多输入来指定路由;传统的默认路由处理路由的方式则更简洁。 但是,属性路由允许(并需要)精确控制应用于每项操作的路由模板。下面示例是精确控制每项操作的路由模板,比如url访问/home/index时,即是调用myindex的action方法。
public class mydemocontroller : controller { [route("")] [route("home")] [route("home/index")] public iactionresult myindex() { return view("index"); } }
1.4 使用 http[verb] 属性的属性路由
属性路由还可以使用 http[verb]
属性,比如 httppostattribute
。 所有这些属性都可采用路由模板。 此示例展示,同一路由模板匹配的两项操作:
[httpget("/products")] public iactionresult listproducts() { // ... } [httppost("/products")] public iactionresult createproduct(...) { // ... }
当 http 谓词为 get 时将执行productsapi.listproducts 操作, 当 http 谓词为 post 时将执行 productsapi.createproduct。生成 rest api 时,很少会在操作方法上使用 [route(...)]。 建议使用更特定的 http*verb*attributes 来明确 api 所支持的操作。 rest api 的客户端需要知道映射到特定逻辑操作的路径和 http 谓词。
例如下面一个web api访问路由,使用http*verb*attributes 来明确定义如下:
public class productsapicontroller : controller { [httpget("/products/{id}", name = "products_list")] public iactionresult getproduct(int id) { ... } }
上面定义只有针对如访问url如: /products/3(而非 /products)之类的 url才会执行 productsapi.getproduct(int) 操作。
1.5 路由合并
若要使属性路由减少重复,可将控制器controller上的路由属性与各个操作action上的路由属性合并。 控制器上定义的所有路由模板均作为操作上路由模板的前缀。 在控制器上放置路由属性会使控制器中的所有操作都使用属性路由。
下面是一个web api的路由合并,访问get的方法的访问路径为: http://localhost:30081/api/products/1
[route("api/products")] public class productsapicontroller : controller { // get api/values/5 [httpget("{id}")] public string get(int id) { return "value"; } }
下面是一个控制器的路由合并。访问index页面的访问路径为: http://localhost:30081/home/index
[route("home")] public class homecontroller : controller { [route("")] // combines to define the route template "home" [route("index")] // combines to define the route template "home/index" [route("/")] // doesn't combine, defines the route template "" public iactionresult index() { //... } }
1.6 指定属性路由参数约束
[httpget("home/{id:int}",name = "pri")] public iactionresult privacy(int id) { return view(); }
如果输入非整数类型的参数,浏览器提示:找不到与以下网址对应的网页:http://localhost:30081/home/dd
1.7 自定义路由属性
该框架中提供的所有路由属性([route(...)]、[httpget(...)] 等)都可实现 iroutetemplateprovider接口。 当应用启动时,mvc 会查找控制器类和操作方法上的属性,并使用可实现 iroutetemplateprovider的属性生成一组初始路由。
下面使用iroutetemplateprovider
来定义自己的路由属性。每个 iroutetemplateprovider
都允许定义一个包含自定义路由模板、顺序和名称的路由:
public class myapicontrollerattribute : attribute, iroutetemplateprovider { //实现接口的三个属性,这里的[controller]是一个标记替换。 public string template => "api/[controller]/{action}/{id?}"; public int? order { get; set; } public string name { get; set; } } public class productsapicontroller : controller { // get api/values/5 // [httpget("{id}")] [myapicontroller()] public string get(int id) { return "value"; } }
通过访问url: http://localhost:30081/api/productsapi/get/1 来调用get方法。
参考文献
官方资料:
上一篇: 设计模式之雇工模式
下一篇: 调用python已有库的代码
推荐阅读
-
asp.net core系列 24 EF模型配置(主键,生成值,最大长度,并发标记)
-
ASP.NET Core 2.2 WebApi 系列【六】泛型仓储模式
-
ASP.NET Core 2.2 WebApi 系列【三】AutoFac 仓储接口的依赖注入
-
ASP.NET Core 2.2 WebApi 系列【四】集成Swagger
-
NET Framework项目移植到NET Core上遇到的一系列坑(2)
-
asp.net core系列 66 Dapper介绍--Micro-ORM
-
asp.net core系列 68 Filter管道过滤器
-
Asp.Net Core2.2 源码阅读系列——控制台日志源码解析
-
ASP.NET Core 2.2 系列【三】集成Swagger
-
asp.net core系列 76 Apollo 快速安装模式下填坑和ASP.NetCore结合使用