resource_design.asciidoc 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. [[resource_design]]
  2. == Designing a resource handler
  3. This chapter aims to provide you with a list of questions
  4. you must answer in order to write a good resource handler.
  5. It is meant to be usable as a step by step guide.
  6. === The service
  7. Can the service become unavailable, and when it does, can
  8. we detect it? For example, database connectivity problems
  9. may be detected early. We may also have planned outages
  10. of all or parts of the system. Implement the
  11. `service_available` callback.
  12. What HTTP methods does the service implement? Do we need
  13. more than the standard OPTIONS, HEAD, GET, PUT, POST,
  14. PATCH and DELETE? Are we not using one of those at all?
  15. Implement the `known_methods` callback.
  16. === Type of resource handler
  17. Am I writing a handler for a collection of resources,
  18. or for a single resource?
  19. The semantics for each of these are quite different.
  20. You should not mix collection and single resource in
  21. the same handler.
  22. === Collection handler
  23. Skip this section if you are not doing a collection.
  24. Is the collection hardcoded or dynamic? For example,
  25. if you use the route `/users` for the collection of
  26. users then the collection is hardcoded; if you use
  27. `/forums/:category` for the collection of threads
  28. then it isn't. When the collection is hardcoded you
  29. can safely assume the resource always exists.
  30. What methods should I implement?
  31. OPTIONS is used to get some information about the
  32. collection. It is recommended to allow it even if you
  33. do not implement it, as Cowboy has a default
  34. implementation built-in.
  35. HEAD and GET are used to retrieve the collection.
  36. If you allow GET, also allow HEAD as there's no extra
  37. work required to make it work.
  38. POST is used to create a new resource inside the
  39. collection. Creating a resource by using POST on
  40. the collection is useful when resources may be
  41. created before knowing their URI, usually because
  42. parts of it are generated dynamically. A common
  43. case is some kind of auto incremented integer
  44. identifier.
  45. The next methods are more rarely allowed.
  46. PUT is used to create a new collection (when
  47. the collection isn't hardcoded), or replace
  48. the entire collection.
  49. DELETE is used to delete the entire collection.
  50. PATCH is used to modify the collection using
  51. instructions given in the request body. A PATCH
  52. operation is atomic. The PATCH operation may
  53. be used for such things as reordering; adding,
  54. modifying or deleting parts of the collection.
  55. === Single resource handler
  56. Skip this section if you are doing a collection.
  57. What methods should I implement?
  58. OPTIONS is used to get some information about the
  59. resource. It is recommended to allow it even if you
  60. do not implement it, as Cowboy has a default
  61. implementation built-in.
  62. HEAD and GET are used to retrieve the resource.
  63. If you allow GET, also allow HEAD as there's no extra
  64. work required to make it work.
  65. POST is used to update the resource.
  66. PUT is used to create a new resource (when it doesn't
  67. already exist) or replace the resource.
  68. DELETE is used to delete the resource.
  69. PATCH is used to modify the resource using
  70. instructions given in the request body. A PATCH
  71. operation is atomic. The PATCH operation may
  72. be used for adding, removing or modifying specific
  73. values in the resource.
  74. === The resource
  75. Following the above discussion, implement the
  76. `allowed_methods` callback.
  77. Does the resource always exist? If it may not, implement
  78. the `resource_exists` callback.
  79. Do I need to authenticate the client before they can
  80. access the resource? What authentication mechanisms
  81. should I provide? This may include form-based, token-based
  82. (in the URL or a cookie), HTTP basic, HTTP digest,
  83. SSL certificate or any other form of authentication.
  84. Implement the `is_authorized` callback.
  85. Do I need fine-grained access control? How do I determine
  86. that they are authorized access? Handle that in your
  87. `is_authorized` callback.
  88. Can access to a resource be forbidden regardless of access
  89. being authorized? A simple example of that is censorship
  90. of a resource. Implement the `forbidden` callback.
  91. Can access be rate-limited for authenticated users? Use the
  92. `rate_limited` callback.
  93. Are there any constraints on the length of the resource URI?
  94. For example, the URI may be used as a key in storage and may
  95. have a limit in length. Implement `uri_too_long`.
  96. === Representations
  97. What media types do I provide? If text based, what charsets
  98. are provided? What languages do I provide?
  99. Implement the mandatory `content_types_provided`. Prefix
  100. the callbacks with `to_` for clarity. For example, `to_html`
  101. or `to_text`. For resources that don't implement methods
  102. GET or HEAD, you must still accept at least one media type,
  103. but you can leave the callback as `undefined` since it will
  104. never be called.
  105. Implement the `languages_provided` or `charsets_provided`
  106. callbacks if applicable.
  107. Is there any other header that may make the representation
  108. of the resource vary? Implement the `variances` callback.
  109. Depending on your choices for caching content, you may
  110. want to implement one or more of the `generate_etag`,
  111. `last_modified` and `expires` callbacks.
  112. Do I want the user or user agent to actively choose a
  113. representation available? Send a list of available
  114. representations in the response body and implement
  115. the `multiple_choices` callback.
  116. === Redirections
  117. Do I need to keep track of what resources were deleted?
  118. For example, you may have a mechanism where moving a
  119. resource leaves a redirect link to its new location.
  120. Implement the `previously_existed` callback.
  121. Was the resource moved, and is the move temporary? If
  122. it is explicitly temporary, for example due to maintenance,
  123. implement the `moved_temporarily` callback. Otherwise,
  124. implement the `moved_permanently` callback.
  125. === The request
  126. Do you need to read the query string? Individual headers?
  127. Implement `malformed_request` and do all the parsing and
  128. validation in this function. Note that the body should not
  129. be read at this point.
  130. May there be a request body? Will I know its size?
  131. What's the maximum size of the request body I'm willing
  132. to accept? Implement `valid_entity_length`.
  133. Finally, take a look at the sections corresponding to the
  134. methods you are implementing.
  135. === OPTIONS method
  136. Cowboy by default will send back a list of allowed methods.
  137. Do I need to add more information to the response? Implement
  138. the `options` method.
  139. === GET and HEAD methods
  140. If you implement the methods GET and/or HEAD, you must
  141. implement one `ProvideResource` callback for each
  142. content-type returned by the `content_types_provided`
  143. callback.
  144. === PUT, POST and PATCH methods
  145. If you implement the methods PUT, POST and/or PATCH,
  146. you must implement the `content_types_accepted` callback,
  147. and one `AcceptCallback` callback for each content-type
  148. it returns. Prefix the `AcceptCallback` callback names
  149. with `from_` for clarity. For example, `from_html` or
  150. `from_json`.
  151. Do we want to allow the POST method to create individual
  152. resources directly through their URI (like PUT)? Implement
  153. the `allow_missing_post` callback. It is recommended to
  154. explicitly use PUT in these cases instead.
  155. May there be conflicts when using PUT to create or replace
  156. a resource? Do we want to make sure that two updates around
  157. the same time are not cancelling one another? Implement the
  158. `is_conflict` callback.
  159. === DELETE methods
  160. If you implement the method DELETE, you must implement
  161. the `delete_resource` callback.
  162. When `delete_resource` returns, is the resource completely
  163. removed from the server, including from any caching service?
  164. If not, and/or if the deletion is asynchronous and we have
  165. no way of knowing it has been completed yet, implement the
  166. `delete_completed` callback.