Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Rails 4.2, How Does One Set the Response Content-Type Header With Media Type Parameters?

In earlier versions, this would work:

ActionController::Renderers.add(:foo) do | data, options |
  self.content_type = 'application/foo; bar=1'
end

In 4.2.4, this causes the Content-Type header to be blank. However, the following works, i.e., sets the Content-Type header to the string assigned to content_type:

ActionController::Renderers.add(:foo) do | data, options |
  self.content_type = 'application/foo'
end

The other approach I know of, setting content_type on the render, seems to have no result anymore, i.e., render('foo', content_type: 'application/foo') doesn't set the header (never mind trying application/foo; bar=1.)

like image 534
Yuri Gadow Avatar asked Sep 29 '15 02:09

Yuri Gadow


People also ask

What is content type header for?

The Content-Type representation header is used to indicate the original media type of the resource (prior to any content encoding applied for sending). In responses, a Content-Type header provides the client with the actual content type of the returned content.

Do I need a content type header for HTTP GET requests?

If a Content-Type header field is not present, the recipient MAY either assume a media type of "application/octet-stream" ([RFC2046], Section 4.5. 1) or examine the data to determine its type. It means that the Content-Type HTTP header should be set only for PUT and POST requests.

Does a GET request have a content type?

Since (by validity of the input spec) there are no content types for GET requests, you will always default to application/json .

How can you tell Rails to render without a layout?

2.2. By default, if you use the :plain option, the text is rendered without using the current layout. If you want Rails to put the text into the current layout, you need to add the layout: true option and use the . text. erb extension for the layout file.


2 Answers

First take a look at the documentation (Section 2.2.13.1):

http://guides.rubyonrails.org/layouts_and_rendering.html#using-render

The example they give there uses your alternative approach, setting content_type when using render:

render file: filename, content_type: "application/rss"

I tested this strategy in a vanilla Rails 4.2.4 application. This is how I defined the controller:

class WelcomeController < ApplicationController
  def index
    render inline: 'Hello World', content_type: 'application/foo; bar=1'
  end
end

And here is what I see in Chrome's network inspector when I hit that action, note the Content-Type under Response Headers:

General

Remote Address:[::1]:3000
Request URL:http://localhost:3000/
Request Method:GET
Status Code:200 OK

Response Headers

Cache-Control:max-age=0, private, must-revalidate
Connection:Keep-Alive
Content-Length:11
Content-Type:application/foo; bar=1; charset=utf-8
Date:Tue, 29 Sep 2015 02:53:39 GMT
Etag:W/"b10a8db164e0754105b7a99be72e3fe5"
Server:WEBrick/1.3.1 (Ruby/2.2.2/2015-04-13)
X-Content-Type-Options:nosniff
X-Frame-Options:SAMEORIGIN
X-Request-Id:3825d446-44dc-46fa-8aed-630dc7f001ae
X-Runtime:0.022774
X-Xss-Protection:1; mode=block
like image 161
Sean Huber Avatar answered Sep 30 '22 08:09

Sean Huber


Sean Huber is correct and the code in the question is correct. Except in the case of having registered a MIME type and then rendering files of that type, e.g.,

Mime::Type.register('application/foobar', :foobar)

render('view') # where view is actually view.foobar.jbuilder

In this case, the type string registered appears to always override the approaches available for setting content type explicitly. This can lead to thinking that the media type parameters are being stripped because, coincidentally, ParamsParser selection appears to "break" when media type parameters are specified for a default parser, i.e., a parser registered for 'application/foo; bar=1' will not parse content provided with that content type, leading one to use a parameterless string for the mime type string and then attempting to override with one including the parameters.

So, to get around that in 4.2, I've removed the ParamsParser and Render registrations and moved to a before_filter on a parent controller and the content type header to an after_filter, e.g.,

class BaseController < ApplicationController
  before_filter :parse_body
  after_filter :set_content_type

  attr_accessor :parsed

  def parse_body
    self.parsed = JSON.load(request.body)
  end

  def set_content_type
    self.content_type = "application/foo; bar=1; charset=utf-8"
  end
end

Note: this is quite the hack/workaround; not recommended for long-term use.

like image 24
Yuri Gadow Avatar answered Sep 30 '22 08:09

Yuri Gadow