使用 active_model_serializers 实现 API 版本控制的正确方法
Correct way to implement API versioning with active_model_serializers
我知道已经有一些问题和 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 版本如果示例未设置,则会出错。
所以:
- 是否有更好的方法记录?
- 这是否接近正确?
- 我会在使用这种方法时遇到更多问题吗?
- 如何改进?
我觉得你这里的东西还可以。我正在使用相同的方法,它适用于我的应用程序。我从 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 令牌。在我的实现中,我使用了两个令牌。在执行 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
由于我没有找到更好的方法,既没有记录也没有在任何地方,它似乎也是正确的,并且在使用一段时间后我没有遇到问题,这似乎是一个很好的方法 API 版本控制。
无论如何,我建议谨慎使用这种方法,不要更改 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 版本如果示例未设置,则会出错。
所以:
- 是否有更好的方法记录?
- 这是否接近正确?
- 我会在使用这种方法时遇到更多问题吗?
- 如何改进?
我觉得你这里的东西还可以。我正在使用相同的方法,它适用于我的应用程序。我从 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 令牌。在我的实现中,我使用了两个令牌。在执行 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
由于我没有找到更好的方法,既没有记录也没有在任何地方,它似乎也是正确的,并且在使用一段时间后我没有遇到问题,这似乎是一个很好的方法 API 版本控制。
无论如何,我建议谨慎使用这种方法,不要更改 API 支持的旧版本的行为。仔细测试并通知您的客户旧版本的弃用和支持删除。