Designing-a-Beautiful-REST+JSON-API

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. Beautiful REST+JSON APIs Les  Hazlewood  @lhazlewood   Apache  Shiro  Project  Chair   Expert Group Member, JEE Application Security   CTO,  Stormpath,  stormpath.com  
2. .com •  User Management API for Developers •  Password security •  Authentication and Authorization •  LDAP & Active Directory Cloud Sync •  Instant-on, scalable, and highly available •  Free for developers @lhazlewood  
3. Outline •  APIs, REST & JSON •  REST Fundamentals •  Design Base  URL   Versioning   Resource  Format   Return  Values   Content  NegoEaEon   References  (Linking)   PaginaEon   Query  Parameters   AssociaEons   @lhazlewood   Errors   IDs   Method  Overloading   Resource  Expansion   ParEal  Responses   Caching  &  Etags   Security   MulE  Tenancy   Maintenance   Batch  OperaEons  
4. APIs •  •  •  •  •  Applications Developers Pragmatism over Ideology Adoption Scale @lhazlewood  
5. Why REST? •  •  •  •  •  •  Scalability Generality Independence Latency (Caching) Security Encapsulation @lhazlewood  
6. Why JSON? •  •  •  •  •  Ubiquity Simplicity Readability Scalability Flexibility @lhazlewood  
7. HATEOAS •  •  •  •  •  •  •  Hypermedia As The Engine Of Application State @lhazlewood  
8. REST Is Easy @lhazlewood  
9. REST Is *&@#$! Hard (for providers) @lhazlewood  
10. REST can be easy (if you follow some guidelines) @lhazlewood  
11. Example Domain: Stormpath •  •  •  •  •  •  Applications Directories Accounts Groups Associations Workflows
12. Fundamentals @lhazlewood  
13. Resources Nouns, not Verbs Coarse Grained, not Fine Grained Architectural style for use-case scalability @lhazlewood  
14. What If? /getAccount /createDirectory /updateGroup /verifyAccountEmailAddress @lhazlewood  
15. What If? /getAccount /getAllAccounts /searchAccounts /createDirectory /createLdapDirectory /updateGroup /updateGroupName /findGroupsByDirectory /searchGroupsByName /verifyAccountEmailAddress /verifyAccountEmailAddressByToken … Smells like bad RPC. DON’T DO THIS. @lhazlewood  
16. Keep It Simple @lhazlewood  
17. The Answer Fundamentally two types of resources: Collection Resource Instance Resource @lhazlewood  
18. Collection Resource /applications @lhazlewood  
19. Instance Resource /applications/a1b2c3 @lhazlewood  
20. Behavior •  GET •  PUT •  POST •  DELETE •  HEAD @lhazlewood  
21. Behavior POST, GET, PUT, DELETE ≠  1:1   Create,  Read,  Update,  Delete   @lhazlewood  
22. Behavior As  you  would  expect:   GET  =  Read   DELETE  =  Delete   HEAD  =  Headers,  no  Body   @lhazlewood  
23. Behavior Not  so  obvious:     PUT  and  POST  can  both  be  used  for   Create  and  Update   @lhazlewood  
24. PUT for Create IdenEfier  is  known  by  the  client:     PUT /applications/clientSpecifiedId { … } @lhazlewood  
25. PUT for Update Full  Replacement     PUT /applications/existingId { “name”: “Best App Ever”, “description”: “Awesomeness” } @lhazlewood  
26. PUT Idempotent @lhazlewood  
27. POST as Create On  a  parent  resource     POST /applications { “name”: “Best App Ever” } Response: 201 Created Location: https://api.stormpath.com/applications/a1b2c3 @lhazlewood  
28. POST as Update On  instance  resource     POST /applications/a1b2c3 { “name”: “Best App Ever. Srsly.” } Response: 200 OK @lhazlewood  
29. POST NOT  Idempotent @lhazlewood  
30. Media Types •  Format  SpecificaEon  +  Parsing  Rules   •  Request:  Accept  header   •  Response:  Content-Type  header   •  •  •  •  application/json application/foo+json application/foo+json;application … @lhazlewood  
31. Design Time! @lhazlewood  
32. Base URL @lhazlewood  
33. http(s)://foo.io vs http://www.foo.com/dev/service/api/rest @lhazlewood  
34. http(s)://foo.io Rest Client vs Browser @lhazlewood  
35. Versioning @lhazlewood  
36. URL https://api.stormpath.com/v1 vs. Media-Type application/foo+json;application&v=2 application/foo2+json;application @lhazlewood  
37. Resource Format @lhazlewood  
38. Media Type Content-Type: application/json When time allows: application/foo+json application/foo2+json;bar=baz … @lhazlewood  
39. Media Type Don’t go overboard! Media Type != Schema! Most only need 2 or 3 custom media types: •  instance resource •  collection resource application/foo+json application/foo2+json;bar=baz … @lhazlewood  
40. camelCase ‘JS’ in ‘JSON’ = JavaScript myArray.forEach Not  myArray.for_each account.givenName Not  account.given_name Underscores for property/function names are unconventional for JS. Stay consistent. @lhazlewood  
41. Date/Time/Timestamp There’s already a standard. Use it: ISO 8601 Example: { …, “createdAt”: “2013-07-10T18:02:24.343Z”, ... } Use UTC! @lhazlewood  
42. createdAt / updatedAt @lhazlewood  
43. createdAt / updatedAt Most people will want this at some point { …, “createdAt”: “2013-07-10T18:02:24.343Z”, “updatedAt”: “2014-09-29T07:02:48.761Z” } Use UTC! @lhazlewood  
44. Response Body @lhazlewood  
45. GET obvious What about POST? Return the representation in the response when feasible. Add override (?_body=false) for control @lhazlewood  
46. Content Negotiation @lhazlewood  
47. Header •  Accept header •  Header values comma delimited •  q param determines precedence, defaults to 1, then conventionally by list order GET /applications/a1b2c3 Accept: application/json, text/ plain;q=0.8 @lhazlewood  
48. Resource Extension /applications/a1b2c3.json /applications/a1b2c3.csv … ConvenEonally  overrides  Accept  header   @lhazlewood  
49. HREF •  Distributed Hypermedia is paramount! •  Every accessible Resource has a canonical unique URL •  Replaces IDs (IDs exist, but are opaque). •  Critical for linking, as we’ll soon see @lhazlewood  
50. Instance w/ HREF (v1) GET /accounts/x7y8z9 200 OK { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, ... } @lhazlewood  
51. Resource References aka ‘Linking’ (v1) @lhazlewood  
52. •  Hypermedia is paramount. •  Linking is fundamental to scalability. •  Tricky in JSON •  XML has it (XLink), JSON doesn’t •  How do we do it? @lhazlewood  
53. Instance Reference (v1) GET /accounts/x7y8z9 200 OK { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: ???? } @lhazlewood  
54. Instance Reference (v1) GET /accounts/x7y8z9 200 OK { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “href”: “https://api.stormpath.com/v1/directories/g4h5i6” } } @lhazlewood  
55. Collection Reference (v1) GET /accounts/x7y8z9 200 OK { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”, “surname”: “Stark”, …, “groups”: { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9/groups” } } @lhazlewood  
56. Linking v2 (recommended) @lhazlewood  
57. Instance HREF (v2) GET /accounts/x7y8z9 200 OK { “meta”: { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “mediaType”: “application/ion+json”, ... }, “givenName”: “Tony”, “surname”: “Stark”, … } @lhazlewood  
58. Instance Reference (v2) GET /accounts/x7y8z9 200 OK { “meta”: { ... }, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “meta”: { “href”: “https://api.stormpath.com/v1/directories/g4h5i6” “mediaType”: “application/ion+json” } } } @lhazlewood  
59. Collection Reference (v2) GET /accounts/x7y8z9 200 OK { “meta”: { ... }, “givenName”: “Tony”, “surname”: “Stark”, …, “groups”: { “meta”: { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9/groups”, “mediaType”: “application/ion+json”, “rel”: [“collection”] } } } @lhazlewood  
60. Reference Expansion (aka Entity Expansion, Link Expansion) @lhazlewood  
61. Account and its Directory? @lhazlewood  
62. GET /accounts/x7y8z9?expand=directory 200 OK { “meta”: {...}, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “meta”: { ... }, “name”: “Avengers”, “description”: “Hollywood’s hope for more $”, “createdAt”: “2012-07-01T14:22:18.029Z”, … } } @lhazlewood  
63. Partial Representations @lhazlewood  
64. GET /accounts/x7y8z9? fields=givenName,surname,directory(name) @lhazlewood  
65. Collections! @lhazlewood  
66. Collections •  •  •  •  A first class resource ‘citizen’ Own href / metadata Own properties Different from all other collections @lhazlewood  
67. GET /accounts/x7y8z9/groups 200 OK { “meta”: { ... }, “offset”: 0, “limit”: 25, “size”: 289, “first”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=0”}}, “previous”: null, “next”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=25”}}, “last”: { “meta”:{“href”: “…”}}, “items”: [ { “meta”: { “href”: “…”, ...} }, … ] } @lhazlewood  
68. Pagination @lhazlewood  
69. Collection Resource supports query params: •  Offset •  Limit …/applications?offset=50&limit=25 @lhazlewood  
70. GET /accounts/x7y8z9/groups 200 OK { “meta”: { ... }, “offset”: 0, “limit”: 25, “first”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=0”}}, “previous”: null, “next”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=25”}}, “last”: { “meta”:{“href”: “…”}}, “items”: [ { “meta”: { “href”: “…”, ...} }, { “meta”: { “href”: “…”, ...} }, … ] } @lhazlewood  
71. Sorting @lhazlewood  
72. GET .../accounts? orderBy=surname,givenName%20desc @lhazlewood  
73. Search @lhazlewood  
74. “Find all accounts with a ‘company.com’ email address that can login to a particular application” @lhazlewood  
75. GET /applications/x7y8z9/accounts? email=*company.com 200 OK { “meta”: { ... }, “offset”: 0, “limit”: 25, “first”: { “meta”:{“href”: “/applications/x7y8z9/accounts? email=*company.com&offset=0”}}, “previous”: null, “next”: { “meta”:{“href”: “/applications/x7y8z9/accounts? email=*company.com&offset=25”}}, “last”: { “meta”:{“href”: “…”}}, “items”: [ { “meta”: { “href”: “…”, ...} }, { “meta”: { “href”: “…”, ...} }, … ] } @lhazlewood  
76. Search cont’d •  Filter search .../accounts?q=some+value •  Attribute Search .../accounts? surname=Joe&email=*company.com @lhazlewood  
77. Search cont’d •  Starts with ?email=joe* •  Ends with ?email=*company.com •  Contains ?email=*foo* @lhazlewood  
78. Search cont’d •  Range queries “all accounts created between September 1st and the 15th” .../accounts? createdAt=[2014-09-01,2014-09-15] @lhazlewood  
79. Many To Many @lhazlewood  
80. Group to Account •  A group can have many accounts •  An account can be in many groups •  Each mapping is a resource: GroupMembership @lhazlewood  
81. GET /groupMemberships/23lk3j2j3 200 OK { “meta”:{“href”: “…/groupMemberships/23lk3j2j3”}, “account”: { “meta”:{“href”: “…”} }, “group”: { “meta”{“href”: “…”} }, … } @lhazlewood  
82. GET /accounts/x7y8z9 200 OK { “meta”:{“href”: “…/accounts/x7y8z9”}, “givenName”: “Tony”, “surname”: “Stark”, …, “groups”: { “meta”:{“href”: “…/accounts/x7y8z9/groups”} }, “groupMemberships”: { “meta”:{“href”: “…/groupMemberships?accountId=x7y8z9”} } } @lhazlewood  
83. Async or Long-Lived Operations @lhazlewood  
84. POST /emails { “from”: me@somewhere.com, “subject”: “Hi!” “body”: “...” } @lhazlewood  
85. 204 Accepted Location: /emails/23Sd932sSl { “status”: “queued”, ... } @lhazlewood  
86. GET /emails/23Sd932sSl Expires: 2014-09-29T18:00:00.000Z { “status”: “sent”, ... } @lhazlewood  
87. Batch Operations @lhazlewood  
88. •  Each batch reflects a resource •  Batches are likely to be a collection •  Batches are likely to have a status •  Batch deletes easier than create/update @lhazlewood  
89. Batch Delete “Delete  all  company.com  accounts”     DELETE /accounts? email=*@company.com @lhazlewood  
90. Batch Create / Update Already  have  a  CollecEon  concept.    Use  it. @lhazlewood  
91. Batch Create or Update POST  /accounts     {          “items”: [ { ... account 1 ... }, { ... account 2 ... }, ... ] } @lhazlewood  
92. Batch Operations: The ‘Catch’ Caching  is  bypassed  enErely  L @lhazlewood  
93. 204 Accepted Location: /batches/a1b2c3 { “status”: “processing”, //overall status “size”: “n”, “limit”: 25, ..., “items”: { { response 1 (w/ individual status) ...}, { response 2 (w/ individual status) ...}, ... } } @lhazlewood  
94. Errors @lhazlewood  
95. •  As descriptive as possible •  As much information as possible •  Developers are your customers @lhazlewood  
96. POST /directories 409 Conflict { “status”: 409, “code”: 40924, “property”: “name”, “message”: “A Directory named ‘Avengers’ already exists.”, “developerMessage”: “A directory named ‘Avengers’ already exists. If you have a stale local cache, please expire it now.”, “moreInfo”: “https://www.stormpath.com/docs/ api/errors/40924” } @lhazlewood  
97. Security @lhazlewood  
98. Avoid sessions when possible Authenticate every request if necessary Stateless Authorize based on resource content, NOT URL! Use Existing Protocol: Oauth 1.0a, Oauth2, Basic over SSL only Custom Authentication Scheme: Only if you provide client code / SDK Only if you really, really know what you’re doing Use API Keys instead of Username/Passwords @lhazlewood  
99. 401 vs 403 •  401 “Unauthorized” really means Unauthenticated “You need valid credentials for me to respond to this request” •  403 “Forbidden” really means Unauthorized “Sorry, you’re not allowed!” @lhazlewood  
100. HTTP Authentication Schemes •  Server  response  to  issue  challenge:     WWW-Authenticate: <scheme name> realm=“Application Name” •  Client  request  to  submit  credenEals:     Authorization: <scheme name> <data> @lhazlewood  
101. API Keys •  •  •  •  •  •  •  Entropy Password Reset Independence Scope Speed Limited Exposure Traceability @lhazlewood  
102. IDs @lhazlewood  
103. •  IDs should be opaque •  Should be globally unique •  Avoid sequential numbers (contention, fusking) •  Good candidates: UUIDs, ‘Url64’ @lhazlewood  
104. HTTP Method Overrides @lhazlewood  
105. POST /accounts/x7y8z9?_method=DELETE @lhazlewood  
106. Caching & Concurrency Control @lhazlewood  
107. Server  (iniEal  response):   ETag: "686897696a7c876b7e” Client  (later  request): If-None-Match: "686897696a7c876b7e” Server  (later  response):    304 Not Modified @lhazlewood  
108. Maintenance @lhazlewood  
109. Use HTTP Redirects Create abstraction layer / endpoints when migrating Use well defined custom Media Types @lhazlewood  
110. .com •  •  •  •  •  •  •  Free for developers Eliminate months of development Automatic security best practices Single Sign On for your apps API Authentication & Key Management Token Authentication for SPAs / Mobile Authorization Libraries  and  integraEons:     h`ps://docs.stormpath.com   @lhazlewood  

首页 - Wiki
Copyright © 2011-2024 iteam. Current version is 2.125.3. UTC+08:00, 2024-05-20 19:46
浙ICP备14020137号-1 $访客地图$