Versionado de API para Rutas Rails
Estoy tratando de versión de mi API como Stripe tiene. A continuación se da la última versión de la 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
Así que básicamente cualquier cosa que no especifique la versión enlaza a la última a menos que exista la versión especificada, entonces redirige 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
6 answers
La forma original de esta respuesta es muy diferente, y se puede encontrar aquí. Sólo la prueba de que hay más de una manera de pelar un gato.
He actualizado la respuesta desde entonces para usar espacios de nombres y para usar redirecciones 301 rather en lugar del valor predeterminado de 302. Gracias a pixeltrix y Bo Jeanes por el impulso en esas cosas.
Es posible que desee usar un realmente casco fuerte porque esto va a volar su mente.
Los Rieles 3 la API de enrutamiento es súper malvada. 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 namespace
que es muy útil para cuando desea un montón de rutas de ámbito a una ruta específica y módulo que tienen un nombre similar. En este caso, queremos que todas las rutas dentro del bloque para nuestro namespace
sean scoped a controladores dentro del módulo Api
y todas las solicitudes a rutas dentro de esta ruta tendrá el prefijo api
. Peticiones 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
: 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 lo consigues haciendo peticiones como /api/v1/users
.
La versión 2 es solo una pequeño un poco diferente. En lugar de que el controlador esté en Api::V1::UsersController
, ahora está en Api::V2::UsersController
. Se llega allí haciendo peticiones como /api/v2/users
.
A continuación, se usa un match
. Esto coincidirá con todas las rutas API que van a cosas como /api/v3/users
.
Esta es la parte que tuve que buscar. El :to =>
opción le permite especificar que una solicitud específica debe ser redirigido a otra parte-yo sabía que mucho, pero no sabía cómo llegar a redirigir a otro lugar y pasar un pieza de la solicitud original junto con ella.
Para hacer esto, llamamos al método redirect
y le pasamos una cadena con un parámetro especial interpolado %{path}
. Cuando llega una solicitud que coincide con este match
final, interpolará el parámetro path
en la ubicación de %{path}
dentro de la cadena y redirigirá al usuario a donde necesita ir.
Finalmente, usamos otro match
para enrutar todos los caminos restantes con el prefijo /api
y redirigirlos a /api/v2/%{path}
. Esto significa solicitudes como /api/users
irá a /api/v2/users
.
No pude averiguar cómo hacer que /api/asdf/users
coincida, 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!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:10
Un par de cosas a añadir:
Su coincidencia de redireccionamiento no va a funcionar para ciertas rutas: el parámetro *api
es codicioso y se tragará todo, por ejemplo, /api/asdf/users/1
se redirigirá a /api/v2/1
. Sería mejor usar un param regular 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 ¿POR QUÉ NO ME GUSTA namespace
? :- ), por ejemplo:
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 añadido de versionado y genérico nombrado ruta. Una nota adicional - la convención cuando se usa :module
es usar notación de subrayado, por ejemplo: api/v1
no 'Api::V1'. En un momento este último no funcionó, pero creo que se arregló en Rails 3.1.
Además, cuando publiques la v3 de tu API, las rutas se actualizarán de la siguiente manera:
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 su API tenga diferentes rutas entre las versiones, en cuyo caso puede 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-09 06:47:01
Si es posible, sugeriría repensar sus url para que la versión no esté en la url, sino que se coloque en el encabezado accepts. Esta respuesta de desbordamiento de pila va bien:
¿Mejores prácticas para el control de versiones de API?
Y este enlace muestra exactamente cómo hacerlo con el enrutamiento de rails:
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:44
No soy un gran fan del versionado por rutas. Construimos VersionCake para soportar una forma más fácil de versionado de API.
Al incluir el número de versión de la API en el nombre de archivo de cada una de nuestras vistas respectivas (jbuilder, RABL, etc.), mantenemos el control de versiones discreto y permitimos una degradación fácil para soportar la compatibilidad hacia atrás (por ejemplo, si v5 de la vista no existe, renderizamos v4 de la vista).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-21 23:58:40
No estoy seguro de por qué desea redirigir a una versión específica si una versión no se solicita explícitamente. Parece que simplemente desea definir una versión predeterminada que se muestre si no se solicita ninguna versión explícitamente. También estoy de acuerdo con David Bock en que mantener las versiones fuera de la estructura de URL es una forma más limpia de soportar el control de versiones.
Shameless plug: Versionist soporta estos casos de uso (y más).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-12 01:28:52
Ryan Bigg answer trabajó 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}" }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-20 12:21:25