在Nancy中,最为神奇的莫过于路由了,定义路由模块是构成Nancy应用的骨架。在Nancy中定义路由,和在 ASP.NET MVC那些类似的框架中有着非常大的区别。
以 ASP.NET MVC 为例,通常情况需要创建一个控制类。多数情况下,这个类提供了路由的约定。通过定义您的控制器类名和该类中的方法的名称,就能定义了该代码所处理的“路由”
请看下面的例子:
using System; using System.Linq; using System.Web.Mvc; namespace Intranet.WebUi.Controllers { public class HomeController : Controller { public ActionResult Index() { return View(); } } }
这段从标准ASP.NET MVC 应用程序摘录的代码定义了Home路由下的Index节点,用于将Http请求路由到这段代码 ,请求地址应该是这样的 /home/index 。 如果使用Nancy,就会有些不一样了。
首先,在Nancy应用中,继承NancyModule基类,才能定义路由。第二,不是每个路由都需要定义在一个单独的类中(如 MVC示例),可以在模块类的构造构造函数中定义路由,使用Rest动词定义路由类型。
当然这可能导致过于庞大的构造函数,但是也有很多方式处理这个问题。
在往下看之前,我们需要先了解一些概念......
如果您习惯于看到从 web 浏览器的触发的web 请求,你可能不知道,在它之下是相当复杂的协议。如果你从事于 web相关的开发工作,很可能听说过称为 HTTP 协议。HTTP通过使用一系列的动词来表示客户端希望服务器采取某些行动。
采用Nancy,也无需了解太多HTTP动词的细节。实际上创建一个REST 风格的应用,用到下面的Rest动词就好:
- GET
- POST
- PUT
- DELETE
GET,顾名思义,用来检索数据从服务;同样,删除是习惯请求数据被删除。PUT和POST可能经常会造成混乱。遵循规范, PUT意思是“整体替换指定的”,而POST用于“在现有基础上添加”。许多开发人员创建基于 rest 风格的应用程序,也不过就是使用GET和POST。
这方面Nancy走的更进一步,不同于大多数其他 web 为基础的框架,你可以定义自己的动词。在某种意义上,允许您创建您自己的基于HTTP的特定领域的语言。让我们深入剖析的Nancy路由模块,看看是如何工作的。
我们的第一个Nancy路由模块
看下面的代码
using Nancy; namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() { Get[@"/"] = _ => Response.AsFile("index.html", "text/html"); } } }
作为一个Nancy项目,这是例子再简单不过了。
这个只有十一行的代码,监听所有向应用程序的"/"根路径的请求,返回"Content"文件一个名为“index.html”HTTP页面。
Content 文件夹是Nancy查询文件的默认文件夹,在下一张关于视图Views的章节,我们会涉及更多,但现在,只是确保项目有一个文件夹被称为Content,所有的 HTML 文件都放置于此。
撇开通常命名空间和类的代码部分 ,只有两点是这个Nancy路由模块添加的:继承了NancyModule ,在构造函数中使用GET规则。
GET规则意味着这将响应使用 GET 动词的 HTTP 调用,代码将执行响应根路径的 GET 请求。
如何你要扩展这个模块处理 GET,POST,PUT,和DELETE,你需要这么处理:
using Nancy; namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() { Get[@"/"] = _ => Response.AsFile("index.html", "text/html"); Put[@"/"] = _ => Response.AsFile("index.html", "text/html"); Post[@"/"] = _ => Response.AsFile("index.html", "text/html"); Delete[@"/"] = _ => Response.AsFile("index.html", "text/html"); } } }
模块监听的路径或路由需要放置到动词后面的方括号中。所以,举个例子,你可以像下面是的修改:
using Nancy; namespace nancybook.modules25 { public class BaseRoutes : NancyModule { public BaseRoutes() { Get[@"/allpeople"] = _ => Response.AsFile("index.html", "text/html"); Put[@"/allpeople"] = _ => Response.AsFile("index.html", "text/html"); Post[@"/newperson"] = _ => Response.AsFile("index.html", "text/html"); Delete[@"/singleperson"] = _ => Response.AsFile("index.html", "text/html"); } } }
为了演示的目的,每个请求都返回一个静态网页。在真是环境中,每个实际的请求你可能都需要根据实际的功能需求编写合适的处理代码。本章接下来对这个话题进行更多的介绍。
你也可以为你的模块编写通用的路径。试想下面的例子:
using Nancy; namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() { Get[@"/single"] = _ => Response.AsFile("index.html", "text/html"); Put[@"/single"] = _ => Response.AsFile("index.html", "text/html"); Post[@"/new"] = _ => Response.AsFile("index.html", "text/html"); Delete[@"/single"] = _ => Response.AsFile("index.html", "text/html"); } } }
很快你就会发现代码变得难以阅读。 另外,如果你想用一致的命名方法来编写你的API,重用single 和new 来处理应用程序中的其他资源。
再一次,Nancy展现了其简单的处理方式,仅仅通过在模块类中重载父类NancyModule的构造函数并传递根目录路径。
namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() : base("/people") { Get[@"/single"] = _ => Response.AsFile("index.html", "text/html"); Put[@"/single"] = _ => Response.AsFile("index.html", "text/html"); Post[@"/new"] = _ => Response.AsFile("index.html", "text/html"); Delete[@"/single"] = _ => Response.AsFile("index.html", "text/html"); } } }
通过添加父类构造函数的调用,瞬间已经更改了您的模块将响应的所有 Url:
- GET /single
- PUT /single
- POST /new
- DELETE /single
转变为:
- GET /people/single
- PUT /people/single
- POST /people/new
- DELETE /people/single
加上一点创造性思维和一些聪明的软件开发点子,你甚至可以在很多地方重用同一个控制器。
在我们继续之前,还是要提一个关于“Rest动词”的警告。一些托管环境(包括ASP.NET/IIS)默认是不支持一些常用的动词。
在前面的示例中,如果运行在 IIS 中,你会发现PUT和DELETE导致服务器返回以下错误: 503 方法不被允许。
这并不是Nancy的问题,而是,IIS 只允许两个最常见动词(GET 和 POST)。如果你参考下Stack Overflow的文章,这个问题不难解决。
路由参数
正如其他的web工具包,您的路由模块也可以接收各种各样的数据。这些数据包括简单的 URL 参数,如记录的 ID 或博客分类号,也有通过POST或PUT传递的复杂对象。
在这一章,我们会讲解基于路由的简单数据,以后的章节再介绍复杂的对象。
你已经看到了,为响应给定的请求,在Nancy路由模块中修改URL非常简单。但如果需要在URL中传递记录ID该怎么办?
using Nancy; namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() : base("/people") { Get[@"/{id}"] = parameters => { var myRecordId = parameters.id; return Response.AsFile("Pages/index.html", "text/html"); }; } } }
如果你想限定参数的类型,请看下面示例:
using Nancy; namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() : base("/people") { Get[@"/{id:int}"] = parameters => { var myRecordId = parameters.id; return Response.AsFile("Pages/index.html", "text/html"); }; } } }
注意在路由参数上增加了int限制;任何尝试传递非整数参数都会导致404未找到的错误。Nancy定义许多这些的限制,基本上都绑在.NET 基元类型:
- long
- decimal
- guid
- bool
- datetime
你可以在URL中添加很多的参数,如下:
using Nancy; namespace nancybook.modules { public class BaseRoutes : NancyModule { public BaseRoutes() : base("/people") { Get[@"/person/{age:int}/{surname}/categories/{category}/{city}"] = parameters => { return Response.AsFile("Pages/index.html", "text/html"); }; } } }
总结
在本章中,您已经学到了如何使用路由模块创建应用程序的骨架,了解到了Nancy与ASP.NET MVC在路由上定义的不同。你已经看到了如何定义路由路径,不再通过类的名字控制路由定义,了解了如何使用动词和在URL中传递参数。在下一章中,我们会看看视图引擎,看看如何返回一些其他东西,不只是普通的 HTML 文档。