在Rails中将所有控制器参数从camelCase转换为snake_case的最佳方法是什么?

正如您所知,JSON命名约定主张使用camelSpace,Rails主张使用snake_case作为参数名称。

在rails控制器中将所有请求的参数转换为snake_case的最佳方法是什么?

由此:

{ ... "firstName": "John", "lastName": "Smith", "moreInfo": { "mealType": 2, "mealSize": 4, ... } } 

对此:

 { ... "first_name": "John", "last_name": "Smith", "more_info": { "meal_type": 2, "meal_size": 4, ... } } 

谢谢

完成以下步骤后,通过JSON请求提交的camelCase param名称将更改为snake_case

例如,名为passwordConfirmation的JSON请求参数将在控制器中作为params[:password_confirmation]

config/initializers/json_param_key_transform.rb创建初始化config/initializers/json_param_key_transform.rb 。 此文件将仅更改JSON请求的参数解析行为(JSON请求必须具有请求标头Content-Type: application/json )。

找到你的Rails版本并选择下面的相应部分(在Gemfile.lock找到你的Rails版本):

仅适用于Rails 5

对于Rails 5,要将camel-case param键转换为snake-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(可能是早期版本),要将camel-case param键转换为snake-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

ActiveSupport已经提供了String#snakecase方法。 您所要做的就是安装一个filter,该filter通过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 

将Sebastian Hoitz的答案与这个要点合并,我可以使用rails 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 

在rails控制台中使用camelCase到snake_case的示例

 2.3.1 :001 > params = ActionController::Parameters.new({"firstName"=>"john", "lastName"=>"doe", "email"=>"john@doe.com"}) => "john", "lastName"=>"doe", "email"=>"john@doe.com"} permitted: false> 2.3.1 :002 > params.transform_keys(&:underscore) => "john", "last_name"=>"doe", "email"=>"john@doe.com"} permitted: false> 

资源:

http://api.rubyonrails.org/classes/ActionController/Parameters.html#method-i-transform_keys http://apidock.com/rails/String/underscore

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 

我想使用Chris Healds版本,但由于我使用的是Rails 4,因此我启用了strong_parameters,所以我不得不改变它。

这是我提出的版本:

 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 

另一个轨道5.1解决方案,背上了Sebastian Hoitz解决方案。 为了澄清我们为什么需要这样做:在R5.1 deep_transform_keys中! 不再是我们可用的方法,因为params不再inheritanceHashWithIndifferentAccess。 并克服了Eliot Sykes提到的问题,其中初始化程序仅适用于application / 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 

您可以创建在任何控制器调用之前运行的filter,并对其应用以下指示:

 # transform camel case string into snake case snake_string = Proc.new {|s| s.gsub(/([az])([AZ])/) {|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调用上施加一个小的开销。

我不相信这是必要的,只要符合惯例。

tlewin的答案在Rails 3中对我没有用。似乎params’=运算符使得它的未来操作无效。 很奇怪。 无论如何以下工作对我来说,因为它只使用[] =和删除操作符:

 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 

我们正在将我们的Rails API JSON密钥从snake_case转换为camelCase。 我们必须逐步进行转换,即一些API使用snake_case,而其他API更改为使用camelCase。

我们的解决方案是我们

  • create方法ActionController::Parameters#deep_snakeize
  • create方法ApplicationController#snakeize_params
  • set 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 

你可以试试这个:

 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