Phiên bản API cho các tuyến đường ray


141

Tôi đang cố gắng phiên bản API của mình như Stripe có. Dưới đây được đưa ra phiên bản API mới nhất là 2.

/api/users trả về 301 /api/v2/users

/api/v1/users trả về 200 chỉ số người dùng ở phiên bản 1

/api/v3/users trả về 301 /api/v2/users

/api/asdf/users trả về 301 /api/v2/users

Vì vậy, về cơ bản, bất cứ điều gì không chỉ định phiên bản liên kết đến phiên bản mới nhất trừ khi phiên bản được chỉ định tồn tại sau đó chuyển hướng đến phiên bản đó.

Đây là những gì tôi có cho đến nay:

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

Câu trả lời:


280

Các hình thức ban đầu của câu trả lời này là rất khác nhau, và có thể được tìm thấy ở đây . Chỉ cần chứng minh rằng có nhiều hơn một cách để lột da một con mèo.

Tôi đã cập nhật câu trả lời kể từ khi sử dụng không gian tên và sử dụng chuyển hướng 301 - thay vì mặc định là 302. Cảm ơn pixeltrix và Bo Jeanes đã nhắc nhở về những điều đó.


Bạn có thể muốn đội một chiếc mũ bảo hiểm thực sự mạnh mẽ bởi vì điều này sẽ thổi bay tâm trí của bạn .

API định tuyến Rails 3 siêu độc ác. Để viết các tuyến đường cho API của bạn, theo yêu cầu của bạn ở trên, bạn chỉ cần điều này:

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

Nếu tâm trí của bạn vẫn còn nguyên vẹn sau thời điểm này, hãy để tôi giải thích.

Đầu tiên, chúng tôi gọi namespaceđó là siêu tiện dụng khi bạn muốn một loạt các tuyến đường nằm trong một đường dẫn và mô-đun cụ thể có tên tương tự. Trong trường hợp này, chúng tôi muốn tất cả các tuyến đường trong khối để chúng tôi namespaceđược đặt trong phạm vi các bộ điều khiển trong Apimô-đun và tất cả các yêu cầu đối với các đường dẫn bên trong tuyến đường này sẽ được bắt đầu bằng api. Yêu cầu như /api/v2/users, bạn biết không?

Trong không gian tên, chúng tôi xác định thêm hai không gian tên (woah!). Lần này chúng ta sẽ xác định không gian tên "v1", vì vậy tất cả các tuyến cho bộ điều khiển ở đây sẽ nằm trong V1mô-đun bên trong Apimô-đun : Api::V1. Bằng cách xác định resources :usersbên trong tuyến đường này, bộ điều khiển sẽ được đặt tại Api::V1::UsersController. Đây là phiên bản 1 và bạn đến đó bằng cách thực hiện các yêu cầu như thế nào /api/v1/users.

Phiên bản 2 chỉ là một nhỏ chút khác nhau. Thay vì bộ điều khiển phục vụ nó đang ở Api::V1::UsersController, bây giờ nó ở Api::V2::UsersController. Bạn đến đó bằng cách thực hiện các yêu cầu như thế nào /api/v2/users.

Tiếp theo, a matchđược sử dụng. Điều này sẽ phù hợp với tất cả các tuyến API đi đến những thứ như /api/v3/users.

Đây là phần tôi đã phải tìm kiếm. Các :to =>tùy chọn cho phép bạn xác định rằng một yêu cầu cụ thể nên được chuyển hướng ở một nơi khác - Tôi biết rằng có rất nhiều - nhưng tôi không biết làm thế nào để có được nó để chuyển hướng đến một nơi khác và vượt qua trong một mảnh yêu cầu ban đầu cùng với nó .

Để làm điều này, chúng ta gọi redirectphương thức và truyền cho nó một chuỗi với %{path}tham số được nội suy đặc biệt . Khi một yêu cầu xuất hiện phù hợp với trận chung kết này match, nó sẽ nội suy paththam số vào vị trí %{path}bên trong chuỗi và chuyển hướng người dùng đến nơi họ cần đến.

Cuối cùng, chúng tôi sử dụng một cái khác matchđể định tuyến tất cả các đường dẫn còn lại có tiền tố /apivà chuyển hướng chúng đến /api/v2/%{path}. Điều này có nghĩa là yêu cầu như /api/userssẽ đi đến /api/v2/users.

Tôi không thể tìm ra làm thế nào để /api/asdf/userskhớp, bởi vì làm thế nào để bạn xác định xem đó có phải là một yêu cầu /api/<resource>/<identifier>hay /api/<version>/<resource>không?

Dù sao, đây là niềm vui để nghiên cứu và tôi hy vọng nó sẽ giúp bạn!


24
Ryan Bigg thân mến. Bạn thông minh.
maletor

18
Người ta không chỉ đơn giản đo lường danh tiếng của Ruby Hero.
Waseem

1
Ryan ... Tôi không nghĩ điều này thực sự chính xác. Điều này sẽ có / api và / api / v2 phục vụ cùng một nội dung thay vì có một URL chính tắc duy nhất. / api nên chuyển hướng đến / api / v2 (như tác giả ban đầu đã chỉ định). Tôi hy vọng các tuyến chính xác sẽ trông giống như gist.github.com/2044335 (được cấp, mặc dù tôi chưa kiểm tra điều đó). Chỉ / api / v [12] mới trả về 200, / api và / api / <phiên bản xấu> sẽ trả lại 301s cho / api / v2
Bo Jeanes

2
Điều đáng chú ý là trong các tuyến đường, tệp 301 đã được thực hiện chuyển hướng mặc định và vì lý do chính đáng. Từ các hướng dẫn: Please note that this redirection is a 301 “Moved Permanently” redirect. Keep in mind that some web browsers or proxy servers will cache this type of redirect, making the old page inaccessible.
maletor

3
Nó không tạo ra các chuyển hướng vô hạn nếu đường dẫn không chính xác? Chẳng hạn, việc yêu cầu / api / v3 / path_that_dont_match_the_routes sẽ tạo ra một chuyển hướng vô hạn, phải không?
Robin

38

Một vài điều cần thêm:

Trận đấu chuyển hướng của bạn sẽ không hoạt động đối với một số tuyến nhất định - *apiparam tham lam và sẽ nuốt chửng mọi thứ, ví dụ như /api/asdf/users/1sẽ chuyển hướng đến /api/v2/1. Bạn nên sử dụng một param thông thường như thế :api. Phải thừa nhận rằng nó sẽ không khớp với các trường hợp như /api/asdf/asdf/users/1nhưng nếu bạn có các tài nguyên lồng nhau trong api thì đó là một giải pháp tốt hơn.

Ryan TẠI SAO BẠN KHÔNG THÍCH namespace? :-), ví dụ:

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

Mà có thêm lợi ích của các tuyến được đặt tên theo phiên bản và chung chung. Thêm một lưu ý - quy ước khi sử dụng :modulelà sử dụng ký hiệu gạch dưới, ví dụ: api/v1không phải 'Api :: V1'. Tại một thời điểm, cái sau không hoạt động nhưng tôi tin rằng nó đã được sửa trong Rails 3.1.

Ngoài ra, khi bạn phát hành v3 API, các tuyến sẽ được cập nhật như sau:

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

Tất nhiên, có khả năng API của bạn có các tuyến khác nhau giữa các phiên bản trong trường hợp bạn có thể thực hiện việc này:

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

Làm thế nào bạn sẽ đối phó với trường hợp cuối cùng? tức /api/asdf/users?là cũng như /api/users/1? Tôi không thể tìm ra câu trả lời được cập nhật của mình, để tìm ra rằng bạn có thể biết một cách
Ryan Bigg

Không có cách nào dễ dàng để làm điều đó - bạn phải xác định tất cả các chuyển hướng trước khi bắt tất cả nhưng bạn chỉ cần thực hiện từng tài nguyên cho từng tài nguyên cha mẹ, ví dụ / api / users / * path => / api / v2 / users /% {path}
pixeltrix

13

Nếu có thể, tôi sẽ đề nghị xem xét lại các url của bạn để phiên bản không có trong url, nhưng được đưa vào tiêu đề chấp nhận. Câu trả lời tràn ngăn xếp này đi sâu vào nó:

Thực tiễn tốt nhất cho phiên bản API?

và liên kết này hiển thị chính xác cách thực hiện với định tuyến đường ray:

http://freelANCE-gods.com/posts/versioning_your_ap_is


Đây cũng là một cách tuyệt vời để làm điều đó và có lẽ cũng sẽ phục vụ cho yêu cầu "/ api / asdf / users".
Ryan Bigg

9

Tôi không phải là một fan hâm mộ lớn của phiên bản theo tuyến đường. Chúng tôi đã xây dựng VersionCake để hỗ trợ một dạng phiên bản API dễ dàng hơn.

Bằng cách bao gồm số phiên bản API trong tên tệp của từng chế độ xem tương ứng của chúng tôi (jbuilder, RABL, v.v.), chúng tôi giữ cho phiên bản không bị phô trương và cho phép xuống cấp dễ dàng để hỗ trợ khả năng tương thích ngược (ví dụ: nếu v5 ​​của chế độ xem không tồn tại, chúng tôi kết xuất v4 của khung nhìn).


8

Tôi không chắc tại sao bạn muốn chuyển hướng đến một phiên bản cụ thể nếu một phiên bản không được yêu cầu rõ ràng. Có vẻ như bạn chỉ muốn xác định một phiên bản mặc định sẽ được phục vụ nếu không có phiên bản nào được yêu cầu rõ ràng. Tôi cũng đồng ý với David Bock rằng giữ các phiên bản ngoài cấu trúc URL là một cách sạch hơn để hỗ trợ phiên bản.

Trình cắm không biết xấu hổ: Trình tạo phiên bản hỗ trợ các trường hợp sử dụng này (và hơn thế nữa).

https://github.com/bploetz/versionist


2

Ryan Bigg trả lời làm việc cho tôi.

Nếu bạn cũng muốn giữ các tham số truy vấn thông qua chuyển hướng, bạn có thể làm như thế này:

match "*path", to: redirect{ |params, request| "/api/v2/#{params[:path]}?#{request.query_string}" }

2

Đã thực hiện điều này ngày hôm nay và tìm thấy những gì tôi tin là 'đúng cách' trên RailsCasts - Phiên bản API REST . Quá đơn giản. Vì vậy, duy trì. Thật hiệu quả.

Thêm lib/api_constraints.rb(thậm chí không phải thay đổi vnd.example.)

class ApiConstraints
  def initialize(options)
    @version = options[:version]
    @default = options[:default]
  end

  def matches?(req)
    @default || req.headers['Accept'].include?("application/vnd.example.v#{@version}")
  end
end

Cài đặt config/routes.rbnhư vậy

require 'api_constraints'

Rails.application.routes.draw do

  # Squads API
  namespace :api do
    # ApiConstaints is a lib file to allow default API versions,
    # this will help prevent having to change link names from /api/v1/squads to /api/squads, better maintainability
    scope module: :v1, constraints: ApiConstraints.new(version:1, default: true) do
      resources :squads do
        # my stuff was here
      end
    end
  end

  resources :squads
  root to: 'site#index'

Chỉnh sửa bộ điều khiển của bạn (tức là /controllers/api/v1/squads_controller.rb)

module Api
  module V1
    class SquadsController < BaseController
      # my stuff was here
    end
  end
end

Sau đó, bạn có thể thay đổi tất cả các liên kết trong ứng dụng của mình từ /api/v1/squadssang /api/squadsvà bạn có thể DỄ DÀNG triển khai các phiên bản api mới mà không cần phải thay đổi liên kết

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.