Dart服務器端 shelf_rest包

介紹

提供Shelf組件,能夠輕鬆建立統一的,分層的REST資源,而且只需極少的樣板。java

shelf_restshelf_route的一個替代品。 它支持shelf_route的全部功能,增長了不少功能以減小樣板。json

基本用法

而不是導入shelf_routeapi

import 'package:shelf_route/shelf_route.dart';

你導入shelf_rest框架

import 'package:shelf_rest/shelf_rest.dart';

注意:不要同時導入二者。函數

若是您願意,能夠繼續使用它與shelf_route徹底相同,例如。fetch

import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_rest/shelf_rest.dart';

void main() {
  var myRouter = router()
    ..get('/accounts/{accountId}', (Request request) {
      var account =
          new Account.build(accountId: getPathParameter(request, 'accountId'));
      return new Response.ok(JSON.encode(account));
    });

  printRoutes(myRouter);
  
  io.serve(myRouter.handler, 'localhost', 8081);
}

class Account {
  final String accountId;

  Account.build({this.accountId});

  Account.fromJson(Map json) : this.accountId = json['accountId'];

  Map toJson() => {'accountId': accountId};
}

使用普通的Dart函數做爲處理程序ui

因爲shelf_rest自動捆綁shelf_bind,您如今能夠移除大部分鍋爐板。this

var myRouter = router()
    ..get('accounts/{accountId}',
        (String accountId) => new Account.build(accountId: accountId));

這裏,accountId路徑參數自動從請求中提取,並做爲變量傳遞給處理函數。 此外,返回的account會自動轉換爲JSONspa

有關與處理程序一塊兒使用的功能的更多詳細信息,請參閱shelf_bind的文檔。rest

將路由分組到類

您可使用addAll方法將路由分組到類中並將它們安裝在給定的子路由中。

class AccountResource {
  void createRoutes(Router r) {
    r..get('{accountId}', (String accountId) => new Account.build(accountId: accountId));
  }
}

void main() {
  var myRouter = router()..addAll(new AccountResource(), path: 'accounts');

  printRoutes(myRouter);

  io.serve(myRouter.handler, 'localhost', 8081);
}

因爲UserResource中的createRoutes方法採用類型爲Router的單個參數,所以將自動調用此方法。

使用路由註解

您可使用Get註解,而不是實現採用路由的方法(如上面的createRoutes)。

class AccountResource {
  @Get('{accountId}')
  Account find(String accountId) => new Account.build(accountId: accountId);
}

對於Router上的全部方法都存在註解,例如@ Get,@ Post,@ Put,@ Delete@AddAll,這些註解支持與相應方法徹底相同的參數。

@AddAll註解用於添加嵌套路由(子資源)。 例如

class AccountResource {
  @AddAll(path: 'deposits')
  DepositResource deposits() => new DepositResource();
}

注意:@AddAll目前僅支持方法。 可能在將來版本中支持getter

使用RestResource註解

大多數REST資源每每包含許多標準CRUD操做。

爲了進一步減小樣板並幫助實現一致性,shelf_rest對實現這些CRUD操做提供了特殊支持。

例如,銀行賬戶的RESTful資源可能具備如下類型的操做

搜索賬戶

GET /accounts?name='Freddy'

獲取一個賬戶

GET /accounts/1234

建立一個賬戶

POST /accounts

更新賬戶

PUT /accounts/1234

刪除賬戶

DELETE /accounts/1234

這是shelf_rest中的標準模式,能夠按以下方式實現

@RestResource('accountId')
class AccountResource {
  List<Account> search(String name) => .....;

  Account create(Account account) => .....;

  Account update(Account account) => .....;

  Account find(String accountId) => ...;

  void delete(String accountId) => ...;
}

@RestResource('accountId')註解用於表示支持標準CRUD操做的類,並告訴shelf_rest使用accountId做爲路徑變量。 DELETE的路由看起來像

DELETE /accounts/{accountId}

shelf_rest遵循標準命名約定以最小化配置。 這也有助於提升您對方法命名方式的一致性。

可是,您可使用ResourceMethod註解覆蓋默認命名

@ResourceMethod(operation: RestOperation.FIND)
Account fetchAccount(String accountId) => ...;

分層資源

建立分層REST資源很常見。

例如,咱們可能但願容許存款到咱們的賬戶,以下所示

PUT ->  /accounts/1234/deposits/999

您可使用上述標準@AddAll註解添加子資源。

@RestResource('accountId')
class AccountResource {

  ....

  @AddAll(path: 'deposits')
  DepositResource deposits() => new DepositResource();
}

DepositResource可能看起來像

@RestResource('depositId')
class DepositResource {

  @ResourceMethod(method: 'PUT')
  Deposit create(Deposit deposit) => ...;
}

請注意,建立操做的默認HTTP方法是POST。 當咱們在調用create時知道資源的主鍵時常用PUT

shelf_rest中,咱們經過使用ResourceMethod註解覆蓋HTTP方法來實現。

要查看此操做,咱們使用printRoutes函數

printRoutes(router);

您能夠看到建立了如下路由

GET    ->  /accounts{?name}                            => bound to search method
POST   ->  /accounts                                   => bound to create method
GET    ->  /accounts/{accountId}                       => bound to find method
PUT    ->  /accounts/{accountId}                       => bound to update method
DELETE ->  /accounts/{accountId}                       => bound to delete method
PUT    ->  /accounts/{accountId}/deposits/{depositId}  => bound to create method of DepositResource

請注意,任何不是現有路徑變量的參數都將添加到uri模板的查詢中。 因此

List<Account> search(String name) => .....;

產生

GET    ->  /accounts{?name}

中間件

您可使用ResourceMethod註解添加將包含在爲資源方法建立的路由中的中間件。

@ResourceMethod(middleware: logRequests)
Account find(String accountId) => ...;

一樣,您能夠將它們添加到GetAddAll等全部Route註解中。 例如

@AddAll(path: 'deposits', middleware: logRequests)
  DepositResource deposits() => new DepositResource();

驗證

因爲shelf_bind用於從資源方法建立Shelf處理程序,所以請求參數的驗證是免費的(由約束提供)。

有關詳細信息,請參閱shelf_bind並約束doco。

默認狀況下,驗證已關閉。 您能夠針對特定資源方法啓用驗證

@ResourceMethod(validateParameters: true)
Account find(String accountId) => ...;

您還能夠經過建立新的handlerAdapter來在路由器層次結構的任何級別打開它。 例如,您能夠按以下方式爲全部路由打開它

var router = router('/accounts', new AccountResource(),
    handlerAdapter: handlerAdapter(validateParameters: true,
        validateReturn: true);

HATEOAS支持

shelf_rest支持使用HATEOAS連接返回響應。 用於操做這些連接的模型位於hateoas_models包中,也能夠在客戶端上使用。

要使用,只需在ResourceLinksFactory類型的處理程序方法中添加一個參數。 例如

AccountResourceModel find(
  String accountId, ResourceLinksFactory linksFactory) =>
    new AccountResourceModel(
        new Account.build(accountId: accountId), linksFactory(accountId));

這裏的AccountResourceModel只是一個包含AccountHATEOAS資源連接的簡單類。

class AccountResourceModel extends ResourceModel<Account> {
  final Account account;

  AccountResourceModel(this.account, ResourceLinks links) : super(links);

  Map toJson() => super.toJson()..addAll({'account': account.toJson()});
}

查找操做的典型響應以下所示

{
    "account": {
        "accountId": "123",
        "name": 'fred'
    },
    "links": [
        {
            "href": "123",
            "rel": "self"
        },
        {
            "href": "123",
            "rel": "update"
        },
        {
            "href": "123/deposits/{?deposit}",
            "rel": "deposits.create"
        }
    ]
}

混合和匹配

指定路由的全部不一樣形式能夠一塊兒使用。 一種常見的方法是將@RestResource方法與@Get,@ Post,@ Put,@ Delete註解一塊兒用於標準CRUD操做以及不適合標準模型的操做。

使用將Router做爲其惟一參數(稱爲RouteableFunctions)的方法提供了更流暢的替代方案。 特別適用於像mojito這樣的框架,例如,使用流暢的api擴展路由器以建立oauth路由。

約定

shelf_rest默認使用如下約定。 每一個均可以用註解覆蓋。

  • create ... POST

TODO:更多doco

相關文章
相關標籤/搜索