ruby-on-rails - root_path - routes ror
Control de versiones de API para rutas de rieles (6)
Estoy intentando versionar mi API como Stripe. A continuación se da la última versión de API es 2.
/api/users
devuelve un 301 a /api/v2/users
/api/v1/users
devuelve un índice de 200 usuarios en la versión 1
/api/v3/users
devuelve un 301 a /api/v2/users
/api/asdf/users
devuelve un 301 a /api/v2/users
De modo que básicamente todo lo que no especifique los enlaces de la versión a la última a menos que exista la versión especificada, entonces redirija a ella.
Esto es lo que tengo hasta ahora:
scope ''api'', :format => :json do
scope ''v:api_version'', :api_version => /[12]/ do
resources :users
end
match ''/*path'', :to => redirect { |params| "/api/v2/#{params[:path]}" }
end
La respuesta de Ryan Bigg funcionó para mí.
Si también desea mantener los parámetros de consulta a través de la redirección, puede hacerlo así:
match "*path", to: redirect{ |params, request| "/api/v2/#{params[:path]}?#{request.query_string}" }
No estoy seguro de por qué quieres redirigir a una versión específica si no se solicita explícitamente una versión. Parece que simplemente quiere definir una versión predeterminada que se sirve si no se solicita explícitamente ninguna versión. También estoy de acuerdo con David Bock en que mantener las versiones fuera de la estructura de la URL es una manera más clara de respaldar el control de versiones.
Conector desvergonzado: Versionist admite estos casos de uso (y más).
No soy un gran fan de versionar por rutas. Creamos VersionCake para admitir una forma más fácil de versiones de API.
Al incluir el número de versión de la API en el nombre del archivo de cada una de nuestras vistas respectivas (jbuilder, RABL, etc.), mantenemos el control de versiones discreto y permite una fácil degradación para admitir la compatibilidad con versiones anteriores (por ejemplo, si v5 de la vista no existe, render v4 de la vista).
Si es posible, sugeriría que reconsidere sus URL para que la versión no esté en la url, sino que se coloque en el encabezado de accept. Esta respuesta de desbordamiento de pila entra bien en esto:
Mejores prácticas para el control de versiones API
y este enlace muestra exactamente cómo hacerlo con el enrutamiento de rieles:
Un par de cosas para agregar:
Su combinación de redirección no funcionará para ciertas rutas: el *api
param es codicioso y lo tragará todo, por ejemplo, /api/asdf/users/1
lo redirigirá a /api/v2/1
. Sería mejor usar un parámetro normal como :api
. Es cierto que no coincidirá con casos como /api/asdf/asdf/users/1
pero si tiene recursos anidados en su API es una mejor solución.
Ryan WHY U NO LIKE namespace
? :-), p.ej:
current_api_routes = lambda do
resources :users
end
namespace :api do
scope :module => :v2, ¤t_api_routes
namespace :v2, ¤t_api_routes
namespace :v1, ¤t_api_routes
match ":api/*path", :to => redirect("/api/v2/%{path}")
end
Que tiene el beneficio adicional de las rutas con nombre y con nombre genérico. Una nota adicional: la convención al usar :module
es usar la notación de subrayado, por ejemplo: api/v1
no ''Api :: V1''. En un momento este último no funcionó, pero creo que se corrigió en Rails 3.1.
Además, cuando publica v3 de su API, las rutas se actualizarían así:
current_api_routes = lambda do
resources :users
end
namespace :api do
scope :module => :v3, ¤t_api_routes
namespace :v3, ¤t_api_routes
namespace :v2, ¤t_api_routes
namespace :v1, ¤t_api_routes
match ":api/*path", :to => redirect("/api/v3/%{path}")
end
Por supuesto, es probable que tu API tenga diferentes rutas entre versiones, en cuyo caso puedes hacer esto:
current_api_routes = lambda do
# Define latest API
end
namespace :api do
scope :module => :v3, ¤t_api_routes
namespace :v3, ¤t_api_routes
namespace :v2 do
# Define API v2 routes
end
namespace :v1 do
# Define API v1 routes
end
match ":api/*path", :to => redirect("/api/v3/%{path}")
end
La forma original de esta respuesta es muy diferente, y se puede encontrar aquí . Solo una prueba de que hay más de una manera de despellejar a un gato.
He actualizado la respuesta desde el uso de espacios de nombres y el uso de 301 redirecciones, en lugar del valor predeterminado de 302. Gracias a pixeltrix y Bo Jeanes por las indicaciones sobre esas cosas.
Es posible que desee usar un casco realmente fuerte porque esto va a volar su mente .
La API de enrutamiento de Rails 3 es muy perversa. Para escribir las rutas para su API, según sus requisitos anteriores, solo necesita esto:
namespace :api do
namespace :v1 do
resources :users
end
namespace :v2 do
resources :users
end
match ''v:api/*path'', :to => redirect("/api/v2/%{path}")
match ''*path'', :to => redirect("/api/v2/%{path}")
end
Si tu mente sigue intacta después de este punto, déjame explicarte.
En primer lugar, llamamos al namespace
que es muy útil cuando se quiere un conjunto de rutas con un alcance específico y un módulo que se llame de manera similar. En este caso, queremos que todas las rutas dentro del bloque para nuestro namespace
de namespace
con alcance a los controladores dentro del módulo Api
y todas las solicitudes a las rutas dentro de esta ruta se prefijan con api
. Solicitudes como /api/v2/users
, ¿sabes?
Dentro del espacio de nombres, definimos dos espacios de nombres más (woah!). Esta vez estamos definiendo el espacio de nombres "v1", por lo que todas las rutas para los controladores aquí estarán dentro del módulo V1
dentro del módulo Api::V1
: Api::V1
. Al definir resources :users
dentro de esta ruta, el controlador se ubicará en Api::V1::UsersController
. Esta es la versión 1, y se llega haciendo solicitudes como /api/v1/users
.
La versión 2 es solo un poco diferente. En lugar de que el controlador sirva en Api::V1::UsersController
, ahora está en Api::V2::UsersController
. Llegas haciendo solicitudes como /api/v2/users
.
A continuación, se usa una match
. Esto coincidirá con todas las rutas API que van a cosas como /api/v3/users
.
Esta es la parte que tuve que buscar. La opción :to =>
permite especificar que una solicitud específica debe ser redireccionada a otra parte, lo sabía, pero no sabía cómo redirigirla a otra parte y pasarle una parte del original. solicitar junto con ella.
Para hacer esto, llamamos al método de redirect
y le pasamos una cadena con un parámetro %{path}
especial interpolado. Cuando se recibe una solicitud que coincida con esta coincidencia final, se interpolará el parámetro de path
en la ubicación de %{path}
dentro de la cadena y se redirigirá al usuario al lugar al que debe dirigirse.
Finalmente, usamos otra match
para enrutar todas las rutas restantes con el prefijo /api
y redirigirlas a /api/v2/%{path}
. Esto significa que las solicitudes como /api/users
irán a /api/v2/users
.
No pude encontrar la manera de hacer coincidir /api/asdf/users
, porque ¿cómo se determina si se supone que es una solicitud a /api/<resource>/<identifier>
o /api/<version>/<resource>
?
De todos modos, esto fue divertido de investigar y espero que te ayude!