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

6
我知道已经有一些问题,也有关于AMS没有有效处理命名空间的这是一个开放性问题(该版本控制方法使用了它),但我想确保在当前约束条件下我走在正确的轨道上。
现在我正在使用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个回答

6

我认为你这里的内容还可以。我正在使用相同的方法,我的应用程序运行良好。我从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检查正确的son输出。
如果需要进行身份验证,则建议使用基于令牌的身份验证和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

谢谢您的输入!我已经这样做来使事情更加DRY。get_serializer总是由AMS调用,如果它不遵循定义的约定,我只需要使用render json:@object,serializer:Namespaced :: Unconventional :: ObjectSerializer即可。 - chrisandrew.cl

0

由于我没有找到更好的方法,无论是在文档中还是其他地方,这似乎也是正确的,并且在使用一段时间后我也没有遇到问题,这似乎是API版本控制的一个不错的方法。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接