What I am trying to do is pretty simple. There are multiple versions of a Rails REST API. So, there are routes like:
http://www.example.com/v1/user.json
http://www.example.com/v2/user.json
http://www.example.com/v3/user.json
What I want to do is add custom http headers to the response based on the API version endpoint that is requested.
In my config/application.rb file, I tried:
config.action_dispatch.default_headers.merge!('my_header_1' => 'my_value_1', 'my_header_2' => 'my_value_2')
I have also tried this in my config/routes.rb file:
scope path: "v1", controller: :test do
get "action_1" => :action_1
get "action_2" => :action_2
Rails.application.config.action_dispatch.default_headers.merge!('my_header_1' => 'my_value_1', 'my_header_2' => 'my_value_2')
end
But both of these snippets append custom headers to the response irrespective of the API version endpoint.
I think I can write a middleware that checks the request url and appends the response headers based on that but it sounds a bit hackish.
Is there a better way to achieve this? Preferably via config or some central piece of code?
What about using a before_action on your controllers? I imagine each API version has its own controllers? That way you could do something like:
class API::V1::BaseController < ApplicationController
before_action :set_headers
protected
def set_headers
response.headers['X-Foo'] = 'V1'
end
end
Related
I have currently configured Devise,Doorkeeper and grape in my rails application.
Devise and Doorkeeper are configured so that I can register and login with Devise on the website and Doorkeeper provides oAuth endpoints that can create tokens.
How can I add a token to a HttpRequest and protect the grape API with it?
Edit:
So I tried to implement the Winebouncer implementation Tom Hert suggested.
I followed the instructions on https://github.com/antek-drzewiecki/wine_bouncer
I have installed the gem.
I have defined config/initializers/wine_bouncer.rb as the following.
WineBouncer.configure do |config|
config.auth_strategy = :default
config.define_resource_owner do
User.find(doorkeeper_access_token.resource_owner_id) if doorkeeper_access_token
end
end
I have registered Winebouncer as middleware in grape in my base api controller.
app\controllers\api\base.rb
module API
class Base < Grape::API
mount API::V1::Base
use ::WineBouncer::OAuth2
end
end
I mounted my projects controller in my V1 base controller
app\controllers\api\v1\base.rb
module API
module V1
class Base < Grape::API
mount API::V1::Projects
end
end
end
And this is my projectscontroller
app\controllers\api\v1\projects.rb
module API
module V1
class Projects < Grape::API
version 'v1'
format :json
resource :projects do
desc "Return list of projects" , auth: { scopes: [] }
get do
Project.all
end
end
end
end
end
To be honest I don't yet know how the ", auth: { scopes: [] }" in the description is suppossed to work. And how to add the token to a request, but I would expect my request but be blocked when no token is added. But the the request is still producing the json data.
I found quite interesting code here: https://github.com/fuCtor/grape-doorkeeper
It seems to be still maintained. But I think this is good just to get the idea of what is going on there.
I would recommend this: https://github.com/antek-drzewiecki/wine_bouncer
As said on the page:
Protect your precious Grape API with Doorkeeper. WineBouncer uses
minimal modification, to make the magic happen.
obedeijn, i just noticed your question on stackoverflow.
WineBouncer works just like doorkeeper, it looks for the Authorizations header with a "Bearer x" where x is the token.
I have a rails web application and I need to create API for mobile clients. I choose a Sinatra web framework for this. But I have a problem with my Sinatra app, after every request all data session lost.
My API looks like this(lib/api/core.rb):
module Api
class Core < Sinatra::Base
set :session_secret, 'secret'
enable :sessions
get '/foo' do
content_type :json
session['foo'] = 'some value'
end
get '/bar' do
content_type :json
session['foo']#everytime is nil
end
end
end
In my route.rb I wrote this:
constraints :subdomain => 'api' do
mount Api::Core => '/'
end
I use Rails 3.2.8, Sinatra 1.3.3
And my questions is how can I store data between requests(it's need me for authentication) ?
Your APIs should be stateless. Authentication is usually done with tokens that are sent along with every request. See RailsCast #352 Securing an API for more info.
I am using the mixpanel gem for my application. It acts as middleware and dynamically inserts the code into the head for any action. I'd like to be able to turn it off for specific actions (for instance, we have an action that sends an email and we'd rather not have the code there). Any ideas for how to accomplish this?
Thanks a lot.
it seems that mixpanel have updated their gem
Prevent middleware from inserting code
Note: Only applies when Rack Middleware is setup.
Occasionally you may need to send a request for HTML that you don't want the middleware to alter. In your AJAX request include the header "SKIP_MIXPANEL_MIDDLEWARE" to prevent the mixpanel code from being inserted.
$.ajax("/path/to/api/endpoint", {
headers: {"Skip-Mixpanel-Middleware": true}, // valid http headers don't allow underscores and get filtered by some webservers
success: function(data) {
// Process data here
} });
//Alternatively, you can add this line of code to your controller to temporarily disable the middleware:
Mixpanel::Middleware.skip_this_request
Taken from:
https://github.com/zevarito/mixpanel#prevent-middleware-from-inserting-code
From the Mixpanel docs:
In your application_controller class add a method to instance mixpanel.
before_filter :initialize_mixpanel
def initialize_mixpanel
#mixpanel = Mixpanel::Tracker.new("YOUR_MIXPANEL_API_TOKEN", request.env, true)
end
Since it's initialized by a before_filter you can use skip_before_filter in your other controllers to, well, skip it for certain actions, or for all except a certain action, e.g.:
class SomeController < ActionController::Base
skip_before_filter :initialize_mixpanel, :only => [ :create, :new ]
# or
skip_before_filter :initialize_mixpanel, :except => [ :update ]
end
We couldn't figure out a way to do this and ended up stripping it out (using gsub) after the fact. If anyone else has a better solution down the road I will definitely mark yours as right, I just want to close the question. Thanks
I was wondering how I go about creating a mobile version of a Rails 3.0 application.
I saw this post: Mobile version of views for Ruby on Rails
But I am confused on the respond_to method. How does the method know which format to render?
Would I create a method in my application controller to render a mobile layout and then for each view use the respond_to method?
Thank you,
Brian
Ryan Bates has done a great tutorial
http://railscasts.com/episodes/199-mobile-devices
The respond_to method will choose according to the current request's mime type.
This works out of the box for common mime types, but you'll need to tell your application about your custom ones. In your application controller, you'll want to define a method that will adjust the format of Rails' internal reprensentation of the request. Then, call that method as a before filter. Here's an example:
class ApplicationController < ActionController::Base
before_filter :adjust_for_mobile
def adjust_for_mobile
request.format = :mobile if mobile_request
end
# You'll also need to define the mobile_request method
# using whatever strategy you want to tell if a request
# is from a mobile client or not
def mobile_request
true
end
end
Make sure you've defined this new type in config/initializers/mime_types.rb:
Mime::Type.register "text/html", :mobile
Then, in your controllers, you'll be able to use the 'mobile' format:
class FoosController < ApplicationController
def index
#foos = Foo.all
respond_to do |format|
format.html # index.html.erb
format.mobile # index.mobile.erb
end
end
end
This sure looks elegant and all but in practice, I find that I rarely use it for mobile sites. The mobile sites I've been working on are generally quite different from the 'complete' sites. In those cases it makes sense to just define another bunch of controllers under a 'mobile' namespace.
Have a look at Rails Mobile
I have developed that plugin a while back. The idea behind that plugin is you can redirect to different controllers or views based on your mobile device capabilities through your router config file.
At the end of the routing.rb add these lines:
MobileDispatch::Categories.add do
def mobile_classifier(device)
"_mobile"
end
end
These lines define a new substring for all mobile devices which will be stored in $ variable for each request in the rouging.rb file.
That way you can play with your routing rules. For instance this line in routing.rb:
match '/photo/:id', :to => "photo#index$", :classifier => :mobile_classifier
for a normal user would be interpreted as:
match '/photo/:id', :to => "photo#index", :classifier => :mobile_classifier
while for a mobile user as:
match '/photo/:id', :to => "photo#index_mobile", :classifier => :mobile_classifier
The power here is in mobile_classifier(device) method where you can return different classification based on device object.
so let say we modify the method to return "_iphone" for all iphone devices and "_android" for all android mobiles, then the above routing line would be interpreted as:
match '/photo/:id', :to => "photo#index_iphone", :classifier => :mobile_classifier
match '/photo/:id', :to => "photo#index_android", :classifier => :mobile_classifier
If you add the $ to the end of view part of each route (similar to what we did here) you will get different methods in your controller for each category of devices and different view names for each method (index_iphone.htm.erb and index_android.ht.erb) This way you have seperate views/layers for each device category that you defined in your mobile_classifier method.
I am implementing a REST API which is versioned(like Twitter API), so based on version in request, I need to render template specific to the version, for example, if the client requests:
http://www.foo.com/api/v1/posts.json
I'd like to have the controller render:
posts/index.v1.json.erb
but if the client requests
http://www.foo.com/api/v2/posts.json
I'd like to have the controller render:
posts/index.v2.json.erb
and so on.
the version number in URL will be put in params hash in route.rb.
I want to do this in a reusable way, so it's not acceptable to repeat the logic in specific controller action.
I have tried view resolver, however it doesn't have access to request so there is no way I can pass the version number to resolver.
is there any way to accomplish this?
Thank you!
-Xiaotian
You can specify the version number in your routes.rb like:
map.connect '/api/:version/posts', :controller => :api, :action => :index, :version => :version
Then you would have access to the version in your controller via params[:version] and can handle it appropriately.
I think I would suggest making a separate controller for each version, maybe in the same module
Api::VersionOneConroller
Api::VersionTwoConroller
or something like that, I am not familiar with your whole app so I cannot say whether that will work, buts its something to consider.
------------update-------------
if the difference in versions in only in output foramtting or somehting, and all the actions do the same thing you could add after filters
class ExampleContrller < ApplicationController
after_filter :manage_versions
...
end
ApplicationController < ActionController::Base
protected
def manage_versions
case params[:version]
when '1.0'
#response to xml
when '1.2'
#response to json
else
# err or default to one
end
end
end
something like that could work
read more about filters here http://rails.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html