介绍
提供用于定义路由的Shelf中间件。
shelf_route是一个功能强大的路由器,可以轻松地以模块化方式定义路由。
shelf_route旨在:
- 易于使用开箱即用
- 简单定制
- 高度可扩展性,使组件作者可以轻松创建新的路由api并将路由合并到其组件中。
这使它非常通用,让您可以将它与其他最喜欢的Shelf中间件组件混合搭配。
Shelf世界中有多种路由选择。 这是一个简单的指南,可以帮助您选择其中一些。
简而言之,如果你想构建自己的堆栈,那么shelf_route和shelf_rest可能会更适合你。 如果你想要一个功能更全面的框架,同时仍然具有高度可扩展性,那么mojito是更好的选择。
要更好地了解您拥有的选项,请阅读博客文章中的路由选项。
运用
基本
使用router函数创建路由器
var myRouter = router();
使用路由器的get方法使用GET Http方法添加路由
myRouter.get('/', (_) => new Response.ok("Hello World");
使用路由器的handler属性获取Shelf Handler
var handler = myRouter.handler;
现在,您可以使用Shelf IO提供路由
io.serve(handler, 'localhost', 8080);
所以完整的hello world看起来像
import 'package:shelf/shelf.dart';import 'package:shelf/shelf_io.dart' as io;import 'package:shelf_route/shelf_route.dart';void main() { var myRouter = router() ..get('/', (_) => new Response.ok("Hello World")); io.serve(myRouter.handler, 'localhost', 8080);}
Http方法
它支持所有标准的http方法
myRouter..get('/', (_) => new Response.ok("Hello World")) ..post('/', (_) => new Response.ok("Hello World")) ..put('/', (_) => new Response.ok("Hello World")) ..delete('/', (_) => new Response.ok("Hello World"));
您可以使用add指定多个方法
myRouter.add('/', ['GET', 'PUT'], (_) => new Response.ok("Hello World"));
路由参数
shelf Router使用UriPattern定义每条路线的匹配路径。 这意味着只要实现此接口,您就可以使用您喜欢的路径的任何格式。
默认情况下,它使用UriTemplate,它实现了同名的强大标准。
UriTemplate允许绑定到:
- 路径字段如/ greeting / fred
- 查询参数如/ greeting?name = fred
它使用{parameter name}表示法来表示路径参数
myRouter.get('/{name}', (request) => new Response.ok("Hello ${getPathParameter(request, 'name')}"));
路径参数通过Shelf Path的getPathParameter函数获取。
同样,您也可以绑定到查询参数
myRouter.get('/{name}{?age}', myHandler);myHandler(request) { var name = getPathParameter(request, 'name'); var age = getPathParameter(request, 'age'); return new Response.ok("Hello $name of age $age");}
层级路由器
为了提高模块性,您可以将路由分解为一系列嵌套路由。
您可以使用addAll方法添加子路由。
例如,您可以为以/ banking开头的所有路由添加子路由器
var rootRouter = router()..addAll((Router r) => r ..addAll((Router r) => r ..get('/', fetchAccountHandler) ..post('/deposit', makeDepositHandler), path: '/account/{accountNumber}'), path: '/banking');
然后通过rootRouter提供所有路由
io.serve(rootRouter.handler, 'localhost', 8080)
请注意,在这种情况下,deposit资源的完整路径实际上是
/banking/account/{accountNumber}/deposit
要试一试,请启动服务器并执行此操作
curl -d 'lots of money' http://localhost:8080/banking/account/1235/deposit
路由特定中间件
您可以将其他中间件添加到各个路由
myRouter.get('/', (_) => new Response.ok("Hello World"), middleware: logRequests());
此中间件将应用于该路由上的所有请求。
如果将其添加到子路由器,它将应用于该路由器的所有路由
var bankingRouter = rootRouter.addAll((Router r) {...}, path: '/banking', middleware: logRequests()),
将适用于所有banking路由和'/ banking'的所有子路由。
在类中分组路由
路由器的addAll方法采用类似的typedef
typedef RouteableFunction(Router router);
借助Dart函数模拟能力,这意味着您可以轻松地将一组路由组合在一起。
class MyGroup { void call(Router router) { router..get('/', (_) => new Response.ok("Hello World")) ..get('/greeting/{name}', (request) => new Response.ok("Hello ${getPathParameter(request, 'name')}")); }}
为了使它更加明确,你可以扩展Routeable类,它只是让你调用createRoutes方法而不是调用。
既然你现在有了一个类,你也可以将处理程序分解为方法class MyGroup extends Routeable { void createRoutes(Router router) { router..get('/', helloWorld) ..get('/greeting/{name}', greeting); } Response helloWorld(request) => new Response.ok("Hello World")) Response greeting(request) => new Response.ok("Hello ${getPathParameter(request, 'name')}"));}
然后将其添加到另一台路由
rootRouter.addAll(new MyGroup());
Printing路由
使用printRoutes函数很容易看到为路由定义的所有路由。
var router = r.router() ..get('/', (_) => new Response.ok("Hello World")) ..post('/', (_) => new Response.ok("Hello World")) ..get('/greeting/{name}{?age}', (request) { var name = getPathParameter(request, 'name'); var age = getPathParameter(request, 'age'); return new Response.ok("Hello $name of age $age"); }); printRoutes(router);
prints
GET -> /POST -> /GET -> /greeting/{name}{?age}
例子
查看项目源下示例文件夹中的更多详细示例。
定制
本节介绍基本的自定义。
这些是定制路由工作方式的有效方法,并且可以处理大多数自定义shelf_route的情况。
如果您需要更多,请参阅下面有关扩展的部分
自定义路径格式
所有路由器方法的路径参数都接受:
- 一个字符串或
- UriPattern
默认情况下,String值将被解析为UriParser,这意味着它应符合UriTemplate。
您也可以实现自己的UriPattern并使用它。 例如,您可能更喜欢:路径变量的样式(例如:name)。
此外,它允许您创建uri路径定义,并可能在客户端和服务器之间共享。 例如
var accountPattern = new UriParser(new UriTemplate('/account/{accountNumber}'));
您现在可以在定义路径和客户端时使用此功能。
myRouter.get(accountPattern, (_) => new Reponse.ok("Hello World"));
安装自定义路径适配器
为了更加无缝地使用您自己的路径样式,您可以在路由中安装路径适配器。 这将由此路由器中的所有路由和任何子路由器使用,除非您在某处覆盖它。
通过将适配器传递给Router函数来安装适配器。
var colonStyleAdapter = ....; // obtain the adapter from somewherevar myRouter = router(pathAdapter: colonStyleAdapter);
现在您可以使用冒号样式路径参数
myRouter.get('/:name', (request) => new Response.ok("Hello ${getPathParameter(request, 'name')}"));
自定义处理程序适配器
您可以安装自定义处理程序适配器,它允许您转换传递给路由器方法的处理程序。 这允许与其他Shelf包更加无缝集成。
例如,如果您想使用普通的Dart函数作为处理程序,您可以使用像Shelf Bind这样的包。 Shelf Bind提供开箱即用的这种适配器。
通过将适配器传递给路由函数来安装适配器。
import 'package:shelf_bind/shelf_bind.dart' as bind;var myRouter = router(handlerAdapter: bind.handlerAdapter())
现在你可以做到
myRouter.get('/{name}', (name) => "Hello ${name}");
代替
myRouter..get('/{name}', (request) => new Response.ok("Hello ${getPathParameter(request, 'name')}"));
注意,如果没有安装适配器,您仍然可以直接调用Shelf Bind的bind方法。
myRouter.get('/{name}', bind((name) => "Hello ${name}"));
注意:包含shelf_bind的最简单方法是使用shelf_rest而不是shelf_route
自定义可路由适配器
类似于HandlerAdapter如何允许您无缝集成提供替代形式的处理程序(如Shelf Bind)的程序包,RouteableAdapter允许您无缝集成支持RouteableFunction的替代表示的程序包。
RouteableFunction和RouteableAdapter定义如下
typedef RouteableFunction(Router router);typedef RouteableFunction RouteableAdapter(Function handler);
安装
您可以在创建顶级路由器时安装适配器
var myRouteableAdapter = // create some howvar myRouter = router(routeableAdapter: myRouteableAdapter)
现在你可以做到
myRouter.addAll(new MyResource(), path: 'mine');
将调用myRouteableAdapter以使MyResource的实例适应RouteableFunction
注意:与所有适配器一样,您可以在路由树的任何级别安装它们,它们将从该点开始生效。 例如
myRouter.addAll(new MyResource(), path: 'mine', routeableAdapter: myRouteableAdapter);
扩展(高级用法)
如果您无法使用上述技术实现自定义,那么您来对地方了。 但首先要了解一下shelf_route的架构。
结构
shelf_route分为两个主要部分:
- 核心路由组件,如[Route],[SimpleRoute]和[RequestRouter]。 这些是不可变组件,用于执行请求的实际路由。
- 路由器构建器组件,如[SpecBasedRouterBuilder]和[DefaultRouterBuilder]。 它们负责构建运行时组件(Route等),并且是使用shelf_route时普通用户与之交互的内容。
路由构建器
对应于运行时路由组件,是一对更抽象的模型。 这些对是路径的抽象表示,称为路径规范,以及负责从给定路径规范创建相应路径组件的适配器。 更具体地说,有:
- SimpleRouteSpec和SimpleRouteAdapter生成SimpleRoute
- 生成RequestRouter的RouterSpec和RouterAdapter
- RouteSpec和RouteAdapter,它们是与Route对应的层次结构的根
请注意这些模型是故意非常抽象的,以支持最大的灵活性。但是,在几乎所有情况下,您更有可能处理提供更具体实现的DefaultRouterAdapter等子类。提供中间件和调整路由路径的支持方式(例如支持不同的路径样式,如':foo')和处理程序(例如shelf_bind提供的允许普通Dart函数用作shelf处理程序的方式)
shelf_route的最高级扩展形式通常在此级别工作,但生成规范和适配器,其中一个或两个可能是自定义子类。
请注意,适配器从父路由继承属性。 因此,通常不必在路由树中的每个节点处提供适配器。 树顶部的单个可能就足够了。
SpecBasedRouterBuilder,也是路由器规范,具有将这些规范添加到构建器的方法,例如addRoute
目前,了解如何扩展shelf_route的最佳位置是shelf_rest的源代码。
更多信息
有关所有选项的更多详细信息,请参阅Wiki
问题
查看未解决的