Rails routing
For Rails routing, you can refer to the official documentation, and searching for something like "Rails routing" also turns up plenty of information.
- Rails Guides: Rails Routing from the Outside In
Even when using API mode, the way you write routes is the same, so it doesn't cause any trouble—but I got curious again about how format is handled, so I looked into it.
The rest of this post assumes we're building an API server that handles JSON, and I verified behavior with the following versions:
- ruby: 2.7.1
- rails: 6.0.3.3
Setting format in routing
As a baseline, let's define the following routes.
Rails.application.routes.draw do
namespace :v1 do
resources :users
end
end
# Output of rails routes (partially omitted)
# v1_users GET /v1/users(.:format) v1/users#index
# POST /v1/users(.:format) v1/users#create
# v1_user GET /v1/users/:id(.:format) v1/users#show
# PATCH /v1/users/:id(.:format) v1/users#update
# PUT /v1/users/:id(.:format) v1/users#update
# DELETE /v1/users/:id(.:format) v1/users#destroy
And let's say v1/users#show is defined as follows:
def show
render json: User.find(params[:id])
end
Here's what happens when requesting in three different patterns. (Some header information is omitted.)
# Pattern 1
$ curl -i localhost:3000/v1/users/1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
# Pattern 2
$ curl -i localhost:3000/v1/users/1.json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
# Pattern 3
$ curl -i localhost:3000/v1/users/1.html
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
Patterns 1 and 2 look fine, but in pattern 3, even though we requested .html, we still get a JSON response back. Since the action uses render json:, the controller returns JSON regardless of the format.
Specifying format (default scope)
Change the routing to namespace :v1, default: { format: 'json' } do.
- Rails Guides: Defining Defaults
Rails.application.routes.draw do
namespace :v1, default: { format: 'json' } do
resources :users
end
end
Requesting the same three patterns produced the same results as when nothing was specified. However, when using the head method to return a response, the headers did change.
# Pattern 1
$ curl -i localhost:3000/v1/users/1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
# Pattern 2
$ curl -i localhost:3000/v1/users/1.json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
# Pattern 3
$ curl -i localhost:3000/v1/users/1.html
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
Specifying format
Change the routing to namespace :v1, format: 'json' do.
Rails.application.routes.draw do
namespace :v1, format: 'json' do
resources :users
end
end
Requesting the same three patterns, pattern 3 now raised a RoutingError and returned a 404.
# Pattern 1
$ curl -i localhost:3000/v1/users/1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
# Pattern 2
$ curl -i localhost:3000/v1/users/1.json
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{"id":1,"email":"email"}
# Pattern 3
$ curl -i localhost:3000/v1/users/1.html
HTTP/1.1 404 Not Found
# RoutingError raised
So which one should you use?
As mentioned in the article below that I referenced, for an API that handles JSON, using format: 'json'—the last option introduced in this post—seems like the best choice.
- scramble cadenza: respond_to vs. specifying format in routes
As in the example, it seems more desirable behavior-wise to return a 404 when .html or some other format is requested unintentionally (or maliciously).
If you want to allow other formats as an exception, you can override this by setting format on either the individual route or the block. It also seems possible to use a regular expression such as /json|yaml/.