Chris Keele
Chris Keele

Reputation: 3453

Rails routing: format constraints when none is specified

For Reasons™, I want to have one controller handle html requests and another handle json api requests.

I've got this in my routes:

scope module: :api, as: :api do
  constraints format: :json do
    resources :users
  end
end
constraints format: :html do
  resources :users
end

This works great when the url sports a suffix: /users.json goes through my Api::UsersController à la api_users_path; /users.html goes through my UsersController à la users_path.

However this does not behave as expected when there is no suffix in the url. Implementing the constraints as lambda show things go wrong:

#=> visiting /users

scope module: :api, as: :api do
  constraints ->(request){ puts "In api: #{request.format}"; request.format == :json } do
    resources :users
  end
end
constraints ->(request){ puts "In html: #{request.format}"; request.format == :html } do
  resources :users
end

#=> In api: text/html
#=> (request.format == :json) => false

and yet it ends up in the api controller.

No fiddling with custom constraint classes or lambdas or anything prevents rails from selecting the first route defined if none of the constraints match.

I can't find a way to write a constraint that catches when the url lacks a suffix, and I don't feel as if I should have to–request.format == :html reports true when I'm navigating to /users. Why isn't the second constraint catching that?

Also, while I could "fix" this by changing the order of these two, I'd much rather know why my constraints aren't working right.

Does anyone know how to enforce these constraints differently to effectively switch on whatever the format is, not just the url suffix, or have an explicit constraint that accommodates no format suffix?

Upvotes: 6

Views: 4765

Answers (3)

mczepiel
mczepiel

Reputation: 711

Setting the suffix-less default format to be json should take care of your needs.

scope module: :api, as: :api, defaults: { format: :json} do
  constraints format: :json do
    resources :users
  end
end

Only explicit .html suffix requests end up at this resource

resources :users

Upvotes: 1

Aditya
Aditya

Reputation: 144

Extending Jeremy's answer.. What if you insert the users default html routes before tha api scope for routes?

like,

resources :users
scope module: :api, as: :api do
  constraints format: :json do
    resources :users
  end
end

Strangely, this works for me!

Upvotes: 1

Jeremy Green
Jeremy Green

Reputation: 8594

I don't think you need the second constraints block (the one for :html). I think this should do what you want:

scope module: :api, as: :api do
  constraints format: :json do
    resources :users
  end
end

resources :users

Upvotes: 3

Related Questions