【问题标题】:How to return JSON from Rails with camelcased key names如何使用驼峰键名从 Rails 返回 JSON
【发布时间】:2017-06-16 20:14:55
【问题描述】:

我正在构建一个带有 Rails 后端的 JS 应用程序,为了不混淆蛇和骆驼案例,我想通过从服务器返回骆驼案例键名来规范这一切。所以user.last_name 从 API 返回时会返回 user.lastName

我如何实现这一目标?谢谢!

编辑:添加控制器代码

class Api::V1::UsersController < API::V1::BaseController
  # authorize_resource
  respond_to :json, only: [:index]
  def sky
      @user = User.find_by_id(params[:user_id])

      if @user
          obj =  {
              sky: {
                  sectors: @user.sectors,
                  slots: @user.slots
              }
          }

          render json: obj
      else
          raise "Unable to get Sky"
      end
  end
end

【问题讨论】:

    标签: ruby-on-rails ruby rails-api camelcasing


    【解决方案1】:

    我的做法是使用 ActiveModelSerializer 和 json_api 适配器:

    在您的 Gemfile 中,添加:

    gem 'active_model_serializers'
    

    创建一个新文件/config/initializers/ams.rb,其中包含:

    ActiveModelSerializers.config.adapter = :json_api
    ActiveModelSerializers.config.key_transform = :camel_lower
    

    您的控制器操作应如下所示:

    class ApiController < ApplicationController
      def sky
        @user = User.find_by_id(params[:user_id])
    
        if @user
          render json: @user, serializer: UserSkySerializer
        else
          raise "Unable to get Sky"
        end
      end
    end
    

    现在您需要创建一个序列化程序。首先,新建一个目录app/serializers/

    接下来,创建一个新的序列化程序app/serializers/user_sky_serializer.rb,包含:

    class UserSkySerializer < ActiveModel::Serializer
      attributes :sectors, :slots
    end
    

    结果将类似于您在 obj 哈希中描述的内容,但所有属性键都将使用 jsonapi 标准以驼峰形式呈现。

    【讨论】:

    • 感谢 moveson,我做到了,我在控制台中获得了带有 [active_model_serializers] Rendered ActiveModel::Serializer::Null with Hash 的未序列化 json 版本。这里有什么想法吗?
    • 会不会是因为我的继承? class Api::V1::UsersController &lt; API::V1::BaseController &lt; ActionController::Base(为了这篇文章的简洁,我结合了继承)
    • FWIW,这是 Rails 应用程序,而不是 rails-api 应用程序
    • 可以即时更改吗?我们所有的 JSON 都是snake_case,但一个新供应商似乎更喜欢 ClassCase。我只想在其命名空间的序列化程序中使用 ClassCase。这可能吗?
    • @Dan 我不相信这可以在不重启服务器的情况下改变。
    【解决方案2】:

    另一个选项是使用olive_branch gem。如this post 中所述,您只需:

    1. 添加此宝石
    2. application.rb 中添加config.middleware.use OliveBranch::Middleware

    然后,将此标头添加到来自客户端应用程序的请求中:

    'X-Key-Inflection': 'camel'
    

    如果您使用像 axios 这样的库,您可以将此标头与其他标头一起添加到常量中:

    const HEADERS = {
      ...
      'X-Key-Inflection': 'camel'
    }
    
    const request = axios.post(url, param, HEADERS)
    

    这样你就不需要在服务器端手动deep_transform 键。即使是深度嵌套的 json 键也会被骆驼化。此类请求的示例响应:

    [
      {
        "id": 1,
        "firstName": "Foo",
        "lastName": "Bar",
        "createdAt": ...,
        "updatedAt": ...,
        ...
        "cabinAssignments": [
          {
            "id": 1,
            "cabinKeeperId": 1,
            "userId": 1,
            "houseId": 1,
            ...
          }
        ]
      }
    ]
    

    【讨论】:

      【解决方案3】:

      你没有提到你的 Rails 版本,所以对于寻找这个的其他人,我会提到在 Rails 5 中,答案是使用来自ActiveSupport::Inflector#camelize。就我而言,我不得不在ActiveRecord 模型(下面的t)上以链的形式调用它:

        def index
          trucks = AvailableTruck.all.map do |t|
            t.as_json(only: AvailableTruck.exposed)
             .deep_transform_keys(&:camelize).deep_transform_values(&:upcase)
          end
          render json: trucks
        end
      

      #deep_transform_keys 可能需要也可能不需要,具体取决于您的应用程序。

      【讨论】:

      • 我正在使用 Rails 5
      • 在您的示例中,t 是什么?你能在你的代码中提供更多的上下文吗?
      • 那么使用这种方法可能会更好?
      • 编辑答案以提供更多上下文。
      • 谢谢。我的控制器是否需要额外的继承或构造才能使用 ActiveSupport::Inflector
      【解决方案4】:

      您可以使用 ruby​​ 的 method_missing 方法来实现。

      创建一个类似concerns/method_missing_extension.rb 的文件并将以下代码放入其中。

      module MethodMissingExtension
        def method_missing(method_name, args = {})
          if self.class.column_names.include? method_name.to_s.underscore
            send method_name.to_s.underscore.to_sym
          else
            super
          end
        end
      end
      

      在每个模型中包含这个模块,例如include MethodMissingExtension

      现在,无论您何时执行user.firstName,都会返回user.first_name

      【讨论】:

      • 我讨厌无缘无故的投反对票。 Downvoters 有什么理由不投票吗?
      • 当然,我投了反对票,因为类似的答案已经存在,我写了一条评论,说明原因。我只是投了反对票,作为对未来看到这篇文章的人关于正确答案可能在哪里的一个信号。希望能解决这个问题
      • 那个答案很不一样。他们对method_missing 的实现有点不同,需要为每个模型的每个属性创建它。但是,这里只有一次编写的模块,可以包含在所有必需的models 中,您会得到想要的结果。希望它能澄清你的困惑。
      • 是的,如果您可以使用自己的小代码实现某些目标,那么为什么要使用一些库或 gem。
      • 尊敬的,我想要的是我对method_missing 不感兴趣,因为我必须为每个模型上的每个字段都实现它,这是繁重且低效的。我的目标是使用active_model_serializers
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-19
      • 1970-01-01
      • 2010-12-09
      • 1970-01-01
      • 2015-04-17
      • 1970-01-01
      相关资源
      最近更新 更多