철도 노선을위한 API 버전 관리


141

Stripe처럼 API를 버전하려고합니다. 아래는 최신 API 버전이 2입니다.

/api/users 에 301을 반환 /api/v2/users

/api/v1/users 버전 1에서 200의 사용자 색인을 리턴합니다.

/api/v3/users 에 301을 반환 /api/v2/users

/api/asdf/users 에 301을 반환 /api/v2/users

따라서 기본적으로 버전을 지정하지 않은 모든 것은 지정된 버전이 존재하지 않는 한 최신 버전으로 연결되어 리디렉션됩니다.

이것이 내가 지금까지 가진 것입니다.

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

답변:


280

이 답변의 원래 형식은 크게 다르며 여기에서 찾을 수 있습니다 . 고양이를 피부에 바르는 방법이 두 가지 이상 있다는 것을 증명하십시오.

네임 스페이스를 사용하고 기본값 302 대신 301 리디렉션을 사용하기 때문에 답변을 업데이트했습니다.


당신은 당신의 마음날려 버릴 것이기 때문에 당신은 정말 강한 헬멧 을 착용하고 싶을 수도 있습니다 .

Rails 3 라우팅 API는 매우 사악합니다. 위의 요구 사항에 따라 API 경로를 작성하려면 다음이 필요합니다.

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

이 시점 이후에도 여전히 마음이 손상되지 않았다면 설명하겠습니다.

먼저, 우리 namespace는 비슷한 경로를 가진 특정 경로와 모듈의 범위를 원할 때 매우 편리한 호출이라고 부릅니다 . 이 경우 블록 namespace내의 Api모든 경로를 모듈 내의 컨트롤러로 범위 지정 하고이 경로 내의 경로에 대한 모든 요청 앞에 접두사가 붙습니다 api. 와 같은 요청을 /api/v2/users알고 있습니까?

네임 스페이스 내에서 두 개의 네임 스페이스를 더 정의합니다 (woah!). 이번에는 "v1"네임 스페이스를 정의하고 있으므로 여기서 컨트롤러의 모든 경로는 V1모듈 내부의 Api모듈 내부에 Api::V1있습니다. resources :users이 경로 내부 를 정의 하면 컨트롤러가에 위치 Api::V1::UsersController합니다. 이것은 버전 1이며 다음과 같이 요청하여 도착합니다 /api/v1/users.

버전 2 만 인 작은 비트 다른. 에 서비스를 제공하는 컨트롤러 대신 Api::V1::UsersController현재 위치에 Api::V2::UsersController있습니다. 와 같은 요청을 통해 도착합니다 /api/v2/users.

다음으로 a match가 사용됩니다. 이것은와 같은 모든 API 경로와 일치합니다 /api/v3/users.

이것은 내가 찾아야 할 부분입니다. 이 :to =>옵션을 사용하면 특정 요청을 다른 곳으로 리디렉션해야한다고 지정할 수 있습니다. .

이를 위해 redirect메소드를 호출하고 특수 보간 %{path}매개 변수가 있는 문자열을 전달합니다 . 이 final과 일치하는 요청이 들어 오면 매개 변수를 문자열 내부의 위치로 match보간 하고 사용자를 필요한 곳으로 리디렉션합니다.path%{path}

마지막으로 다른 match경로를 사용하여 접두사가있는 나머지 모든 경로를 라우팅 /api하고로 리디렉션합니다 /api/v2/%{path}. 이는와 같은 요청 /api/users이로 이동 함을 의미합니다 /api/v2/users.

내가 얻는 방법을 알아낼 수없는 /api/asdf/users그가에 요청 있어야하는 경우 당신은 어떻게 결정 않기 때문에, 일치 /api/<resource>/<identifier>또는 /api/<version>/<resource>?

어쨌든, 이것은 연구하기에 재미 있었고 나는 그것이 당신에게 도움이되기를 바랍니다!


24
Ryan Bigg님께 당신은 훌륭합니다.
maletor

18
루비 히어로의 평판 만 측정하는 것은 아닙니다.
Waseem

1
라이언 .. 이것이 사실이라고 생각하지 않습니다. 이것은 단일 표준 URL 대신 / api 및 / api / v2가 동일한 내용을 제공하게합니다. / api는 / api / v2로 리디렉션되어야합니다 (지정된 원본 작성자). 올바른 경로가 gist.github.com/2044335 와 같이 보일 것으로 기대합니다 (허가를 받았지만 테스트하지는 않았습니다). / api / v [12] 만이 200을, / api / <bad version>은 301을 / api / v2에 반환해야합니다.
Bo Jeanes

2
라우트 파일 (301)에서 디폴트 리다이렉션이되었고 합당한 이유가 있음을 주목할 가치가있다. 가이드에서 : 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
경로가 올바르지 않으면 무한 리디렉션을 생성하지 않습니까? 예를 들어, / api / v3 / path_that_dont_match_the_routes를 요청하면 무한 리디렉션이 생성됩니까?
Robin

38

몇 가지 추가 할 사항 :

리디렉션 경로가 특정 경로에서는 작동하지 않습니다. *api매개 변수는 욕심이 많고 모든 것을 삼킬 것입니다 (예 : /api/asdf/users/1리디렉션) /api/v2/1. 과 같은 일반 매개 변수를 사용하는 것이 좋습니다 :api. 분명히 그것은 같은 경우와 일치하지 /api/asdf/asdf/users/1않지만 API에 중첩 된 리소스가있는 경우 더 나은 솔루션입니다.

라이언 왜 좋아하지 않아 namespace? :-), 예 :

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

버전이 지정된 일반 라우트의 추가 이점이 있습니다. 추가 참고 사항-사용시의 규칙 :module은 밑줄 표기법을 사용 하는 것 입니다 (예 : api/v1not 'Api :: V1'). 언젠가 후자가 작동하지 않았지만 Rails 3.1에서 수정되었다고 생각합니다.

또한 API v3을 릴리스하면 경로가 다음과 같이 업데이트됩니다.

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

물론 API마다 버전마다 다른 경로가있을 수 있습니다.이 경우 다음을 수행 할 수 있습니다.

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

마지막 사건을 어떻게 처리 하시겠습니까? 즉 /api/asdf/users?뿐만 아니라 /api/users/1? 업데이트 된 답변에서 그 사실을 알 수 없었습니다. 그래서 당신은 방법을 알고있을 것이라고 생각했습니다
Ryan Bigg

쉽게 할 수있는 방법은 없습니다-모두 잡기 전에 모든 리디렉션을 정의해야하지만 / api / users / * path => / api / v2 / users와 같이 각 부모 리소스에 대해 각각 만 수행하면됩니다. / % {path}
pixeltrix 2014 년

13

가능한 경우 버전이 URL에 없지만 accepts 헤더에 들어가도록 URL을 다시 생각하는 것이 좋습니다. 이 스택 오버플로 답변은 잘 들어갑니다.

API 버전 관리 모범 사례?

이 링크는 레일 라우팅을 통해이를 수행하는 방법을 정확하게 보여줍니다.

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


이것은 또한 그것을 수행하는 훌륭한 방법이며 아마도 "/ api / asdf / users"요청을 충족시킬 것입니다.
Ryan Bigg

9

나는 노선별로 버전 관리를 좋아하지 않습니다. 보다 쉬운 API 버전 관리 를 지원하기 위해 VersionCake 를 구축했습니다 .

각각의 각 뷰 (jbuilder, RABL 등)의 파일 이름에 API 버전 번호를 포함하여 버전 관리를 방해하지 않고 이전 버전과의 호환성을 지원하기 위해 쉽게 저하되도록합니다 (예 : 뷰의 v5가 존재하지 않는 경우 뷰의 v4 렌더링).


8

버전이 명시 적으로 요청되지 않은 경우 왜 특정 버전 으로 리디렉션 해야하는지 잘 모르겠습니다 . 명시 적으로 요청 된 버전이없는 경우 제공되는 기본 버전을 정의하려는 것 같습니다. 또한 URL 구조에서 버전을 유지하는 것이 버전 관리를 지원하는 더 확실한 방법이라는 David Bock에 동의합니다.

뻔뻔한 플러그 : Versionist는 이러한 사용 사례 등을 지원합니다.

https://github.com/bploetz/versionist


2

Ryan Bigg의 답변이 저에게 효과적이었습니다.

리디렉션을 통해 쿼리 매개 변수를 유지하려면 다음과 같이하십시오.

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

2

오늘 이것을 구현하고 RailsCasts-REST API Versioning 에서 '올바른 방법'으로 생각되는 것을 발견했습니다 . 너무 간단합니다. 따라서 유지 관리가 가능합니다. 너무 효과적입니다.

추가 lib/api_constraints.rb(예 : 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

config/routes.rb그렇게 설정

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'

편집 컨트롤러 (예 /controllers/api/v1/squads_controller.rb)

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

그런 다음에서 응용 프로그램의 모든 링크를 변경할 수 있습니다 /api/v1/squads/api/squads당신은 할 수 있습니다 쉽게 도 링크를 변경하지 않고도 새로운 API 버전을 구현

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.