20

Given my API consumers are required to send a customer HTTP header like this:

# curl -H 'X-SomeHeader: 123' http://127.0.0.1:3000/api/api_call.json

Then I can read this header in a before_filter method like this:

# app/controllers/api_controller.rb
class ApiController < ApplicationController
    before_filter :log_request

private
    def log_request
        logger.debug "Header: #{request.env['HTTP_X_SOMEHEADER']}"
        ...
    end
end

So far great. Now I would like to test this using RSpec as there is a change in behavior:

# spec/controllers/api_controller_spec.rb
describe ApiController do
    it "should process the header" do
        @request.env['HTTP_X_SOMEHEADER'] = '123'
        get :api_call
        ...
    end
end

However, the request received in ApiController will not be able to find the header variable.

When trying the same code with the HTTP_ACCEPT_LANGUAGE header, it will work. Are custom headers filtered somewhere?

PS: Some examples around the web use request instead of @request. While I'm not certain which one is correct as of the current Rails 3.2/RSpec 2.14 combination - both methods will not trigger the right behavior, BUT both work with HTTP_ACCEPT_LANGUAGE as well.

Sebastian Roth
  • 11,344
  • 14
  • 61
  • 110

3 Answers3

25

well, maybe too late for people but just to be lined up:

it 'should get profile when authorized' do
  user = FactoryGirl.create :user
  request.headers[EMAIL_TOKEN] = user.email
  request.headers[AUTH_TOKEN] = user.authentication_token
  get :profile
  response.should be success
end

just call request.headers with appropriate settings.

alexey_the_cat
  • 1,812
  • 19
  • 33
12

You can define it in get directly.

get :api_call, nil, {'HTTP_FOO'=>'BAR'}

I just verified it works in console.

Billy Chan
  • 24,625
  • 4
  • 52
  • 68
  • Sorry - your answer came in just as I tested this as well. Please see the update. Thank you. – Sebastian Roth Aug 26 '13 at 07:04
  • 3
    Thanks again - I've tried this as well and it will not work for me. Have you successfully tested this in a RSpec Controller spec? – Sebastian Roth Aug 26 '13 at 07:09
  • I request with this header, and then I ask for `request.env`, this header appears. `get` is Rails's `ActionDispatch::Integration::RequestHelpers` method, not Rspec. I did not do it in Rspec but I'm 100% sure it will work the same. – Billy Chan Aug 26 '13 at 07:11
  • As said, I've added `@request.env['HTTP_ACCEPT_LANGUAGE'] = 'en'` as well - this works and the variable can be read using `request.env`. Seems like there is a filter going which I'm not aware of. – Sebastian Roth Aug 26 '13 at 07:17
  • 1
    I think @BillyChan is correct the problem is you should not read the header from `request.env` but from `request.headers` http://api.rubyonrails.org/classes/ActionDispatch/Request.html#method-i-headers custom headers won't be included in `request.env` https://github.com/rails/rails/blob/bdf1a2e7b9096889321f7653a0ccca628a089e2f/actionpack/lib/action_dispatch/http/request.rb#L28-L43 – j03w Sep 07 '13 at 02:53
  • @BillyChan would you be able to test it in RSpec? – Sebastian Roth Aug 19 '14 at 05:52
  • for me, with Rails 4.2.0, it works without the "nil": get :api_call, {'HTTP_FOO'=>'BAR'} – Frédéric Mascaro Apr 10 '15 at 07:20
  • this is exactly what i'm trying to do ... I can specify certain headers ('Authorization: token token=...') but not others (my custom 'Session_Token' header). – max pleaner Nov 05 '15 at 01:50
7

RSpec request specs changed in Rails 5 so that custom headers and params must now be defined using key-value hash arguments. E.g.:

Before in Rails 4:

it "creates a Widget and redirects to the Widget's page" do
  headers = { "CONTENT_TYPE" => "application/json" }
  post "/widgets", '{ "widget": { "name":"My Widget" } }', headers
  expect(response).to redirect_to(assigns(:widget))
end

Now for Rails 5:

it "creates a Widget and redirects to the Widget's page" do
  headers = { "CONTENT_TYPE" => "application/json" }
  post "/widgets", :params => '{ "widget": { "name":"My Widget" } }', :headers => headers
  expect(response).to redirect_to(assigns(:widget))
end
Kelsey Hannan
  • 2,857
  • 2
  • 30
  • 46
  • My result - this just adds a "headers" section into params. The headers the server receives are always {"HTTPS"=>"off", "HTTP_HOST"=>"example.org", "HTTP_COOKIE"=>""} - and where those are defined, or how to alter them, is unknown. – JosephK Aug 16 '19 at 06:11