In a Rails project, there's a namespace called api. I'm looking into making the API versioned so that api/v1 would be the preferred namespace.
For the routing, I was thinking of doing this:
namespace :api do
redirect_api_path_to_api_v1_path
namespace :v1 do
...
end
end
Its a large project, so I was thinking that I would still support the v1 endpoint and just redirect people to v1. Then slowly add v2 and at a point in time, do a v2 redirect.
What I've tried:
namespace :api do
namespace :v1 do
...
resources :countries
...
end
match "*", to: redirect(-> (params, request) {
"https://#{request.host_with_port}/api/v1#{request.path.split("/api").last}"
}), via: [:get, :post, :put, :post]
end
Specs return:
it "should redirect to v1" do
get "/api/countries"
expect(response).to have_http_status 302
end
Failures:
1) Version /api should redirect to v1
Failure/Error: get "/api/countries"
ActionController::RoutingError:
No route matches [GET] "/api/countries"
Related
I have the following routes:
config/routes.rb:
Rails.application.routes.draw do
scope module: :api do
namespace :v1 do
resources :users
end
end
end
controllers/api/v1/users_controller.rb:
module Api
module V1
class UsersController < ApplicationController
def index
...
end
end
end
end
The controller specs are under the folder spec/controllers/api/v1/.
spec/controllers/api/v1/users_controller_spec.rb:
module Api
module V1
RSpec.describe UsersController, type: :controller do
let!(:users) { create_list(:user, 10) }
describe 'GET /v1/users' do
before { get :index }
# Already tried `get '/v1/users'`, got the same error
it 'should return 10 users' do
expect(JSON.parse(response).size).to eq(10)
end
end
end
end
end
Once I run the tests I receive the following error:
ActionController::UrlGenerationError:
No route matches {:action=>"index", :controller=>"api/v1/users"}
Note that it "infers" the api part from somewhere, but my routes are like these:
v1/users
v1/users/:id
...
How can I make it work?
I believe this is because you are namespacing your test with Api::V1::UsersController, but your route is defined within the scope of Api. That's why your routes are not under /api
Try removing the Api module from the controller and test or changing scope module: :api to namespace :api
Alternatively, you could keep it all as-is and make the get request to your v1_users_path or whatever the corresponding path is when you run rails routes (instead of :index).
What I want to do is,
When someone make request to http://www.domain.com/api, the server forward this request to http://main.domain.com/api
Now I configure my rails routes on www.domain.com like this:
namespace :api do
namespace :v1 do
resources :users
end
namespace :v2 do
resources :sessions
end
match 'v:api/*path', :to => redirect("http://main.domain.com/api/%{path}")
end
currently The http request is not accepted on main.domain.com
Any suggestion ?
In your ApplicationController
def redirect_api
if request.host == 'http://www.domain.com/api'
redirect_to 'http://main.domain.com/api'
end
end
I'm new to rails, and I'm trying to build API following Code School tutorial.
I get this error while trying to post to '/users' path.
routes file code :
Rails.application.routes.draw do
namespace :api,constraints: {subdomain: 'api'}, path: '/' do
resources :users, except: :delete
end
end
and the test code is :
require 'test_helper'
class CreatingUsersTest < ActionDispatch::IntegrationTest
test 'create users' do
post api_users_path,
{user: {name: 'test', email:'test#test.com'}}.to_json,
{'Accept' => Mime::JSON, 'Content-Type': Mime::JSON.to_s}
assert_equal response.status, 201
assert_equal response.content_type, Mime::JSON
user = json(response.body)
assert_equal api_user_url(user[:id]), response.location
end
end
And when I use rake routes :
api_users GET /users(.:format) api/users#index {:subdomain=>"api"}
POST /users(.:format) api/users#create {:subdomain=>"api"}
....
In the route you constraint the subdomain to be api
namespace :api,constraints: {subdomain: 'api'}, path: '/' do
but then in the test you call api_user_path
post api_users_path
that uses a generic hostname (test.host), and not the api hostname. The solution is to pass a specific host to the helper that satisfies the requirement.
post api_users_path(host: "api.test.host")
After an upgrade from Rails 3.0 to 3.2.21 and RSpec from 2.14 to 3.1, my API routes (namespaced, see below) aren't working any more. Specifically, they appear in rake routes and work in the browser in development mode, but they cause "Route not found" exceptions in tests and I cannot get them recognized in the console (but this might be my ignorance... any help appreciated).
Other (non-namespaced) routes work fine.
Here are the route definitions:
namespace :api, path: '', defaults: { format: :json }, constraints: { format: :json, subdomain: 'api' } do
namespace :v1 do
resources :foos, only: [:index] do
post :bar, on: :collection
end
end
end
match '*path' => 'application#route_not_found' # default 404 route
Here is the rake routes output:
bar_api_v1_foo_index POST /v1/foo/bar(.:format) api/v1/foo#bar {:format=>:json, :subdomain=>"api"}
api_v1_foo DELETE /v1/foo/:id(.:format) api/v1/foo#destroy {:format=>:json, :subdomain=>"api"}
Here is my spec (in spec/integration/api_v1_foo_controller_spec.rb):
require "spec_helper"
describe Api::V1::FooController do
before do
host! "api.#{host}" # set subdomain, see http://elia.schito.me/2011/09/15/set-subdomain-in-rails-3-request-specs/
end
describe "POST" do
it "should bar" do
post "/v1/foo/bar"
end
end
end
Here is the spec output:
Failures:
1) Api::V1::FooController POST should bar
Failure/Error: post "/v1/foo/bar"
ActionController::RoutingError:
No route matches [POST] "/v1/foo/bar"
# /opt/local/lib/ruby2.1/gems/2.1.0/gems/actionpack-3.2.21/lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
# /opt/local/lib/ruby2.1/gems/2.1.0/gems/actionpack-3.2.21/lib/action_dispatch/middleware/show_exceptions.rb:56:in `call'
My feeble attempts at getting the routes to work in the console also fail, but for all Rails versions. It's probably the syntax (how do you use this?).
Rails.application.routes.recognize_path '/v1/foo/bar', method: :post, subdomain: 'api', format: :json
=> {:controller=>"application", :action=>"route_not_found", :path=>"v1/foo/bar"}
Why does the test above fail after the upgrade to Rails 3.2 and Rspec 3.1?
Update
This is probably an effect of a RoR bug: https://github.com/rails/rails/issues/8679
If I remove subdomain: 'api' inside the constraints block of the routes definition, all of Rails 3.2.21, 4.1.9 and 4-2-stable will accept the routes and work. If I force the spec to be a request spec and change the host! "api.#{host}" to host! "api.example.com", the routing also works.
Is this intentional? Is there an alternate way of restricting a route to a specific subdomain?
I have this line of code in my routes
namespace :api, defaults: {format: 'json'} do
namespace :v1 do
match '/auth/:provider/callback', to: 'sign_in#authenticate'
end
end
And my test as
require 'spec_helper'
describe "Routing" do
it "should route to sign_in#authenticate" do
expect(post: 'api/v1/auth/:provider/callback').to route_to({ controller: 'api/v1/sign_in', action: 'authenticate', format: 'json' })
end
end
However, no matter what I do I keep getting the error
No route matches "/api/v1/auth/:provider/callback"
What am I missing here in order to make this test pass?
I believe the error you're getting is because match defaults to a GET request, but you are testing a POST request in your spec. You should be able to fix the issue by changing the route to:
match '/auth/:provider/callback', to: 'sign_in#authenticate', via: :post
My personal preference is to use the post method instead of match, it just reads better to me:
post '/auth/:provider/callback' => 'sign_in#authenticate'