1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/lib/action_controller/metal/rendering.rb

126 lines
3.3 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
module ActionController
module Rendering
extend ActiveSupport::Concern
RENDER_FORMATS_IN_PRIORITY = [:body, :plain, :html]
2014-02-14 10:45:46 -05:00
module ClassMethods
2015-01-21 16:23:22 -05:00
# Documentation at ActionController::Renderer#render
delegate :render, to: :renderer
# Returns a renderer instance (inherited from ActionController::Renderer)
# for the controller.
attr_reader :renderer
def setup_renderer! # :nodoc:
@renderer = Renderer.for(self)
end
def inherited(klass)
klass.setup_renderer!
super
end
end
# Before processing, set the request formats in current controller formats.
def process_action(*) #:nodoc:
2013-04-10 03:18:06 -04:00
self.formats = request.formats.map(&:ref).compact
super
end
# Check for double render errors and set the content_type after rendering.
def render(*args) #:nodoc:
raise ::AbstractController::DoubleRenderError if response_body
super
end
# Overwrite render_to_string because body can now be set to a Rack body.
def render_to_string(*)
result = super
if result.respond_to?(:each)
string = +""
result.each { |r| string << r }
string
else
result
end
end
def render_to_body(options = {})
super || _render_in_priorities(options) || " "
end
private
def _process_variant(options)
if defined?(request) && !request.nil? && request.variant.present?
options[:variant] = request.variant
end
end
def _render_in_priorities(options)
RENDER_FORMATS_IN_PRIORITY.each do |format|
return options[format] if options.key?(format)
end
2014-02-14 10:45:46 -05:00
nil
end
2014-02-14 10:45:46 -05:00
def _set_html_content_type
self.content_type = Mime[:html].to_s
end
def _set_rendered_content_type(format)
if format && !response.media_type
self.content_type = format.to_s
end
end
Add `Vary: Accept` header when rendering Problem description (quoted from @rafaelfranca's excellent explanation in https://github.com/rails/jquery-ujs/issues/318#issuecomment-88129005): > Let say that we requested /tasks/1 using Ajax, and the previous page has the same url. When we click the back button the browser tries to get the response from its cache and it gets the javascript response. With vary we "fix" this behavior because we are telling the browser that the url is the same but it is not from the same type what will skip the cache. And there's a Rails issue discussing about this problem as well https://github.com/rails/rails/issues/25842 Also, according to [RFC 7231 7.1.4](https://tools.ietf.org/html/rfc7231#section-7.1.4) > An origin server SHOULD send a Vary header field when its algorithm > for selecting a representation varies based on aspects of the request > message other than the method and request target we should add `Vary: Accept` header when determining content based on the `Accept` header. Although adding such header by default could cause unnecessary cache invalidation. But this PR only adds the header if: - The format param is not provided - The request is a `xhr` request - The request has accept headers and the headers are valid So if the user - sends request with explicit format, like `/users/1.json` - or sends a normal request (non xhr) - or doesn't specify accept headers then the header won't be added. See the discussion in https://github.com/rails/rails/issues/25842 and https://github.com/rails/rails/pull/36213 for more details.
2019-05-08 10:28:47 -04:00
def _set_vary_header
self.headers["Vary"] = "Accept" if request.should_apply_vary_header?
end
# Normalize arguments by catching blocks and setting them on :update.
2016-12-23 23:26:29 -05:00
def _normalize_args(action = nil, options = {}, &blk)
options = super
options[:update] = blk if block_given?
options
end
# Normalize both text and status options.
2016-12-23 23:26:29 -05:00
def _normalize_options(options)
_normalize_text(options)
if options[:html]
options[:html] = ERB::Util.html_escape(options[:html])
end
if options[:status]
options[:status] = Rack::Utils.status_code(options[:status])
end
super
end
def _normalize_text(options)
RENDER_FORMATS_IN_PRIORITY.each do |format|
if options.key?(format) && options[format].respond_to?(:to_text)
options[format] = options[format].to_text
end
2014-02-14 10:45:46 -05:00
end
end
# Process controller specific options, as status, content-type and location.
2016-12-23 23:26:29 -05:00
def _process_options(options)
status, content_type, location = options.values_at(:status, :content_type, :location)
self.status = status if status
self.content_type = content_type if content_type
headers["Location"] = url_for(location) if location
super
end
end
end