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

a.s*_*r.o 32 json ruby-on-rails

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

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

由此:

{
  ...
  "firstName": "John",
  "lastName": "Smith",
  "moreInfo":
  {
    "mealType": 2,
    "mealSize": 4,
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

对此:

{
  ...
  "first_name": "John",
  "last_name": "Smith",
  "more_info":
  {
    "meal_type": 2,
    "meal_size": 4,
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

谢谢

Eli*_*kes 58

完成以下步骤后,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,要将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)
}
Run Code Online (Sandbox Code Playgroud)

对于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
  }
)
Run Code Online (Sandbox Code Playgroud)

所有Rails版本的最后一步

重启rails server.

  • `deep_transform_keys!` 在 Rails 5 中不推荐用于 `params` 对象。而是使用 `transform_keys!`,如 `params.transform_keys!(&:underscore)` ([documentation](http://api.rubyonrails. org/v5.0.0.1/classes/ActionController/Parameters.html#method-i-transform_values-21)) (3认同)
  • 得到了`ActionDispatch :: ParamsParser :: ParseError:未定义的方法'下划线'表示:_json:传递Array时的符号`.你应该跳过:_json或使用另一个容器哈希(即`data = {'_ container'=> data}`然后调用deep_transform_keys). (2认同)
  • @EliotSykes您知道GET请求的类似解决方案吗? (2认同)
  • @BrunoFacca在`before_action`中调用它的一个问题是转换发生在后面的过程中.因此,如果您想要参数包装(或者甚至只是记录参数)等操作正常工作,您将需要在初始化程序中执行此操作.这就是为什么我今天在这里结束:) (2认同)

Ale*_* K. 10

Rails 6.1中将被添加deep_transform_keysActionController::Parameters因此它使您能够使其变得如此简单:

class ApplicationController < ActionController::Base
  before_action :underscore_params!

  private

  def underscore_params!
    params.deep_transform_keys!(&:underscore)
  end
end
Run Code Online (Sandbox Code Playgroud)

编辑

目前,您可以按以下方式向后移植:

module DeepTransformKeys
  def deep_transform_keys!(&block)
    @parameters.deep_transform_keys!(&block)
    self
  end
end

ActionController::Parameters.include(DeepTransformKeys)
Run Code Online (Sandbox Code Playgroud)

  • 丑陋的解决方法,直到可用:`params.instance_variable_get(:@parameters).deep_transform_keys!(&amp;:underscore)` (2认同)
  • 由于某种原因,它在 [stable](https://github.com/rails/rails/blob/6-0-stable/actionpack/lib/action_controller/metal/strong_parameters.rb) 上不可用。将在 6.1 中提供(我在[此处]创建了一个问题(https://github.com/rails/rails/issues/39081))。 (2认同)

Chr*_*ald 8

ActiveSupport已经提供了String#snakecase方法.您所要做的就是安装一个过滤器,通过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
Run Code Online (Sandbox Code Playgroud)

  • 您可以使用string.underscore而不是snakecase (2认同)

pat*_*tie 8

将Sebastian Hoitz的答案与这个要点合并,我可以使它在rails 4.2上工作,使用wrap_parameters标记方法包装强参数和参数.

我无法使用a工作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
Run Code Online (Sandbox Code Playgroud)


Hub*_*der 7

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

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

2.3.1 :002 > params.transform_keys(&:underscore)
=> <ActionController::Parameters {"first_name"=>"john", "last_name"=>"doe", "email"=>"john@doe.com"} permitted: false>
Run Code Online (Sandbox Code Playgroud)

资源:

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

  • @Matt 你好,谢谢你的回复。如果您使用 Rails 6,我认为对于嵌套属性,您可以将 ActionController::Parameters 转换为散列,然后进行深度转换属性: params.permit!.to_h.deep_transform_keys { |key| key.to_s.underscore } 或 params.permit!.to_h.deep_transform_values { |value| value.to_s.underscore } 请参阅:https://apidock.com/rails/v6.0.0/Hash/deep_transform_values https://apidock.com/rails/v6.0.0/Hash/deep_transform_keys (2认同)

Art*_*yev 6

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
Run Code Online (Sandbox Code Playgroud)


Sal*_*alz 5

我不能在这里直接使用其他建议,但它让我走上了正确的轨道。

在 Rails 5.2 中,使用版本化 API,因此无法为整个应用程序更改它。我创建了这个问题,然后将其包含到我的新 api 版本模块的基本控制器中。

module UnderscoreizeParams
  extend ActiveSupport::Concern

  def process_action(*args)
    request.parameters.deep_transform_keys!(&:underscore)
    super
  end
end
Run Code Online (Sandbox Code Playgroud)

然后在我的 API V3 BaseController 中

class V3::BaseController
  include UnderscoreizeParams
end
Run Code Online (Sandbox Code Playgroud)

请享用。


Thi*_*win 0

您可以创建一个在任何控制器调用之前运行的过滤器,并向其应用以下指令:

# 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)
Run Code Online (Sandbox Code Playgroud)

您必须考虑到上述过程会给每个 Rails 调用带来很小的开销。

如果只是为了符合惯例,我不认为这是必要的。

  • 还可以利用ActiveSupport的下划线方法http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore (3认同)