我正在开发一个主要作为API提供服务的应用程序(除了一些小的视图,例如会话/注册,这些是“标准”的)。我喜欢在Railscast#350:版本化API中确定的方法,并遵循它。我的路由如下:
namespace :api, :defaults => {:format => 'json'} do
scope :module => :v1, :constraints => ApiConstraints.new(:version => 1, :default => false) do
resources :posts, :only => [:create, :show, :destroy, :index]
end
scope :module => :v2, :constraints => ApiConstraints.new(:version => 2, :default => true) do
resources :posts, :only => [:create, :show, :destroy, :index]
end
end
在每个路由中,我的Constraint是一个新的ApiConstraints对象,该对象位于我的./lib
文件夹中。该类如下所示:class ApiConstraints
def initialize(options)
@version = options[:version]
@default = options[:default]
end
def matches?(req)
@default || req.headers['Accept'].include?("application/vnd.MYAPP.v#{@version}")
end
end
现在,手动测试时一切都按预期工作。在我的API中,每个版本可能有5到10个控制器,并且不想为每个单独的控制器测试API约束,因为这毫无意义。我正在寻找一个规范文件来测试我的API约束,但我不确定该把这个规范文件放在哪里。我尝试添加了一个
spec/routing/api_spec.rb
文件来测试,但它没有正常工作,因为它抱怨某些内容未被提供,如下所示:it "should route an unversioned request to the latest version" do
expect(:get => "/api/posts", :format => "json").to route_to(:controller => "api/v1/posts")
end
尽管控制器匹配正确,但仍会出现错误。它将以以下错误失败:
The recognized options <{"format"=>"json", "action"=>"index", "controller"=>"api/v1/posts"}>
did not match <{"controller"=>"api/v1/posts"}>,
difference: <{"format"=>"json", "action"=>"index"}>.
请注意,控制器已正确确定,但由于我不想在此测试中测试格式和操作,因此它会出现错误。我希望有3个"API规范":
- 将未版本化的请求路由到最新版本
- 如果没有指定格式,则应默认为JSON格式
- 在请求时应返回指定的API版本
有没有人有编写此类路由规范的经验?我不想为API内的每个控制器添加规范,因为它们不负责此功能。
route_to
时,您需要提供更具体的期望,例如expect(:get => "/api/posts.json"').to route_to(:controller => "api/v1/posts", :action => "index", :format => "json")
。不幸的是,默认的 rspec-rails 匹配器无法避免这种情况。 - gregatesApiConstraint
类上的#matches?
方法,并信任ActionDispatch来完成它的工作。 - gregates