123456789_123456789_123456789_123456789_123456789_

Module: ActionDispatch::Http::MimeNegotiation

Relationships & Source Files
Namespace Children
Exceptions:
Extension / Inclusion / Inheritance Descendants
Included In:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Defined in: actionpack/lib/action_dispatch/http/mime_negotiation.rb

Constant Summary

Class Method Summary

::ActiveSupport::Concern - Extended

class_methods

Define class methods from given block.

included

Evaluate given block in context of base class, so that you can write class macros here.

prepended

Evaluate given block in context of base class, so that you can write class macros here.

append_features, prepend_features

Instance Attribute Summary

Instance Method Summary

DSL Calls

included

[ GitHub ]


19
20
21
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 19

included do
  mattr_accessor :ignore_accept_header, default: false
end

Instance Attribute Details

#formats (rw)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 72

def formats
  fetch_header("action_dispatch.request.formats") do |k|
    v = if params_readable?
      Array(Mime[parameters[:format]])
    elsif use_accept_header && valid_accept_header
      accepts.dup
    elsif extension_format = format_from_path_extension
      [extension_format]
    elsif xhr?
      [Mime[:js]]
    else
      [Mime[:html]]
    end

    v.select! do |format|
      format.symbol || format.ref == "*/*"
    end

    set_header k, v
  end
end

#formats=(extensions) (rw)

Sets the formats by string extensions. This differs from #format= by allowing you to set multiple, ordered formats, which is useful when you want to have a fallback.

In this example, the :iphone format will be used if it’s available, otherwise it’ll fall back to the :html format.

class ApplicationController < ActionController::Base
  before_action :adjust_format_for_iphone_with_html_fallback

  private
    def adjust_format_for_iphone_with_html_fallback
      request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
    end
end
[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 194

def formats=(extensions)
  parameters[:format] = extensions.first.to_s
  set_header "action_dispatch.request.formats", extensions.collect { |extension|
    Mime::Type.lookup_by_extension(extension)
  }
end

#has_content_type?Boolean (readonly)

This method is for internal use only.
[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 37

def has_content_type? # :nodoc:
  get_header "CONTENT_TYPE"
end

#params_readable?Boolean (readonly, private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 223

def params_readable?
  parameters[:format]
rescue *RESCUABLE_MIME_FORMAT_ERRORS
  false
end

#should_apply_vary_header?Boolean (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 214

def should_apply_vary_header?
  !params_readable? && use_accept_header && valid_accept_header
end

#variant (rw)

Returns the variant for the response template as an instance of ::ActiveSupport::ArrayInquirer.

request.variant = :phone
request.variant.phone?  # => true
request.variant.tablet? # => false

request.variant = [:phone, :tablet]
request.variant.phone?                  # => true
request.variant.desktop?                # => false
request.variant.any?(:phone, :desktop)  # => true
request.variant.any?(:desktop, :watch)  # => false
[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 159

def variant
  @variant ||= ActiveSupport::ArrayInquirer.new
end

#variant=(variant) (rw)

Sets the variant for the response template.

When determining which template to render, Action View will incorporate all variants from the request. For example, if an ArticlesController#index action needs to respond to request.variant = [:ios, :turbo_native], it will render the first template file it can find in the following list:

  • app/views/articles/index.html+ios.erb

  • app/views/articles/index.html+turbo_native.erb

  • app/views/articles/index.html.erb

Variants add context to the requests that views render appropriately. Variant names are arbitrary, and can communicate anything from the request’s platform (‘:android`, :ios, :linux, :macos, :windows) to its browser (`:chrome`, :edge, :firefox, :safari), to the type of user (`:admin`, :guest, :user).

Note: Adding many new variant templates with similarities to existing template files can make maintaining your view code more difficult.

#### Parameters

  • #variant - a symbol name or an array of symbol names for variants used to render the response template

#### Examples

class ApplicationController < ActionController::Base
  before_action :determine_variants

  private
    def determine_variants
      variants = []

      # some code to determine the variant(s) to use

      variants << :ios if request.user_agent.include?("iOS")
      variants << :turbo_native if request.user_agent.include?("Turbo Native")

      request.variant = variants
    end
end
[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 137

def variant=(variant)
  variant = Array(variant)

  if variant.all?(Symbol)
    @variant = ActiveSupport::ArrayInquirer.new(variant)
  else
    raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols."
  end
end

Instance Method Details

#accepts

Returns the accepted MIME type for the request.

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 42

def accepts
  fetch_header("action_dispatch.request.accepts") do |k|
    header = get_header("HTTP_ACCEPT").to_s.strip

    v = if header.empty?
      [content_mime_type]
    else
      Mime::Type.parse(header)
    end
    set_header k, v
  rescue ::Mime::Type::InvalidMimeType => e
    raise InvalidType, e.message
  end
end

#content_mime_type

The MIME type of the HTTP request, such as [Mime](:xml).

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 24

def content_mime_type
  fetch_header("action_dispatch.request.content_type") do |k|
    v = if get_header("CONTENT_TYPE") =~ /^([^,;]*)/
      Mime::Type.lookup($1.strip.downcase)
    else
      nil
    end
    set_header k, v
  rescue ::Mime::Type::InvalidMimeType => e
    raise InvalidType, e.message
  end
end

#format(_view_path = nil)

Returns the MIME type for the format used in the request.

# GET /posts/5.xml
request.format # => Mime[:xml]

# GET /posts/5.xhtml
request.format # => Mime[:html]

# GET /posts/5
request.format # => Mime[:html] or Mime[:js], or request.accepts.first
[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 68

def format(_view_path = nil)
  formats.first || Mime::NullType.instance
end

#format=(extension)

Sets the format by string extension, which can be used to force custom formats that are not controlled by the extension.

class ApplicationController < ActionController::Base
  before_action :adjust_format_for_iphone

  private
    def adjust_format_for_iphone
      request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
    end
end
[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 174

def format=(extension)
  parameters[:format] = extension.to_s
  set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
end

#format_from_path_extension (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 238

def format_from_path_extension
  path = get_header("action_dispatch.original_path") || get_header("PATH_INFO")
  if match = path && path.match(/\.(\w+)\z/)
    Mime[match.captures.first]
  end
end

#negotiate_mime(order)

Returns the first MIME type that matches the provided array of MIME types.

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 202

def negotiate_mime(order)
  formats.each do |priority|
    if priority == Mime::ALL
      return order.first
    elsif order.include?(priority)
      return priority
    end
  end

  order.include?(Mime::ALL) ? format : nil
end

#use_accept_header (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 234

def use_accept_header
  !self.class.ignore_accept_header
end

#valid_accept_header (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/http/mime_negotiation.rb', line 229

def valid_accept_header
  (xhr? && (accept.present? || content_mime_type)) ||
    (accept.present? && !accept.match?(BROWSER_LIKE_ACCEPTS))
end