使用active_model_serializers实现API版本控制的正确方法

时间:2016-06-23 20:01:20

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

我知道已经存在一些问题以及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. 如何改进?

2 个答案:

答案 0 :(得分:5)

我认为你在这里没事。我使用相同的方法,它适用于我的应用程序。我从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

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

如果您需要进行身份验证,那么我建议您使用基于令牌的身份验证和JWT - JSON Web令牌。在我的实现中,我使用两个令牌。在执行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

答案 1 :(得分:0)

由于我没有找到更好的方法,无论是记录还是在任何地方,它似乎也是正确的,并且在使用它之后我还没有遇到问题,这似乎是API的一个好方法版本

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