versioning.py

バージョニング

インターフェースのバージョニングとは、デプロイされたクライアントを「丁寧に」停止させる方法に過ぎません。

Roy Fielding.

APIバージョニングを使用すると、異なるクライアント間で動作を変更できます。REST frameworkは、さまざまなバージョニングスキームを提供します。

バージョニングは、着信クライアントリクエストによって決定され、リクエストURLまたはリクエストヘッダーに基づいて行われます。

バージョニングへのアプローチには、多くの有効な方法があります。バージョン管理されていないシステムも適切な場合があります。特に、制御できない複数のクライアントを持つ非常に長期的なシステムを設計している場合です。

REST frameworkでのバージョニング

APIバージョニングが有効になっている場合、request.version属性には、着信クライアントリクエストで要求されたバージョンに対応する文字列が含まれます。

デフォルトでは、バージョニングは有効になっておらず、request.versionは常にNoneを返します。

バージョンに基づいて動作を変更する

APIの動作をどのように変更するかはあなた次第ですが、一般的に必要となる例としては、新しいバージョンで異なるシリアライゼーションスタイルに切り替えることが挙げられます。例えば

def get_serializer_class(self):
    if self.request.version == 'v1':
        return AccountSerializerVersion1
    return AccountSerializer

バージョン管理されたAPIのURLを逆引きする

REST frameworkに含まれるreverse関数は、バージョニングスキームと連携しています。次のように、現在のrequestをキーワード引数として含める必要があります。

from rest_framework.reverse import reverse

reverse('bookings-list', request=request)

上記の関数は、リクエストバージョンに適したURL変換を適用します。例えば

  • NamespaceVersioningが使用されていて、APIバージョンが「v1」の場合、使用されるURLルックアップは'v1:bookings-list'となり、http://example.org/v1/bookings/のようなURLに解決される可能性があります。
  • QueryParameterVersioningが使用されていて、APIバージョンが1.0の場合、返されるURLはhttp://example.org/bookings/?version=1.0のようになります。

バージョン管理されたAPIとハイパーリンクシリアライザー

ハイパーリンクシリアライゼーションスタイルとURLベースのバージョニングスキームを一緒に使用する場合は、リクエストをシリアライザーのコンテキストとして含めるようにしてください。

def get(self, request):
    queryset = Booking.objects.all()
    serializer = BookingsSerializer(queryset, many=True, context={'request': request})
    return Response({'all_bookings': serializer.data})

そうすることで、返されるURLに適切なバージョニングを含めることができます。

バージョニングスキームの設定

バージョニングスキームは、DEFAULT_VERSIONING_CLASS設定キーによって定義されます。

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'
}

明示的に設定されていない限り、DEFAULT_VERSIONING_CLASSの値はNoneになります。この場合、request.version属性は常にNoneを返します。

個々のビューでバージョニングスキームを設定することもできます。通常、グローバルに単一のバージョン管理スキームを使用する方が理にかなっているため、これは必要ありません。必要な場合は、versioning_class属性を使用します。

class ProfileList(APIView):
    versioning_class = versioning.QueryParameterVersioning

その他のバージョニング設定

以下の設定キーもバージョニングの制御に使用されます

  • DEFAULT_VERSION。バージョニング情報が存在しない場合にrequest.versionに使用される値。デフォルトはNoneです。
  • ALLOWED_VERSIONS。設定されている場合、この値はバージョニングスキームによって返されるバージョンのセットを制限し、指定されたバージョンがこのセットにない場合はエラーを発生させます。 DEFAULT_VERSION設定に使用される値は、常にALLOWED_VERSIONSセットの一部と見なされることに注意してください(Noneでない限り)。デフォルトはNoneです。
  • VERSION_PARAM。メディアタイプまたはURLクエリパラメータなどのバージョニングパラメータに使用される文字列。デフォルトは'version'です。

独自のバージョン管理スキームを定義し、`default_version`、`allowed_versions`、`version_param`クラス変数を使用することで、ビューごと、またはビューセットごとにバージョン管理クラスとこれら3つの値を設定することもできます。たとえば、`URLPathVersioning`を使用する場合

from rest_framework.versioning import URLPathVersioning
from rest_framework.views import APIView

class ExampleVersioning(URLPathVersioning):
    default_version = ...
    allowed_versions = ...
    version_param = ...

class ExampleView(APIVIew):
    versioning_class = ExampleVersioning

APIリファレンス

AcceptHeaderVersioning

このスキームでは、クライアントは`Accept`ヘッダーのメディアタイプの一部としてバージョンを指定する必要があります。バージョンは、メインのメディアタイプを補足するメディアタイプパラメータとして含まれています。

acceptヘッダーバージョニングスタイルを使用したHTTPリクエストの例を次に示します。

GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0

上記の例のリクエストでは、`request.version`属性は文字列`'1.0'`を返します。

アクセプトヘッダーに基づくバージョニングは、一般的に ベストプラクティスと見なされていますが、クライアントの要件によっては他のスタイルが適している場合があります。

ベンダーメディアタイプでアクセプトヘッダーを使用する

厳密に言えば、`json`メディアタイプは追加パラメータを含むとは指定されていません。適切に指定されたパブリックAPIを構築している場合は、ベンダーメディアタイプの使用を検討することができます。そのためには、レンダラーをカスタムメディアタイプを持つJSONベースのレンダラーを使用するように設定します

class BookingsAPIRenderer(JSONRenderer):
    media_type = 'application/vnd.megacorp.bookings+json'

クライアントリクエストは次のようになります

GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/vnd.megacorp.bookings+json; version=1.0

URLPathVersioning

このスキームでは、クライアントはURLパスの一部としてバージョンを指定する必要があります。

GET /v1/bookings/ HTTP/1.1
Host: example.com
Accept: application/json

URLconfには、バージョンと一致する`'version'`キーワード引数を持つパターンを含める必要があります。これにより、この情報がバージョニングスキームで使用できるようになります。

urlpatterns = [
    re_path(
        r'^(?P<version>(v1|v2))/bookings/$',
        bookings_list,
        name='bookings-list'
    ),
    re_path(
        r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',
        bookings_detail,
        name='bookings-detail'
    )
]

NamespaceVersioning

クライアントにとって、このスキームは`URLPathVersioning`と同じです。唯一の違いは、Djangoアプリケーションでの設定方法です。URLキーワード引数の代わりにURL名前空間を使用します。

GET /v1/something/ HTTP/1.1
Host: example.com
Accept: application/json

このスキームでは、`request.version`属性は、着信リクエストパスと一致する`namespace`に基づいて決定されます。

次の例では、ビューのセットに2つの異なるURLプレフィックスを指定しています。それぞれ異なる名前空間の下にあります

# bookings/urls.py
urlpatterns = [
    re_path(r'^$', bookings_list, name='bookings-list'),
    re_path(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]

# urls.py
urlpatterns = [
    re_path(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
    re_path(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]

`URLPathVersioning`と`NamespaceVersioning`はどちらも、単純なバージョニングスキームが必要な場合に合理的です。 `URLPathVersioning`アプローチは、小規模なアドホックプロジェクトに適している可能性があり、`NamespaceVersioning`は大規模プロジェクトの管理が容易になる可能性があります。

HostNameVersioning

ホスト名バージョニングスキームでは、クライアントはURLのホスト名の一部として要求されたバージョンを指定する必要があります。

たとえば、`http://v1.example.com/bookings/` URLへのHTTPリクエストは次のとおりです

GET /bookings/ HTTP/1.1
Host: v1.example.com
Accept: application/json

デフォルトでは、この実装ではホスト名が次の単純な正規表現と一致することを想定しています

^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$

最初のグループは括弧で囲まれており、これがホスト名の一致部分であることを示していることに注意してください。

`HostNameVersioning`スキームは、`127.0.0.1`などの生のIPアドレスにアクセスすることが多いため、デバッグモードで使用するのが難しい場合があります。 カスタムサブドメインでlocalhostにアクセスする方法に関するさまざまなオンライントutorialがあり、この場合に役立つ場合があります。

ホスト名に基づくバージョニングは、バージョンに基づいて着信リクエストを異なるサーバーにルーティングする必要がある場合に特に役立ちます。異なるAPIバージョンに対して異なるDNSレコードを設定できるためです。

QueryParameterVersioning

このスキームは、URLのクエリパラメータとしてバージョンを含むシンプルなスタイルです。例えば

GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json

カスタムバージョニングスキーム

カスタムバージョニングスキームを実装するには、`BaseVersioning`をサブクラス化し、`.determine_version`メソッドをオーバーライドします。

次の例では、カスタム`X-API-Version`ヘッダーを使用して、要求されたバージョンを決定します。

class XAPIVersionScheme(versioning.BaseVersioning):
    def determine_version(self, request, *args, **kwargs):
        return request.META.get('HTTP_X_API_VERSION', None)

バージョニングスキームがリクエストURLに基づいている場合は、バージョン付きURLの決定方法も変更する必要があります。そのためには、クラスの`.reverse()`メソッドをオーバーライドする必要があります。例については、ソースコードを参照してください。