Dart服務器端 shelf_route包

介紹

提供用於定義路由的Shelf中間件。java

shelf_route是一個功能強大的路由器,能夠輕鬆地以模塊化方式定義路由。api

shelf_route旨在:服務器

  • 易於使用開箱即用
  • 簡單定製
  • 高度可擴展性,使組件做者能夠輕鬆建立新的路由api並將路由合併到其組件中。

這使它很是通用,讓您能夠將它與其餘最喜歡的Shelf中間件組件混合搭配。架構

Shelf世界中有多種路由選擇。 這是一個簡單的指南,能夠幫助您選擇其中一些。框架

簡而言之,若是你想構建本身的堆棧,那麼shelf_route和shelf_rest可能會更適合你。 若是你想要一個功能更全面的框架,同時仍然具備高度可擴展性,那麼mojito是更好的選擇。curl

要更好地瞭解您擁有的選項,請閱讀博客文章中的路由選項。模塊化

運用

基本

使用router函數建立路由器函數

var myRouter = router();

使用路由器的get方法使用GET Http方法添加路由post

myRouter.get('/', (_) => new Response.ok("Hello World");

使用路由器的handler屬性獲取Shelf Handlerfetch

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 Bindbind方法。

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

問題

查看未解決的問題

相關文章
相關標籤/搜索