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
Author: Charles, 2012-03-09

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!

 274
Author: Ryan Bigg,
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, &current_api_routes
  namespace :v2, &current_api_routes
  namespace :v1, &current_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, &current_api_routes
  namespace :v3, &current_api_routes
  namespace :v2, &current_api_routes
  namespace :v1, &current_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, &current_api_routes
  namespace :v3, &current_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
 37
Author: pixeltrix,
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:

Http://freelancing-gods.com/posts/versioning_your_ap_is

 13
Author: David Bock,
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).

 9
Author: aantix,
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).

Https://github.com/bploetz/versionist

 8
Author: Brian Ploetz,
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}" }
 0
Author: Amed Rodríguez,
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