I have 2 wildcard routes that look like:
get '*country_path/', to: 'country#list', constraints: { country_path: /\/$/ }
get '*country_path/:title', to: 'country#show'
country#list should be called when the url has a trailing slash. Examples:
www.example.com/usa/california/
www.example.com/usa/california/abc/
country#show should be called when it does NOT have a trailing slash. Examples:
www.example.com/usa/californa/travel
www.example.com/usa/californa/average-income
Currently my constraint doesn't seem to work as all requests go to country#list.
What is wrong with my route constraint?
You should ask yourself whether this is really the best way to accomplish what you are trying to do. Websites in general do not care about trailing slashes in directories. For example:
www.facebook.com/username
Will take you to the same place as:
www.facebook.com/username/
The vast majority of websites work like this, so what you are trying to do right now is against web conventions. It will certainly have ramifications for SEO, if you care about that. Even if you don't, you should think about the difficulties you will face when testing these routes. Your tests will be brittle and this will cause a lot of frustration in the long run.
My advice would be this: if you want to distinguish between list
and show
, put those into the URL. For example:
www.example.com/usa/california/list
www.example.com/usa/california/abc/list
Can take the user to country#list
, with "california" and "abc" as parameters. Similarly:
www.example.com/usa/californa/travel/show
www.example.com/usa/californa/average-income/show
Can take the user to country#show
. This is significantly easier to do. It is also more standard and user-friendly. For more information, see Rails Routing from the Outside In.
The trouble is that Rails has stripped the trailing /
before it gets to the routing, so it's not possible to match on it using the conventional methods. The only way to do this is to use an advanced constraint. The following will work for what you want:
# config/routes.rb
class TrailingSlashMatcher
def matches?(request)
uri = request.env["REQUEST_URI"]
uri.present? && uri.end_with?("/")
end
end
Rails.application.routes.draw do
get '*country_path', to: 'country#list', :constraints => TrailingSlashMatcher.new
get '*country_path/:title', to: 'country#show'
end
(taken from here).
The trouble with this approach is that it won't work in all circumstances. It works for real requests to a server, but it may well have issues when being called from tests etc. The reason for this is that the REQUEST_URI
env variable isn't part of the rack spec, therefore isn't set by the test framework. env["PATH_INFO"]
is mandatory, but that has the trailing /
removed, so is no good here.
All in all, it may be worth reconsidering the design of your routes. Matching on the presence or absence of a trailing /
is fairly brittle. It could also lead to some confusion for your users (e.g. when links get copied/pasted etc). Is there no other way to differentiate between list and show requests (maybe based on the depth of the route).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With