I am a ruby newbie and have been trying Sinatra for quite some time now, one thing that Iam not able to figure out is why does a '/' in the url make such a big difference.
I mean isnt:
get 'some_url' do
end
and
get 'some_url/' do
end
Supposed to point to the same route? why is that Sinatra considers it as different routes? I spent a good one hour trying to figure that out.
According to RFC 2616 and RFC 2396 (RFCs defining resource identity) those URLs do not define the same resource. Therefore Sinatra treats them differently. This is esp. important if you imagine the route returning a page with relative links. This link
click me
Would point to /bar if you're coming from /foo, to /foo/bar if you're coming from /foo/.
You can use the following syntax to define a route matching both:
get '/foo/?' do
# ...
end
Or the Regexp version mentioned in the comments above.
They are different routes. The second is a URL with a directory extension ('/'); the first is a URL with no extension. A lot of frameworks (like Rails) will interpret both as the same route, or append the `/' (e.g., Django, and Apache can be configured to do that as well), but technically they are different URLs.
Related
I'm using Sinatra and the shotgun server.
When I type in http://localhost:9393/tickets, my page loads as expected. But, with an extra "/" on the end, Sinatra suggests that I add
get '/tickets/' do
How do I get the server to accept the extra "/" without creating the extra route?
The information in Sinatra's "How do I make the trailing slash optional?" section looks useful, but this means I would need to add this code to every single route.
Is there an easier or more standard way to do that?
My route is set up as
get '/tickets' do
It looks like the FAQ doesn't mention an option that was added in 2017 (https://github.com/sinatra/sinatra/pull/1273/commits/2445a4994468aabe627f106341af79bfff24451e)
Put this in the same scope where you are defining your routes:
set :strict_paths, false
With this, Sinatra will treat /tickets/ as if it were /tickets so you don't need to add /? to all your paths
This question is actually bigger than it appears at first glance. Following the advice in "How do I make the trailing slash optional?" does solve the problem, but:
it requires you to modify all existing routes, and
it creates a "duplicate content" problem, where identical content is served from multiple URLs.
Both of these issues are solvable but I believe a cleaner solution is to create a redirect for all non-root URLs that end with a /. This can easily be done by adding Sinatra's before filter into the existing application controller:
before '/*/' do
redirect request.path_info.chomp('/')
end
get '/tickets' do
…
end
After that, your existing /tickets route will work as it did before, but now all requests to /tickets/ will be redirected to /tickets before being processed as normal.
Thus, the application will respond on both /ticket and /tickets/ endpoints without you having to change any of the existing routes.
PS: Redirecting the root URL (eg: http://localhost:9393/ → http://localhost:9393) will create an infinite loop, so you definitely don't want to do that.
I'm having issues getting Grape to respond to a purely wild card route.
By that I mean that if I have a simple route defined as
get do
...
end
I need to respond to all potential requests made to the API. The situation being I need to parse the path and params and then work through a decision tree based on those values.
I've tried a few variations on the route definition, such as:
get '/*' do
...
end
get '/(*)' do
...
end
But to no avail.
I know that there is some support for regular expressions and route anchoring in Grape, but I've had no luck figuring it out.
Thanks!
You were close with the guess at the syntax, you just need to name the matching param:
E.g.
get '*thing' do
{ :thing => params[:thing] }
end
Using the * will make the param capture the rest of the URI path, ignoring / separators. But otherwise it behaves just like any other param.
Note this will only pickup within the Rack mount point at best, so if you have
prefix 'api'
version 'v2'
then it will respond to paths like /api/v2/hkfhqk/fwggw/ewfewg
If you are using this for custom 404 or other catch-all routes, then you need to add it at the end, otherwise it will mask more specific routes.
I am currently developing a Ruby API based on Sinatra. This API mostly receives GET requests from an existing social platform which supports external API integration.
The social platform fires off GET requests in the following format (only relevant parameters shown):
GET /{command}
Parameters: command and text
Where text is a string that the user has entered.
In my case, params[:text] is in fact a series of commands, delimited by a space. What I want to achieve is, for example: If params[:text]="corporate finance"
Then I want my API to interpret the request as a GET request to
/{command}/corporate/finance
instead of requesting /{command} with a string as a parameter containing the rest of the request.
Can this be achieved on my side? Nothing can be changed in terms of the initial request from the social platform.
EDIT: I think a better way of explaining what I am trying to achieve is the following:
GET /list?text=corporate finance
Should hit the same endpoint/route as
GET /list/corporate/finance
This must not affect the initial GET request from the social platform as it expects a response containing text to display to the user. Is there a neat, best practice way of doing this?
get "/" do {
text = params[:text].split.join "/"
redirect "#{params[:command]}/#{text}"
end
might do the trick. Didn't check though.
EDIT: ok, the before filter was stupid. Basically you could also route to "/" and then redirect. Or, even better:
get "/:command" do {
text = params[:text].split.join "/"
redirect "#{params[:command]}/#{text}"
}
There a many possible ways of achieving this. You should check the routes section of the sinatra docs (https://github.com/sinatra/sinatra)
The answer by three should do the trick, and to get around the fact that the filter will be invoked with every request, a conditional like this should do:
before do
if params[:text]
sub_commands = params[:text].split.join "/"
redirect "#{params[:command]}/#{sub_commands}"
end
end
I have tested it in a demo application and it seems to work fine.
The solution was to use the call! method.
I used a regular expression to intercept calls which match /something with no further parameters (i.e. /something/something else). I think this step can be done more elegantly.
From there, I split up my commands:
get %r{^\/\w+$} do
params[:text] ? sub_commands="/"+params[:text].split.join("/") : sub_commands=""
status, headers, body = call! env.merge("PATH_INFO" => "/#{params[:command]}#{sub_commands}")
[status, headers, body]
end
This achieves exactly what I needed, as it activates the correct endpoint, as if the URL was typed it the usual format i.e. /command/subcommand1/subcommand2 etc.
Sorry, I completely misunderstood your question, so I replace my answer with this:
require 'sinatra'
get '/list/?*' do
"yep"
end
like this, the following routes all lead to the same
You need to add a routine for each command or replace the command with a * and depend your output based on a case when.
The params entered by the user can be referred by the params hash.
http://localhost:4567/list
http://localhost:4567/list/corporate/finance
http://localhost:4567/list?text=corporate/finance
i'm trying to make this code snippet from the sinatra tutorial work so that I can set some routes based on regex matching. it doesn't seem to be working and I'm copy pasting direct from the tutorial, any ideas on what i'm missing. make the assumption that my sinatra app is working and i have other correctly structured GET routes working so I'm unclear why a direct copy past like this doesn't work.
get %r{/hello/([\w]+)} do
"Hello, #{params[:captures].first}!"
end
should correct map a route for http://0.0.0.0:4567/hello but is routing to a 'sinatra doesn't know this ditty' error message.
Thks.
%r{/hello/([\w]+)} doesn't match /hello at all. Your regex requires a slash and another string, but your path doesn't include that.
That route would match /hello/there but not /hello or even /hello/.
And given that you are looking at the captures, you probably don't even want this to work with /hello at all since that capture would be nil and who want to say hello to nil?
I was just wondering if it is possible to ignore the last URI segment of my application via either mod_rewrite or CodeIgniter. I don't want a redirect away or a remove the URI segment. I just want my app to not know it exists. So in the browser the client will see:
http://example.com/keep/keep/ignore/
but the app is only aware of:
http://example.com/keep/keep/
The idea is, if JavaScript detects /ignore/ in the URI, it will trigger an action.
/ignore/ may appear as 1st, 2nd, 3rd or 4th segment, but will only ever appear as the final one and may sometimes not appear at all.
I found some info online about ignoring sub-directories with mod-rewrite, but none of them really work like this.
**
Incase any CodeIgniters suggest passing it as an unused extra parameter to my method - The app has far too many controllers and far too many wildcard routes for this to work site wide.
I think a mod_rewrite solution would be best if possible. If not, perhaps it can be done with a CodeIgniter pre-controller hook or something, but I'm not sure how that would work.
EDIT: How I got it to work
For anyone else who would ever like to know the same thing - in the end I overwrote _explode_segments() in MY_URI to not include this segment.
With the URI class you can check and detect what URI's are and what they have.
$this->uri->segment(n)
Check out the user guide: http://codeigniter.com/user_guide/libraries/uri.html