Dart服务器端 shelf_route包 原

介绍

提供用于定义路由的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 somewhere

var 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 how
var 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分为两个主要部分:

  1. 核心路由组件,如[Route],[SimpleRoute]和[RequestRouter]。 这些是不可变组件,用于执行请求的实际路由。
  2. 路由器构建器组件,如[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

问题

查看未解决的问题

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏web前端教室

你可以从面试中学到什么?

讲一下我对面试的一些。。。“偏见”,哈哈,熟悉我的同学们一定要批判的读接下来的内容哈。

12300
来自专栏前端桃园

知识体系解决迷茫的你

最近在星球里群里都有小伙伴说道自己对未来的路比较迷茫,一旦闲下来就不知道自己改干啥,今天我这篇文章就是让你觉得一天给你 25 个小时你都不够用,觉得睡觉都是浪费...

22040
来自专栏非著名程序员

「我真的没有改需求」

12010
来自专栏腾讯大讲堂的专栏

白底黑字or黑底白字,眼睛更喜欢哪一个?

12310
来自专栏非著名程序员

这是对付产品经理的一副毒药,程序员慎入

程序员和产品经理的日常就像是一对天生的冤家,为了需求的实现,几乎天天在争吵。这不,就在昨天各大技术和产品群里一个程序员暴打产品经理的视频火了,被广泛传播。

12520
来自专栏华章科技

穿越十年后看互联网+:家电行业的金矿在哪里?

现在市场上炒得火热的智能家居未来出路在何方?做智能家居的创业者应该注意哪些机会?传统家电厂商又到底如何借助互联网进行转型?本文以智能空调为例,用故事的形式,提前...

8410
来自专栏haifeiWu与他朋友们的专栏

复杂业务下向Mysql导入30万条数据代码优化的踩坑记录

从毕业到现在第一次接触到超过30万条数据导入MySQL的场景(有点low),就是在顺丰公司接入我司EMM产品时需要将AD中的员工数据导入MySQL中,因此楼主负...

29840
来自专栏腾讯NEXT学位

今天我就说三句话

11620
来自专栏FSociety

SQL中GROUP BY用法示例

GROUP BY我们可以先从字面上来理解,GROUP表示分组,BY后面写字段名,就表示根据哪个字段进行分组,如果有用Excel比较多的话,GROUP BY比较类...

5.2K20
来自专栏Ken的杂谈

【系统设置】CentOS 修改机器名

18230

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励