Get absolute URL with Grape-Api gem on Rack app - ruby-on-rails

I'm doing an API with grape gem, in one of my services I would like to retrieve the complete URL. For example if the user does request on :
api.myapp.com/android/users.json
I would like be able to retrieve api.myapp.com/android/users.json or at least /android/users.json
class MyApp::API::Users < Grape::API
resource :users do
get do
request.original_url
# stuff ...
end
end
end
I tried what I know from Rails, but now it's Grape and it doesn't work :
"error": "undefined method `original_url' for #<Grape::Request:0x00000005a78c08>"

I wanted to do the same thing (generate absolute URIs) within my own API and after much research, I eventually gave up. There is, amazingly, no good way I can find to get the information you're looking for out of Grape—you cannot, for instance, specify a resource is "mounted" at a specific path and then retrieve that information later.
What I wound up doing in the meantime was saving the base URL (scheme, hostname and port) in a global variable at the start of each request:
before do
# Save the base URL of the request, used by resources to build
# their canonical URI
Resources::base_url = request.base_url
end
and then, within each resource representer, "manually" assembling the URI using hardcoded path information:
link :self do
RideYork::API::Resources::base_url +
"/resources/agencies/#{represented.id}" if represented.id
end
It's a terrible hack, but I'm not aware of a better solution.

Grape::Request is just a Rack::Request. It looks like the Rack::Request has a #url method you could try.

Related

Accessing request context object in rails router

I need to create a few dynamic routes in my rails router in the following way:
Rails.application.routes.draw do
account = Account.find_by(
subdomain: request.subdomain,
domain: request.domain
)
EditableField.where(account_id: account.id).links.each do |link|
get link.link_href, to: link.method
end
end
As shown above, I need to determine the account based on the request domain and subdomain, however I cant find how to access the request object in the rails router. What is the correct way to do this ?
I would suggest using the rack-rewrite gem
https://github.com/jtrupiano/rack-rewrite
It is in lower level and give you much more options to do this kind of dynamic routing
And if you still want to do that in the routes.rb file, you can follow this answer https://stackoverflow.com/a/24411835/2529330
Enjoy :)

InvalidCrossOriginRequest when trying to send a Javascript Asset

I'm trying to create an "asset controller" shim which will filter static asset requests so only authorized users can get retrieve certain assets. I wanted to continue to use the asset pipeline so I setup a route like this
get 'assets/*assetfile' => 'assets#sendfile'
Then I created an AssetsController with one method "sendfile". Stripping it down to only the stuff that matters, it looks like this:
class AssetsController < ApplicationController
def sendfile
# Basically the following function forces the file
# path to be Rails.root/public/assets/basename
assetfilename=sanitize_filename(params[:assetfile] + '.' + params[:format])
send_file(assetfilename)
end
end
It looks like I have to run this in production mode as rails by-passes my route for assets in development. So I precompile my assets and I can verify in the controller that the files exist where they are expected to be.
However, now the problem is that I'm getting a "ActionController::InvalidCrossOriginRequest" when the Javascript asset is requested (just using the default application.* assets for now). I've read about this error and I understand that as of Rails 4.1 there are special cross-origin protections for Javascript assets. Sounds good to me, but I don't understand where the "cross-origin" part is coming from. Using firebug, I can see that the asset requests are being requested from the same domain as the original page.
I am certain that this is the problem because I can solve it by putting "skip_before_action :verify_authenticity_token" in the beginning of my controller. However, I really don't want to do this (I don't fully understand why this check is necessary, but I'm sure there are very good reasons).
The application.html.erb file is unchanged from the default generated file so I assume it's sending the CSRF token when the request is made, just as it would if I didn't have my own controller for assets.
So what am I missing?
Ok, I think I answered my own question (unsatisfactorily). Again, long post, so bear with me. I mistakenly forgot to add this to my original questions, but I'm using Ruby 2.2.0 and Rails 4.2.4.
From looking at the code in "actionpack-4.2.4/lib/action_controller/metal/request_forgery_protection.rb", it looks like Rails is doing two checks. The first check is the "verify_authenticity_token" method which does the expected validation of the authenticity token for POST requests. For GET requests, it ALSO sets a flag which causes a second check on the formed computed response to the request.
The check on the response simply says that if the request was NOT an XHR (AJAX) request AND the MIME Type of the response is "text/javascript", then raise an "ActionController::InvalidCrossOriginRequest", which was the error I was getting.
I verified this by setting the type to "application/javascript" for ".js" files in "send_file". Here's the code:
if request.format.js?
send_file(assetfilename, type: 'application/javascript')
else
send_file(assetfilename)
end
I can skip the response check all together by just adding the following line to the top of my controller class:
skip_after_action :verify_same_origin_request
The check on the response seems pretty weak to me and it's not clear how this really provides further protection against CSRF. But I'll post that in another question.

rails get app root/base url

In my app I have a few APIs that under api domain. Now in one of the API I want to generate a url that pointing to the main domain, say
test.com/blabla...
I tried to use url_for but seems the default root_url or request.host is in api domain. Url_for will make it to be
api.test.com/blabla..
while I want it to be
test.com/blabla...
Url_for can take a parameter
host: ...
to set it to be test.com/, the question is how can I get the root/base url (test.com) for host? root_url or request.host are all api.test.com.
Any ideas? Thanks.
Just so that it's useful to someone else , i came across this today
request.base_url
gives the full path in local as well as on live .
request.domain
gives just the domain name so it sometimes kinda breaks the link while redirecting
According to this you can do request.domain
Simplest alternative method:
include in you're class
include Rails.application.routes.url_helpers
create function or just use root_url to get app root/base url:
def add_host_prefix(url)
URI.join(root_url, url).to_s
end
finally: add
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
in:
Your_project_root_deir/config/environments/development.rb
although helpers can be accessible only in views but this is working solution.
request.domain fails on CF it given domain url not base url

How to change ActionCaching "views/" prefix per request

So we use the same controllers to serve both mobile and desktop views of our site. We also use action caching heavily to cache the html for a page in memcache. I've been trying to figure out a way to globally change the caching prefix for all mobile requests to "views-mobile/" instead of the standard "views/". That way the mobile and and desktop pages will be saved under a different namespace so there are no conflicts in memcache.
We could do this per caches_action method by creating a custom cache_path using the controller variable for is_mobile?, but we'd prefer to do it globally somehow. Any suggestions? I imagine this would require monkey-patching ActionController::Caching but I can't figure out where it generates the "views/" prefix.
I'm sorry, I was Rails nubie, so I don't really understand about your question, but if it right, is this what you mean?
This is on my routes.rb:
scope "/administrator" do
resources :users
end
I changed my users_path 'prefix' to administrator. Sorry if wrong :D
I actually ended up figuring this out myself. Basically ActionController::Base uses a function called fragment_cache_key to generate the cache key for a specific fragment (which is what ActionCaching uses deep down). So you basically override that method and include your own logic for how to generate the prefix. This is how my method override looks:
# Monkey patch fragment_cache_key
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, mobile_device? ? "views-mobile" : "views")
end
Where mobile_device? is my own function that figures out whether the user is requesting the mobile or desktop version of the site.

Support for multiple domains/subdomains in Rails

I have a Rails app that has a similar setup to Tumblr, that is, you can have either:
(1) Subdomain hosting (your-username.myapp.com)
(2) Domain hosting (your-username.com)
Both would forward to a personalized website for that user, created with my application.
How can I accomplish this in Rails? I have been able to get (1) working with subdomain-fu, but I'm not sure how to get (2) working. Any pointers (plugins, gems, tutorials), etc. would be greatly helpful, I can't seem to find any.
Thanks!
The principle for domains is the same as the subdomain - find the domain, map to an account.
The details will depend on how your hosting is going to handle the DNS.
I am currently using Heroku and its wildcard service.
In this case, the domain is mapped with a cname to the subdomain hosted by my Heroku app. From here I can work out the associated account and details.
EDIT: I've found a much easier way: http://www.arctickiwi.com/blog/7-host-and-domain-based-routing-in-ruby-on-rails
Not exactly an answer but this is the best I can give. Maybe this'll help you too.
Ideally, this blog post from transfs.com and subdomain-fu should do the trick. I've been trying to implement it, however, and they don't seem to play nicely together.
Basically, if I don't include the intiializer, the subdomain route works fine. If I include the initializer, the subdomain route breaks (everything gets caught by map.root). I have a feeling it's with the way it builds the condition string in the initializer. If you can figure out how it breaks, then you'll have a working app.
My initializer:
module ActionController
module Routing
class RouteSet
def extract_request_environment(request)
env = { :method => request.method }
env[:domain] = request.domain if request.domain
env[:host] = request.host if request.host
env
end
end
class Route
alias_method :old_recognition_conditions, :recognition_conditions
def recognition_conditions
result = old_recognition_conditions
[:host, :domain].each do |key|
if conditions[key]
operator = "==="
if conditions[key].is_a?(Regexp)
operator = "=~"
end
result << "conditions[:#{key.to_s}] #{operator} env[:#{key.to_s}]"
end
end
result
end
end# end class Route
end
end
My routes (just for development). You'll see my local development domain, stiltify.dev. Sorry, I tried to make it look good in here but I couldn't get the code block to look nice. I put it on pastie instead: http://pastie.org/940619.
The comments section in Ryan Bates' screencast was very helpful, and got me to figure out the subdomain => false and the other errors they were getting into. Still didn't fix the problem though!

Resources