Why does Rails render templates for HEAD requests? - ruby-on-rails

For HEAD requests, Rails seems to perform all the usual steps including rendering templates and then discards the respective output (sends back an empty response).
I can't think of any way that rendering templates in case of a HEAD request makes any sense (unless you have some actual business logic that gets executed in the templates, but that should never be the case).
So my question would be: Do you agree with me or are there any possible side-effects I didn't foresee?
The reason I'm asking is because I'm thinking of filing a Rails issue and possibly submit a pull request for a feature which disables default (non-explicit) template rendering for HEAD requests.

Good point Remo, however, I am not completely agree.
For every http verb, we manually need to write code to handle things. Similar thing with HEAD. HEAD request will follow execution style of GET request unless we don't handle it.
An example can be:
def index
if request.head?
head :created
else
# handle GET request
Rails.logger.info "Derp #{request.method}"
end
end

Related

Issue while processing HEAD and GET request in rails 3

Currently we are facing issue for processing the HEAD and GET request. Let me explain the detailed scenario
We have integrated inbound and outbound SMS facility in our application.
But from last 2-3 months we are getting 2-3 times GET request from the SMS service provider and it is affecting on our system.
After long discussion with SMS service provider, they are saying "Both Head and Get requests are handled similarly from your end"
I also referred this link. You can find respective logs at this link
So can any one suggest how to resolve this issue.
EDIT
After research we found that we are getting all the parameters in both HEAD and GET request because of this server is processing it.
I think the problem might be the ActionDispatch::Head middleware.
Part of that is following code:
def call(env)
if env["REQUEST_METHOD"] == "HEAD"
env["REQUEST_METHOD"] = "GET"
env["rack.methodoverride.original_method"] = "HEAD"
status, headers, _ = #app.call(env)
[status, headers, []]
else
#app.call(env)
end
end
So essentially the middleware changes the request-method before the router even gets the request.
If you want your router to handle the difference between HEAD and GET requests, you can remove the middleware by adding
config.middleware.delete "ActionDispatch::Head"
to your application.rb
Otherwise you should be able to access that variable in your controller like this:
if request.env["rack.methodoverride.original_method"]=='HEAD'
#do head processing here
head :ok, :additional_header => 'value'
else
#do get processing here
end
If you worry about performance, I suggest writing your own middleware to handle these requests. Railscasts has a few good tutorials on this.
Also note, that other middlewares, like Rack::Cache might interfere in this process as well. So you should insert your middleware on top:
config.middleware.insert_before 0, "YourMiddleware"
I would just implement my own :head response as the example in https://stackoverflow.com/a/10453525/5446866
if request.head?
head :ok # or whatever
else
# your other complex stuff here
end
You could also add a route specific to head request. for example
match '/validate_messages/sms' => 'validate_messages#noop', via: :head
and then in your controller
def noop
head :ok
end
basically, you have to implement what you want to do with HEAD request otherwise it will go ahead and use your GET handler
Hope that helps

Using response_with in Rails, how can I prevent execution of create action on not accepted MIME types?

I'm building a RESTful API using Rails 3.2.21. The API should only response to xml or json for now. I have a simple resource called Users with a create action, that creates a new user on a POST Request.
Here is the Code for doing that:
respond_to :json, :xml
def create
#user = User.new(params[:user])
#user.save
respond_with(#user)
end
Everything goes fine so far, but then I tried to check the error cases. So if I do a POST request to /users.html, the answer is '406 Not Acceptable'. What is correct. But then I saw in the database, that the user was created anyway. So the create action is executed although the requested accept format (html) is not supported and a 406 error is responded.
I don't know if this is intended. Until now I really liked response_with from Rails, because it does lots of stuff for me, but this behaviour seems to be odd. From the perspective of a client you try to create a new user, receive 406 and you obviously assume that the request failed, so the user is not created, right?
Since I've defined the accepted MIME types in the class method respond_to, it should be possible for Rails, to prevent execution of the entire action. Sure, respond_to is only related to the http response, but then for the client it is still unknown which part of the POST request succeeded and which failed.
Are there any setting or additional functions in Rails what I've overseen or is this just 'not thought to the end' in case of POST requests using respond_with or is this behaviour even intended to react like that? Of course I can add some custom before_filters for checking the MIME types, but then I can also remove respond_with and handle everything on my own.
I'm looking for a nice and clean solution for that problem using respond_with.

Create a generic template for JSON from a rails application

I'm writing a rails application with an AngularJS front-end, this is part of a tutorial series I'm writing on connecting rails and angularjs. This means my rails application communicates with the browser exclusively in JSON.
In the angularjs $http documentation it describes a potential json security vulnerability where the json request can be embedded into a script tag, plus some tricky use of jsonp, to allow something akin to a cross-site scripting attack. I've found a few other pages, one in particular I thought described this well, and dates from 2008, so this isn't a new issue.
Apparently this isn't a vulnerability in standard rails json rendering, as rails by default provides back an object containing an array. But when working with angularjs we appear to set root: false (although I have to confess I can't find where I did that, but it's definitely not giving the root node).
Anyway, the bottom line is that the angular documentation recommends prefixing any json response with )]}', so:
['one','two']
Becomes
)]}',
['one','two']
Angular then automatically strips that off again.
I'm looking for a way to do this elegantly. I've seen a lot of questions and answers on stackoverflow about this, but most of those either relate to much earlier versions of rails before JSON handling was more thoroughly embedded, or seem to require me to create a lot of boilerplate code. I'm looking for a method that I can apply to the application controller, or as a helper method, that will work everywhere.
The controller that I'm currently using looks as follows:
class ClubsController < ApplicationController
respond_to :json
# GET /clubs.json
def index
#clubs = Club.all
render json: #clubs
end
end
This doesn't call any templates - the render action skips the templating engine. I can get this working by changing the render line instead to:
respond_with json: #clubs
And creating a template file views/clubs/index.json.erb that contains
)]}',
<%= raw(#clubs.to_json) %>
But I'd then have to create a template for every action on every controller, which feels like boilerplate. I'd like instead to be able to change views/layouts/application.json.erb to have something like:
)]}',
<%= yield %>
But that doesn't work because we only get templating if we call respond_with. And if we call respond_with, we have no way to put the #clubs into the response - so we end up with:
)]}',
As the entirety of the response.
An alternative would perhaps be to override the as_json method to prepend what I want, but that seems a bit like a sledgehammer. Ideally there would be a place I could introduce a helper method, something like:
render prepend_vulnerability_protection(json: #clubs)
So, after all that, two questions:
Is this even a real problem, or does Rails already have some other protection that means I don't need to worry about this at all
Is there a way to do this centrally, or do I need to bite the bullet and create all the boilerplate templates? I can modify the scaffold generators to do it, so it's not the end of the world, but it does seem like a lot of boilerplate code
So, no responses as yet. I'm going to write down what I find from my research, and my current answer.
Firstly, I think this is a genuine vulnerability in rails. Unfortunately the rails and JSON/JSONP area has had some other recent vulnerabilities relating to the JSON parser at the Rails end. That has really drowned out any google search relating to this specific XSS issue.
There are a couple of approaches to resolving this:
Have your application only respond to put/post/delete requests. That's not really an option when integrating to Angular - well, it is, but it means overriding a bunch of standard behaviour
Insert something at the front of your returned JSON - this can be the root node (default rails behaviour in rails 3, no longer in 3.1), a closure like )]};, or a loop like while (1);. Angular expects and can deal with )]}',
I've looked at using a json template in my rails app. You can do this with one of many gems, the one I like the look of is JBuilder (railscast 320), but RABL is perhaps more powerful (railscast 322).
This does mean a template for each of the actions on each of the controllers. However, I've also just completed working out how to have rails scaffold those for me automatically, so it's not as scary as it was when I first asked the question, and I can see some other reasons that I might want more control over the json that is returned from my application.
Having said that, I couldn't immediately see a way to get JBuilder to prepend an arbitrary string - it seems to only want to prepare valid JSON (and this I think is not valid JSON). RABL looks like it can do it, but it is a bit more complex. It can definitely be done through just using ERB, but I feel kinda wrong in doing that.
The other alternative I've identified is a helper method in application_controller.rb, which I then call in each of my controller methods. This is reasonably elegant, and I can quite easily change my template to do it. So I'm going with this for now:
class ApplicationController < ActionController::Base
def render_with_protection(json_content, parameters = {})
render parameters.merge(content_type: 'application/json', text: ")]}',\n" + json_content)
end
end
class ClubsController < ApplicationController
respond_to :json
# GET /clubs.json
def index
#clubs = Club.all
render_with_protection #clubs.to_json
end
# GET /clubs/1.json
def show
#club = Club.find(params[:id])
render_with_protection #club.to_json
end
# POST /clubs.json
def create
#club = Club.new(params[:club])
if #club.save
render_with_protection #club.to_json, {status: :created, location: #club}
else
render_with_protection #club.errors.to_json, {status: :unprocessable_entity}
end
end
end
Note that you should be also including CSRF protection in your application controller - so see this as additive to the security precautions you were already taking, not a replacement.

HEAD HTTP requests in Rails 3

Rails 3 currently routes a HEAD request to the matching GET route. There is a head? method on the request, but in returns false and the request acts like a get request. Can I detect if the request is a HEAD request?
Reasoning: I get that a HEAD request should return the EXACT same headers as the get, so Rails wants to perform the full GET and then shave off the body. However, I can conform to this request without issuing the same DB calls, etc that the GET would. Does this make sense?
I had this exact issue. It turns out that enabling caching causes this. Turn caching off in your environment and #head? will work as expected.
The issue is that Rack::Cache turns HEAD requests into GET requests so they can be cached. This is arguably the correct behavior, but it interfered with my application.
You can use the request.head? method to find out if it's a HEAD request:
http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-head-3F
Once you've determined that it is, you can also use the controller's head() method instead of the typical render:
http://guides.rubyonrails.org/layouts_and_rendering.html#using-head-to-build-header-only-responses
So I'd simply check request.head? before bothering with the database activities. Then use
head :ok, :custom_header => 'value'
def index
if request.head?
head :created
else
Rails.logger.info "Derp #{request.method}"
end
end
Hmm. The above controller method works like I'd expect on Ruby v1.9.3-p194 and Rails v3.2.3; 201's w/o response body for the HEAD requests and 200's w/ for the GET's.

Rails resource_controller with interruption?

I want to use the before action to optionally interrupt the processing if it fails to comply certain criteria (e.g. the object is not owned by such user etc)
I need to use this because I have a more complicated scenario that I
need the object to be loaded before the access rights could be
determined, so I would have situations where I want to interrupt the
action if it is invalid access, anyway I could achieve this?
OK, this is something I was thinking about myself when working with RC.
the usual RC action is something like:
def show
load_object
before :show
response_for :show
rescue ActiveRecord::RecordNotFound
response_for :show_fails
end
So suppose you want to interrupt the show just after load_object if some conditions fail.
The best way to do it that I could think of (except for modifying RC :) is:
use before_filter to check the condition
in the the before_filter use the object or collection helpers (according to the action). this way the load_object/load_collection in the RC action implementation will use the same value cached from your usage of the helper so no extra queries will be made.
Assuming you are referring to before_filter:
Any render or redirect call should abort the filter chain and the execution of the action. So simply put your access control filter after the one to load 'stuff', then render an error message (with an appropriate status code for good web karma and to prevent a w3c beat down).
It's just another area of rails that works fine as long as you don't think too hard.
Any help or am I missing the point?
Vitaly's approach is probably correct, but I have a interesting over-engineered approach too, so to post as a reference:
Use around_filter
At the before hook, throw AccessDeniedException
Capture the exception in the around filter
That would do the job as well.
If you find yourself writing before_filters for precondition checks very often, you might find Aegis useful. It allows you to define access rules in a single file so you can see at a glance who may access what.
It was also built for easy integration with resource_controller.

Resources