【问题标题】:Correct way to implement API versioning with active_model_serializers使用 active_model_serializers 实现 API 版本控制的正确方法
【发布时间】:2016-10-26 07:53:42
【问题描述】:

我知道已经有一些问题和this is a open issue regarding AMS not handling namespaces too efficiently(此版本控制方法使用),但我想确保我在当前限制内处于正确的轨道。

现在我使用的是 Rails 5 和 AMS 0.10.1,所以我做了以下操作:

# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.serializer_lookup_enabled = false

禁用默认的序列化程序查找(无论如何都不起作用);和

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  def get_serializer(resource, options = {})
    unless options[:each_serializer] || options[:serializer] then
      serializer = (self.class.name.gsub("Controller","").singularize + "Serializer").constantize
      resource.respond_to?(:to_ary) ? options[:each_serializer] = serializer : options[:serializer] = serializer
    end
    super(resource, options)
  end
end

覆盖默认情况下如何找到序列化程序;我的控制器和序列化器是这样的:

# app/controllers/api/v2/api_controller.rb
module Api::V2
  class ApiController < ApplicationController
    ...

# app/controllers/api/v2/users_controller.rb
module Api::V2
  class UsersController < ApiController
    ...

# app/serializers/api/v2/user_serializer.rb
module Api::V2
  class UserSerializer < ActiveModel::Serializer
    ...    

现在,ActiveModel::Serializer.serializer_for(object) 之类的东西将不起作用,因此我还必须使用 example.metadata[:api_version] 猴子修补我的请求规范,以便在每次测试之前设置 API 版本,如果示例未设置,则会引发错误。

所以:

  1. 有没有更好的记录方法?
  2. 这是否接近正确?
  3. 使用这种方法我会面临进一步的问题吗?
  4. 如何改进?

【问题讨论】:

    标签: ruby-on-rails api ruby-on-rails-5 active-model-serializers rails-api


    【解决方案1】:

    由于我没有找到更好的方法,既没有记录也没有任何地方,它似乎也是正确的,并且在使用一段时间后我没有遇到问题,这似乎是 API 版本控制的好方法。

    无论如何,我建议谨慎使用这种方法,不要更改 API 支持的旧版本的行为。仔细测试并通知您的客户弃用旧版本并支持删除。

    【讨论】:

      【解决方案2】:

      我认为你这里的东西还可以。我正在使用相同的方法,它适用于我的应用程序。我从 Ryan Bates 那里选择了最初的想法,他确实解释了非常相似的方法

      http://railscasts.com/episodes/350-rest-api-versioning

      这是我用来为每个资源指定不同序列化程序的方法:

      module API
        module V3
          class AssetController < API::V3::ApiController
            def index
              render json: assets, status: :ok, each_serializer: API::V3::Serializers::AssetSerializer
            end
        end
      end
      

      在我的实现中,我在 api/controllers/api/v3/serializers 中使用序列化程序。所以你正在对序列化程序类和控制器类进行版本控制

      不确定您是否真的需要 get_serializer,因为这更明确但没什么大不了

      如果您有很多 api 端点,请尝试在资源中组织它们。在我的 config/routes.rb 我有大约 700 个资源,所以我将它们分成单独的文件 config/api/v1/routes.rb...

      namespace :api, defaults: {format: 'json'} do
        namespace :v1
          resources :assets
        end
      end
      

      在 inflections.rb 初始化器中也很方便

      ActiveSupport::Inflector.inflections(:en) do |inflect|
        inflect.acronym 'API'
      end
      

      对我来说,最重要的问题是拥有良好的测试覆盖率。我更喜欢规范并检查正确的状态代码 200、201、...等,以及使用 json_schema 的正确子输出

      如果您需要进行身份验证,那么我建议您使用基于令牌的身份验证和 JWT - JSON Web Token。在我的实现中,我使用了两个令牌。进行 POST 和 PATCH 时用于读取的一个令牌和不同的令牌(不确定是否需要)。所以在 API 控制器内部是这样的

      class ApiController < ActionController::Base
        skip_before_action :verify_authenticity_token, if: :json_request?
        before_action :authenticate
      
        protected
        def json_request?
          request.format.json?
        end
        if request.headers['X-Authorization']
          token = request.headers['X-Authorization']
          payload = JWT.decode(token, 'my_custom_key_to_check_if_key_has_been_tempered d_on_client_side')[0]
        end
      end
      

      【讨论】:

      • 感谢您的输入!我这样做是为了把事情弄干一点。 get_serializer 总是由 AMS 调用,如果它不遵循定义的约定,我只需要使用 render json: @object, serializer: Namespaced::Unconventional::ObjectSerializer
      猜你喜欢
      • 1970-01-01
      • 2022-01-02
      • 2018-04-22
      • 2012-05-22
      • 2017-06-14
      • 2015-08-31
      • 2010-10-24
      • 2018-07-18
      • 2011-05-10
      相关资源
      最近更新 更多