1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
|
require 'active_support/core_ext/module/attribute_accessors'
module ActionDispatch
module Http
module MimeNegotiation
extend ActiveSupport::Concern
included do
mattr_accessor :ignore_accept_header
self.ignore_accept_header = false
end
# The MIME type of the HTTP request, such as Mime[:xml].
#
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
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
end
end
def content_type
content_mime_type && content_mime_type.to_s
end
def has_content_type?
has_header? 'CONTENT_TYPE'
end
# Returns the accepted MIME type for the request.
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
end
end
# 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
#
def format(view_path = [])
formats.first || Mime::NullType.instance
end
def formats
fetch_header("action_dispatch.request.formats") do |k|
params_readable = begin
parameters[:format]
rescue ActionController::BadRequest
false
end
v = if params_readable
Array(Mime[parameters[:format]])
elsif format = format_from_path_extension
Array(Mime[format])
elsif use_accept_header && valid_accept_header
accepts
elsif xhr?
[Mime[:js]]
else
[Mime[:html]]
end
set_header k, v
end
end
# Sets the \variant for template.
def variant=(variant)
variant = Array(variant)
if variant.all? { |v| v.is_a?(Symbol) }
@variant = ActiveSupport::ArrayInquirer.new(variant)
else
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols. " \
"For security reasons, never directly set the variant to a user-provided value, " \
"like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
"then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
end
end
def variant
@variant ||= ActiveSupport::ArrayInquirer.new
end
# 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
def format=(extension)
parameters[:format] = extension.to_s
set_header "action_dispatch.request.formats", [Mime::Type.lookup_by_extension(parameters[:format])]
end
# 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 fallback
# 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
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
# Receives an array of mimes and return the first user sent mime that
# matches the order array.
#
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
protected
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
def valid_accept_header
(xhr? && (accept.present? || content_mime_type)) ||
(accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
end
def use_accept_header
!self.class.ignore_accept_header
end
def format_from_path_extension
path = @env['action_dispatch.original_path'] || @env['PATH_INFO']
if match = path && path.match(/\.(\w+)\z/)
match.captures.first
end
end
end
end
end
|