Skip to content

Commit a15db09

Browse files
etiennebarriebyroot
authored andcommitted
Add a config.action_controller.json_renderer_escapes framework default
1 parent f0b8238 commit a15db09

File tree

6 files changed

+45
-7
lines changed

6 files changed

+45
-7
lines changed

actionpack/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
Escaping will still occur when the `:callback` option is set, since the JSON is used as JavaScript code in this
1010
situation (JSONP).
1111

12-
You can use the `:escape` option to restore the escaping behavior.
12+
You can use the `:escape` option or set `config.action_controller.json_renderer_escapes` to `true` to restore the
13+
escaping behavior.
1314

1415
```ruby
1516
class PostsController < ApplicationController

actionpack/lib/action_controller/metal/renderers.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ module Renderers
2929

3030
included do
3131
class_attribute :_renderers, default: Set.new.freeze
32+
class_attribute :json_renderer_escapes, instance_accessor: false, default: true
3233
end
3334

3435
# Used in ActionController::Base and ActionController::API to include all
@@ -153,7 +154,7 @@ def _render_to_body_with_renderer(options) # :nodoc:
153154

154155
add :json do |json, options|
155156
json_options = options.except(:callback, :content_type, :status)
156-
json_options[:escape] ||= false unless options[:callback].present?
157+
json_options[:escape] ||= false if !self.class.json_renderer_escapes? && options[:callback].blank?
157158
json = json.to_json(json_options) unless json.kind_of?(String)
158159

159160
if options[:callback].present?

actionpack/test/controller/render_json_test.rb

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require "abstract_unit"
44
require "controller/fake_models"
55
require "active_support/logger"
6+
require "active_support/core_ext/object/with"
67

78
class RenderJsonTest < ActionController::TestCase
89
class JsonRenderable
@@ -120,10 +121,12 @@ def test_render_json_with_callback_escapes_js_chars
120121
assert_equal "text/javascript", @response.media_type
121122
end
122123

123-
def test_render_json_without_callback_does_not_escape_js_chars
124-
get :render_json_unsafe_chars_without_callback
125-
assert_equal %({"hello":"\u2028\u2029<script>"}), @response.body
126-
assert_equal "application/json", @response.media_type
124+
def test_render_json_with_new_default_and_without_callback_does_not_escape_js_chars
125+
TestController.with(json_renderer_escapes: false) do
126+
get :render_json_unsafe_chars_without_callback
127+
assert_equal %({"hello":"\u2028\u2029<script>"}), @response.body
128+
assert_equal "application/json", @response.media_type
129+
end
127130
end
128131

129132
def test_render_json_with_custom_content_type
@@ -157,6 +160,6 @@ def test_render_json_calls_to_json_from_object
157160

158161
def test_render_json_avoids_view_options
159162
get :render_json_inspect_options
160-
assert_equal '{"options":{"escape":false}}', @response.body
163+
assert_equal '{"options":{}}', @response.body
161164
end
162165
end

guides/source/configuring.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Below are the default values associated with each target version. In cases of co
6060

6161
#### Default Values for Target Version 8.1
6262

63+
- [`config.action_controller.json_renderer_escapes`](#config-action-controller-json-renderer-escapes): `false`
6364
- [`config.yjit`](#config-yjit): `!Rails.env.local?`
6465

6566
#### Default Values for Target Version 8.0
@@ -1964,6 +1965,16 @@ The default value depends on the `config.load_defaults` target version:
19641965
Configures the [`ParamsWrapper`](https://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html). This can be called at
19651966
the top level, or on individual controllers.
19661967

1968+
#### `config.action_controller.json_renderer_escapes`
1969+
1970+
Configures the JSON renderer to escape HTML entities and Unicode characters that are invalid in JavaScript.
1971+
1972+
This is useful if you relied on the JSON response having those characters escaped to embed the JSON document in
1973+
\<script> tags in HTML.
1974+
1975+
This is mainly for compatibility when upgrading Rails applications, otherwise you can use the `:escape` option for
1976+
`render json:` in specific controller actions.
1977+
19671978
### Configuring Action Dispatch
19681979

19691980
#### `config.action_dispatch.cookies_serializer`

railties/lib/rails/application/configuration.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ def load_defaults(target_version)
355355
# redefine methods (e.g. mocking), hence YJIT isn't generally
356356
# faster in these environments.
357357
self.yjit = !Rails.env.local?
358+
359+
if respond_to?(:action_controller)
360+
action_controller.json_renderer_escapes = false
361+
end
358362
else
359363
raise "Unknown version #{target_version.to_s.inspect}"
360364
end

railties/lib/rails/generators/rails/app/templates/config/initializers/new_framework_defaults_8_1.rb.tt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,21 @@
88
#
99
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
1010
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
11+
12+
###
13+
# Skips escaping HTML entities and line separators. When set to `false`, the
14+
# JSON renderer no longer escapes these to improve performance.
15+
#
16+
# Example:
17+
# class PostsController < ApplicationController
18+
# def index
19+
# render json: { key: "\u2028\u2029<>&" }
20+
# end
21+
# end
22+
#
23+
# Renders `{"key":"\u2028\u2029\u003c\u003e\u0026"}` with the previous default, but `{"key":"

<>&"}` with the config
24+
# set to `false`.
25+
#
26+
# Applications that want to keep the escaping behavior can set the config to `true`.
27+
#++
28+
# Rails.configuration.action_controller.json_renderer_escapes = false

0 commit comments

Comments
 (0)