3

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?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Josh
  • 366
  • 2
  • 8
  • 20
  • Is there a reason that you do the redirecting in your application? If not I would recommend to use mod_rewrite which is a bit complexer to setup but much faster or the similar thing of your web sever of choice. – Sir l33tname Mar 27 '15 at 07:53

4 Answers4

1
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)

three
  • 8,262
  • 3
  • 35
  • 39
  • It seems the redirect causes the before method to be called again, resulting in a NoMethodError for using a 'join' on a string. Is there a way to only execute the before method on first load? – Josh Mar 23 '15 at 08:53
  • Additionally, would the redirect affect the initial get request, which is waiting for a response from my API? – Josh Mar 23 '15 at 08:55
  • have you tried params = params[:text].split.join "/" – peter Mar 23 '15 at 09:12
  • @peter The problem is that there are other parameters that I need, which I left out of the question since I didn't think they were relevant. I don't want to overwrite these – Josh Mar 23 '15 at 09:16
0

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.

nicohvi
  • 2,270
  • 2
  • 28
  • 42
  • The redirect seems to cause problems with the initial GET request, I don't receive a response on the social platform side – Josh Mar 23 '15 at 10:41
0

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
peter
  • 41,770
  • 5
  • 64
  • 108
  • Would this method cause my get routes to be still be hit or would that require a redirect? I am aiming for the following effect: GET /?text=corporate finance Hits the same endpoint as GET /corporate/finance – Josh Mar 23 '15 at 10:27
  • @Josh: i adapted my answer to make that more clear, haven't tried it since you do this in the before block this sould indeed have that effect, the sinatra intro stipulates "By the way, unless you disable the path traversal attack protection (see below), the request path might be modified before matching against your routes." – peter Mar 23 '15 at 12:34
  • This wouldn't cause the get /corporate/finance endpoint to fire off. The value of the text parameter can't affect which endpoint is hit – Josh Mar 23 '15 at 13:42
  • I wouldn't want all of those to lead to the same. Only the second two. The answer that I posted takes this into account and accurately mimics a GET request to /param1/param2 etc where these parameters are defined as mentioned in the question. This way it supports an indefinite number of meaningful parameters. – Josh Mar 23 '15 at 14:56
0

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.

Josh
  • 366
  • 2
  • 8
  • 20
  • 1
    yes, call is an internal redirect without browser redirection. Might be the best solution. I have fixed my answer but it still uses a manual redirect. – three Mar 23 '15 at 14:16