Каков наилучший способ преобразования всех параметров контроллера из camelCase в snake_case в Rails?
Как вы уже знаете, соглашение об именах JSON защищает использование camelSpace, а Rails защищает использование snake_case для имен параметров.
Каков наилучший способ преобразования всех параметров запроса в snake_case в контроллере rails?
Из этого:
{
...
"firstName": "John",
"lastName": "Smith",
"moreInfo":
{
"mealType": 2,
"mealSize": 4,
...
}
}
:
{
...
"first_name": "John",
"last_name": "Smith",
"more_info":
{
"meal_type": 2,
"meal_size": 4,
...
}
}
Спасибо
Ответы
Ответ 1
Когда вы выполните приведенные ниже шаги, camelCase
имена параметров, представленные через запросы JSON, будут изменены на snake_case
.
Например, параметр запроса JSON с именем passwordConfirmation
будет доступен в контроллере как params[:password_confirmation]
Создайте инициализатор в config/initializers/json_param_key_transform.rb
. Этот файл будет изменять поведение разбора параметров только для запросов JSON (запросы JSON должны иметь заголовок запроса Content-Type: application/json
).
Найдите версию Rails и выберите соответствующий раздел ниже (найдите версию Rails в Gemfile.lock
):
Только для Rails 5
Для Rails 5, чтобы преобразовать ключи params camel-case в змеиный случай, поместите это в инициализатор:
# File: config/initializers/json_param_key_transform.rb
# Transform JSON request param keys from JSON-conventional camelCase to
# Rails-conventional snake_case:
ActionDispatch::Request.parameter_parsers[:json] = -> (raw_post) {
# Modified from action_dispatch/http/parameters.rb
data = ActiveSupport::JSON.decode(raw_post)
data = {:_json => data} unless data.is_a?(Hash)
# Transform camelCase param keys to snake_case:
data.deep_transform_keys!(&:underscore)
}
Для Rails 4.2 (и, возможно, более ранних версий)
Для Rails 4.2 (и, возможно, более ранних версий), чтобы преобразовать ключи params camel-case в змеиный случай, поместите это в инициализатор:
# File: config/initializers/json_param_key_transform.rb
# Transform JSON request param keys from JSON-conventional camelCase to
# Rails-conventional snake_case:
Rails.application.config.middleware.swap(
::ActionDispatch::ParamsParser, ::ActionDispatch::ParamsParser,
::Mime::JSON => Proc.new { |raw_post|
# Borrowed from action_dispatch/middleware/params_parser.rb except for
# data.deep_transform_keys!(&:underscore) :
data = ::ActiveSupport::JSON.decode(raw_post)
data = {:_json => data} unless data.is_a?(::Hash)
data = ::ActionDispatch::Request::Utils.deep_munge(data)
# Transform camelCase param keys to snake_case:
data.deep_transform_keys!(&:underscore)
data.with_indifferent_access
}
)
Конечный шаг для всех версий Rails
Перезапустите rails server
.
Ответ 2
ActiveSupport уже предоставляет метод snarkecase String #. Все, что вам нужно сделать, это установить фильтр, который выполняет глубокую итерацию с помощью хэша params и заменяет ключи key.snakecase
.
before_filter :deep_snake_case_params!
def deep_snake_case_params!(val = params)
case val
when Array
val.map {|v| deep_snake_case_params! v }
when Hash
val.keys.each do |k, v = val[k]|
val.delete k
val[k.snakecase] = deep_snake_case_params!(v)
end
val
else
val
end
end
Ответ 3
Объединив ответ Себастьяна Хойца с этот смысл, я мог бы заставить его работать с рельсами 4.2, сильными параметрами и параметрами, обертывающими с помощью wrap_parameters
метод тегов.
Я не мог заставить его работать с помощью before_filter
, возможно, потому, что перенос параметров выполняется до фильтрации.
В config/initializers/wrap_parameters.rb
:
# Convert json parameters, sent from Javascript UI, from camelCase to snake_case.
# This bridges the gap between javascript and ruby naming conventions.
module ActionController
module ParamsNormalizer
extend ActiveSupport::Concern
def process_action(*args)
deep_underscore_params!(request.parameters)
super
end
private
def deep_underscore_params!(val)
case val
when Array
val.map {|v| deep_underscore_params! v }
when Hash
val.keys.each do |k, v = val[k]|
val.delete k
val[k.underscore] = deep_underscore_params!(v)
end
val
else
val
end
end
end
end
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
ActiveSupport.on_load(:action_controller) do
wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
# Include the above defined concern
include ::ActionController::ParamsNormalizer
end
Ответ 4
Пример использования camelCase для snake_case в консоли rails
2.3.1 :001 > params = ActionController::Parameters.new({"firstName"=>"john", "lastName"=>"doe", "email"=>"[email protected]"})
=> <ActionController::Parameters {"firstName"=>"john", "lastName"=>"doe", "email"=>"[email protected]"} permitted: false>
2.3.1 :002 > params.transform_keys(&:underscore)
=> <ActionController::Parameters {"first_name"=>"john", "last_name"=>"doe", "email"=>"[email protected]"} permitted: false>
источник:
http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-transform_keys http://apidock.com/rails/String/underscore
Ответ 5
Решение для Rails 5
before_action :underscore_params!
def underscore_params!
underscore_hash = -> (hash) do
hash.transform_keys!(&:underscore)
hash.each do |key, value|
if value.is_a?(ActionController::Parameters)
underscore_hash.call(value)
elsif value.is_a?(Array)
value.each do |item|
next unless item.is_a?(ActionController::Parameters)
underscore_hash.call(item)
end
end
end
end
underscore_hash.call(params)
end
Ответ 6
Другое решение для рельсов 5.1, которое поросенок отступает от решения Sebastian Hoitz выше. Чтобы уточнить, зачем нам это нужно: в R5.1 deep_transform_keys! больше не является методом, доступным для нас, поскольку params больше не наследуются от HashWithIndifferentAccess. И преодолевает проблему, упомянутую Элиотом Сайкесом, где инициализатор работает только для приложений /json mime. Тем не менее, он добавляет накладные расходы на все запросы. (Мне бы хотелось увидеть некоторые инициализаторы для ActionDispatch::Request.parameter_parsers[:multipart_form]
), хотя, поскольку инициализатор - лучшее место для этого, IMO.
before_action :normalize_key!
def normalize_keys!(val = params)
if val.class == Array
val.map { |v| normalize_keys! v }
else
if val.respond_to?(:keys)
val.keys.each do |k|
current_key_value = val[k]
val.delete k
val[k.to_s.underscore] = normalize_keys!(current_key_value)
end
end
val
end
val
end
Ответ 7
Я хотел использовать версию Chris Healds, но поскольку я использую Rails 4, у меня есть сильные_параметры, поэтому мне пришлось немного изменить его.
Это версия, с которой я столкнулся:
before_filter :deep_underscore_params!
def deep_underscore_params!(val = request.parameters)
case val
when Array
val.map { |v| deep_underscore_params!(v) }
when Hash
val.keys.each do |k, v = val[k]|
val.delete k
val[k.underscore] = deep_underscore_params!(v)
end
params = val
else
val
end
end
Ответ 8
Мы конвертируем наши ключи JSON Rails API из snake_case в camelCase. Мы должны делать преобразование постепенно, т.е. Некоторые API работают с snake_case, а другие - с помощью camelCase.
Наше решение состоит в том, что мы
- создать метод
ActionController::Parameters#deep_snakeize
- создать метод
ApplicationController#snakeize_params
- установить
before_action :snakeize_params
только для действий контроллера, которые обрабатывают входящий запрос с помощью клавиш camelCase.
Вы можете попробовать vochicong/rails-json-api для полностью работающего примера приложения Rails.
# File: config/initializers/params_snakeizer.rb
# Transform JSON request param keys from JSON-conventional camelCase to
# Rails-conventional snake_case
module ActionController
# Modified from action_controller/metal/strong_parameters.rb
class Parameters
def deep_snakeize!
@parameters.deep_transform_keys!(&:underscore)
self
end
end
end
# File: app/controllers/application_controller.rb
class ApplicationController < ActionController::API
protected
# Snakeize JSON API request params
def snakeize_params
params.deep_snakeize!
end
end
class UsersController < ApplicationController
before_action :snakeize_params, only: [:create]
# POST /users
def create
@user = User.new(user_params)
if @user.save
render :show, status: :created, location: @user
else
render json: @user.errors, status: :unprocessable_entity
end
end
end
Ответ 9
Я не мог использовать другие предложения здесь напрямую, но это привело меня на правильный путь.
С Rails 5.2, используя версионный API и, следовательно, не может изменить его для всего приложения. Я создал эту проблему, которую затем включил в базовый контроллер моего нового модуля версии API.
module UnderscoreizeParams
extend ActiveSupport::Concern
def process_action(*args)
request.parameters.deep_transform_keys!(&:underscore)
super
end
end
тогда в моем API V3 BaseController
class V3::BaseController
include UnderscoreizeParams
end
наслаждаться.
Ответ 10
Вы можете создать фильтр, который запускается до вызова контроллера, и применить к нему следующие инструкции:
# transform camel case string into snake case
snake_string = Proc.new {|s| s.gsub(/([a-z])([A-Z])/) {|t| "#{$1}_#{$2.downcase}"}}
# transform all hash keys into snake case
snake_hash = Proc.new do |hash|
hash.inject({}) do |memo, item|
key, value = item
key = case key
when String
snake_string.call(key)
when Symbol
snake_string.call(key.to_s).to_sym
else
key
end
memo[key] = value.instance_of?(Hash) ? snake_hash.call(value) : value
memo
end
end
params = snake_hash.call(params)
Вы должны учитывать, что описанная выше процедура накладывает небольшие накладные расходы на каждый вызов Rails.
Я не уверен, что это необходимо, если оно просто подходит к соглашению.
Ответ 11
Ответ tlewin не работал у меня в Rails 3. Кажется, оператор params = = делает будущие операции на нем недействительными. очень странно. в любом случае для меня работает следующее, поскольку оно использует только операторы [] = и delete:
before_filter :underscore_param_keys
def underscore_param_keys
snake_hash = ->(hash) {
# copying the hash for iteration so we are not altering and iterating over the same object
hash.to_a.each do |key, value|
hash.delete key
hash[key.to_s.underscore] = value
snake_hash.call(value) if value.is_a? Hash
value.each { |item| snake_hash.call(item) if item.is_a? Hash } if value.is_a? Array
end
}
snake_hash.call(params)
end
Ответ 12
Вы можете попробовать это:
class ApplicationController < ActionController::API
include ControllerHelper
before_action :deep_underscore_params!
def deep_underscore_params!(app_params = params)
app_params.transform_keys!(&:underscore)
app_params.each do |key, value|
deep_underscore_params!(value) if value.instance_of?(ActionController::Parameters)
end
app_params.reject! { |k, v| v.blank? }
end
end
Ответ 13
Обращаясь к ответу Элиота Сайкса выше, я думаю, что мы можем сделать немного лучше в случае с Rails5. Я не люблю полностью переписывать эту функцию, так как этот код может измениться. Поэтому вместо этого я предлагаю использовать композицию функций:
# File: config/initializers/json_param_key_transform.rb
# Transform JSON request param keys from JSON-conventional camelCase to
# Rails-conventional snake_case:
ActionDispatch::Request.parameter_parsers[:json] = (
# Compose the original parser with a transformation
ActionDispatch::Request.parameter_parsers[:json] >>
# Transform camelCase param keys to snake_case
->(data) {
data.deep_transform_keys(&:underscore)
}
)
Ответ 14
Rails 6 добавил deep_transform_keys
в ActionController::Parameters
, чтобы вы могли сделать это так просто, как:
class ApplicationController < ActionController::Base
before_action :underscore_params!
private
def underscore_params!
params.deep_transform_keys!(&:underscore)
end
end