HEAD HTTP requests in Rails 3 - ruby-on-rails

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.

Related

How not to send any data to client?

I use Ruby-on-Rails in my project and there is a functionality to delete the whole data from table in one click. And in that case, I don't want to send any data to client from server. To do that, I wrote this part of code:
render :json => {}
Is there a better way to solve this problem?
I would use
head :ok
which returns an empty response with the HTTP status code 200 (OK).
Docs about head.

Stubbing with WebMock not intercepting request

I'm attempting to stub a request to a Controller using WebMock. However, as I'm creating the stub, the request isn't being intercepted the way I'd expect or want it to be.
The Controller does nothing but render JSON based on the query parameter:
def index
render json: MyThing.search(params[:query]).as_json(only: [:id], methods: [:name_with_path])
end
And the stubbing goes as follows:
mything_val = { ...json values... }
stub_request(:any, mything_path).with(query: { "query" => "a+thing" }).to_return(body: mything_val, status: 200)
page.find('.MyThingInput > input').set('a thing')
# Note: I've tried this with and without the `query:` parameter, as well as
with and without specifying header info.
This is triggering a React component. What it does is, when a word or words are entered into the input, it sends an AJAX request to mything_path with the inputted value, which returns as JSON several suggestions as to what the user might mean. These are in a li element within .MyThingInput-wrapper.
In the spec file, I include:
require 'support/feature_helper'
require 'support/feature_matchers'
require 'webmock/rspec'
WebMock.disable_net_connect!
What's actually happening when I input the text into the React component however is that regardless of the WebMock stub, it's hitting the Controller, making the DB request, and failing due to some restrictions of the testing environment. My understanding of how this should work is that when the request is made to mything_url, it should be intercepted by WebMock which would return the values I pre-defined, and never hit the Controller at all.
My guess is that somehow I'm mocking the wrong URI, but honestly, at this point I'm really uncertain. Any and all input is appreciated, and I'm happy to clarify any points I've made here. Thanks massively!
What ended up solving my problem was stubbing out the model. I'd tried stubbing the Controller but ran into issues; however, this code did the trick:
before do
mything_value = [{ "id" => "fb6135d12-e5d7-4e3r-b1h6-9bhirz48616", "name_with_path" => "New York|USA" }]
allow(MyThing).to receive(:search).and_return(mything_value.to_json)
end
This way, it still hits the controller but stubs out the DB query, which was the real problem because it made use of Elasticsearch (not running in test mode.)
I'm not super happy about hard-coding the JSON like that, but I've tried a few other methods without success. Honestly, at this point I'm just going with what works.
Interestingly enough, I'd tried this method before Infused's suggestion, but couldn't quite get the syntax right; same went with stubbing out the Controller action. Went to bed, woke up, tried it again with what I thought was the same syntax, and it worked. I'm just going to slowly back away and thank the code gods.
If elastic search is the problem, then maybe try
installing Webmock
# in your gemfile
group :test do
gem 'webmock'
end
stubbing out the requests to elasticsearch and returning the JSON
Something like this in spec_helper:
config.before(:each) do
WebMock.enable!
WebMock.stub_request(:get, /#{ELASTICSEARCH_URL}/).to_return(body: File.read('spec/fixtures/elasticsearch/search-res.json'))ELASTICSEARCH_URL
# and presumably, if you are using elasticsearch-rails, you'd want to stub out the updating as well:
WebMock.stub_request(:post, /#{ELASTICSEARCH_URL}/).to_return(status: "200")
WebMock.stub_request(:put, /#{ELASTICSEARCH_URL}/).to_return(status: "200")
WebMock.stub_request(:delete, /#{ELASTICSEARCH_URL}/).to_return(status: "200")
end
Of course, this stubs out all calls to elastic-search and returns the same JSON for all answers. Dig into the documentation for webmock if you need a different response for each query.

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

Why does Rails render templates for HEAD requests?

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

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.

Resources