提供Shelf組件,能夠輕鬆建立統一的,分層的REST資源,而且只需極少的樣板。java
shelf_rest是shelf_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會自動轉換爲JSON。spa
有關與處理程序一塊兒使用的功能的更多詳細信息,請參閱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
大多數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) => ...;
一樣,您能夠將它們添加到Get和AddAll等全部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);
shelf_rest支持使用HATEOAS連接返回響應。 用於操做這些連接的模型位於hateoas_models包中,也能夠在客戶端上使用。
要使用,只需在ResourceLinksFactory類型的處理程序方法中添加一個參數。 例如
AccountResourceModel find( String accountId, ResourceLinksFactory linksFactory) => new AccountResourceModel( new Account.build(accountId: accountId), linksFactory(accountId));
這裏的AccountResourceModel只是一個包含Account和HATEOAS資源連接的簡單類。
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默認使用如下約定。 每一個均可以用註解覆蓋。
TODO:更多doco