CSC/ECE 517 Spring 2015/ch1b 15 SH
Rails 4.0 and the PATCH verb
Background
HyperText Transfer Protocol (HTTP) is the underlying protocol used by web browsers to communicate with servers. It is a standard for the messages sent by the client to request for information or a specific action from the server. A HTTP request consists of a method that tells the server what the client needs. This method is also called an action or a verb. Some commonly used HTTP verbs in the Rails context are GET,POST, PUT, PATCH and DELETE.
The Rails app receives a request from a client browser and routes it based on the request's URI and the verb (or action or method). Then rails router dispatches the request to the appropriate controller method along with the parameters/data associated with it. This is the job of the Rails router. The router can also generate paths and URLs for all the common actions for a given controller and this is called resource routing.
Rails and HTTP Verbs
As mentioned above, browsers request pages or services from server through a URL using a specific HTTP verb. These verbs specify an operation that is requested on a resource. A resource route associates these verbs with actions on a specific controller. In other words, it specifies a mapping between a (URL, HTTP verb) combination and a controller action.
For example, an entry in a routing file as
resources :recipes
creates seven default routes in the application, mapping to actions in the Recipes controller. Each action is also associated with a CRUD operation in a database.
Before Rails 4.0 the routes used to look like this:
HTTP Verb Path action used for GET /recipes index display a list of all recipes GET /recipes/new new return an HTML form for creating a new recipe POST /recipes create create a new recipe GET /recipes/:id show display a specific recipe GET /recipes/:id/edit edit return an HTML form for editing a recipe PUT /recipes/:id update update a specific recipe DELETE /recipes/:id destroy delete a specific recipe
With Rails 4.0, the entry for the update route changed to:
HTTP Verb Path action used for PATCH/PUT /recipes/:id photos#update update a specific recipe
In the following sections, we will look at why PATCH is the correct HTTP verb to match the "update" action.
PUT and PATCH
First, we will look at how the PUT action is different from the more familiar HTTP POST. PUT is used when a resource can be updated through itself. POST is used when we do not know the actual location of the resource, like in case of a create action. For example, if we want to add a new recipe, but let the server decide where to store it, we use POST
POST /recipes HTTP/1.1 { "title" : "Lemonade", "description" : "A refreshing drink", "category" : "1" } HTTP/1.1 201 Created Location: /recipes/124
If we want to access a recipe that we know resides at /recipes/123, it can be done directly with a PUT action on this URL
PUT /recipes/123 HTTP/1.1 { "title": "Lemonade", "description": "A refreshing drink", "category" : "1" }
(Note that the action in the example for POST above can also be done with PUT, in case the client decides the actual URL) So the PUT action can be used to request creation or an update to a resource. Before 4.0, Rails by default uses POST for create action and PUT for update action.
What is wrong with PUT
The problem seen with HTTP PUT is that it does not allow partial updates to resources. The HTTP verb PUT means "creation and replacement" at the given URL. A request to update a resource through PUT would need a complete representation of the resource for replacement.
For example, if the recipe resource has the following representation
{ "title": "Lemonade", "description" : "A refreshing drink", "category" : "1" }
and we want to update the category field to "5", submitting only category=5 as payload via PUT to the /recipes/:id URL does not conform to the HTTP semantics. We would need to PUT the entire recipe back with the updated value of category, which may require a GET of its current state, can get complex and would require more bandwidth<ref>https://www.mnot.net/blog/2012/09/05/patch</ref>.
The way out of these constraints with HTTP methods is to define category as a resource by itself, and give it a URL for update, like this : /recipe/:id/category. A PUT to this URL will now work as category=5 would be a complete representation of its resource. This approach would need us to specify routes for every field of the resource for updating. An alternative could be to use POST for partial updates, which does not have any generic semantics, and the server and client would need application-specific code to support it. We could use PATCH instead.
PATCH
The HTTP method PATCH can be used to update partial resources. It was proposed as a new way to modify HTTP resources. PATCH is like PUT in that it updates a resource, but instead of replacing the entire resource with data from the payload, it applies a "diff" or a "delta". While PATCH does not require the complete representation of the resource to be sent, it is not enough to send just an updated value. The payload for PATCH describes modifications to be made to that resource.
The PATCH method requests that these modifications be applied to the resource identified by the URL. A diff is applied to the resource currently existing at the server to produce a new version.
PATCH /recipes/123 HTTP/1.1 [description of changes]
Note that this set of changes must be applied atomically. It should not be possible that resources are only half-modified when requested by a GET.<ref>http://restcookbook.com/HTTP%20Methods/patch/</ref>
When using PATCH to update the resource,you can use whatever format you want as [description of changes], as far as its semantics are well-defined. PATCH with incorrect semantics is not recommended<ref>http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/</ref>.
From the RFC<ref>https://tools.ietf.org/html/rfc5789</ref>:
"The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH."
One of its principles of REST is to leverage on the standardized behavior of the protocol underlying it (cite) Since PATCH follows the semantics of HTTP better than PUT, it is more RESTful<ref>http://stackoverflow.com/questions/19732423/why-isnt-http-put-allowed-to-do-partial-updates-in-a-rest-api</ref>.
PATCH Usage - Example
This section illustrates how PATCH works in Rails with a simple example<ref>https://www.railstutorial.org/book/updating_and_deleting_users</ref>. A form for editing a user model Active Record which contains name and email is shown below:
<%= form_for(@user) do |f| %> <%= f.label :name %> <%= f.text_field :name, class: 'form-control' %> <%= f.label :email %> <%= f.email_field :email, class: 'form-control' %> <%= f.submit "Update User", class: "btn btn-primary" %> <% end %>
Rails converts this form to HTML and places either a POST or a PATCH action, depending on the return value from ActiveRecord’s new_record? boolean method. If @user.new_record? is true, Rails uses POST; otherwise, it uses PATCH.
For the form above, Rails uses the PATCH method for updating the existing record, and generates HTML with the following code:
<form accept-charset="UTF-8" action="/users/1" class="edit_user" id="edit_user_1" method="post"> <input name="_method" type="hidden" value="patch" />
As web browsers cannot directly send PATCH requests, Rails fakes it with a POST request, with PATCH as a hidden input field (as seen in the generated HTML code).
The PATCH verb can also be used in testing to check if an update action is successful. A sample test which uses the PATCH verb is shown below:
require 'test_helper' class UsersEditTest < ActionDispatch::IntegrationTest def setup @user = users(:user1) end test "incomplete edit" do get edit_user_path(@user) assert_template 'users/edit' patch user_path(@user), user: { name: "", email: "email@invalid"} assert_template 'users/edit' end end
If the edit method in the UsersController renders the edit page again on an unsuccessful update, then this test will pass.
See Also
https://tools.ietf.org/html/rfc5789
https://github.com/rails/rails/pull/505
http://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates/
http://guides.rubyonrails.org/routing.html
http://restful-api-design.readthedocs.org/en/latest/methods.html
http://restcookbook.com/HTTP%20Methods/patch/
http://williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot/
http://robots.thoughtbot.com/back-to-basics-http-requests
References
<references />