问题
I know there are already some questions and also this is a open issue regarding AMS not handling namespaces too efficiently (which is used by this versioning approach) but I wanted to be sure I am in the right track within the current constraints.
Right now I am using Rails 5 and AMS 0.10.1, so I did the following:
# config/initializers/active_model_serializer.rb
ActiveModelSerializers.config.serializer_lookup_enabled = false
to disable default serializer lookup (which didn't work anyway); and
# 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
to override how serializers are found by default; my controllers and serializer are like this:
# 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
...
and
# app/serializers/api/v2/user_serializer.rb
module Api::V2
class UserSerializer < ActiveModel::Serializer
...
Now, things like ActiveModel::Serializer.serializer_for(object)
won't work, so I had to also monkey patch my request specs using example.metadata[:api_version]
to set the API version before each test and raising and error if the example didn't set it.
So:
- Is there a better way documented?
- Is this any close to being correct?
- Will I be facing problem further in with this approach?
- How can it be improved?
回答1:
I think what you have here is okay. I am using the same approach and it works fine for my application. I picked the original idea here from Ryan Bates where he did explains very similar approach
http://railscasts.com/episodes/350-rest-api-versioning
This is what I use to specify different serializers for each resource:
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
In my implementation I am using serializers inside api/controllers/api/v3/serializers. So you are versioning serializers classes and controller classes
Not sure that you really need to have get_serializer since this is more explicit but not a big deal
If you have a lot of api endpoints try to organize them in resources. In my config/routes.rb I have about 700 resources so I split them into separate files config/api/v1/routes.rb...
namespace :api, defaults: {format: 'json'} do
namespace :v1
resources :assets
end
end
Also It is handy to do inside inflections.rb initializer
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
For me I would say that the biggest important issue is to have good testing coverage. I prefer spec and check for correct status codes 200, 201,...etc and as well as the correct son output using json_schema
If you need to do auth then I would suggest that you use token based auth and JWT - JSON Web Token. In my implementation I am using two tokens. One token for reading and different token when doing POST and PATCH (not sure it is needed maybe). so inside API controller something like this
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
回答2:
Since I haven't found a better way, neither documented nor anywhere, it also seems to be correct and I haven't faced problems after a while using it, this appear to be a good approach for API versioning.
Anyway, I advise caution using this approach to not change behaviour of older supported versions for your API. Test carefully and notify your clients for deprecations and support removal for old versions.
来源:https://stackoverflow.com/questions/38000787/correct-way-to-implement-api-versioning-with-active-model-serializers