44

そのため、よく調べてみると、REST URIにバージョン番号を埋め込むことは悪い習慣であり、悪い考えであるというのが一般的なコンセンサスのようです。

SOでも、これを支持する強力な支持者がいます。
例:APIバージョニングのベストプラクティス?

私の質問は、これを達成するためにdjango-rest-frameworkでacceptヘッダー/コンテンツネゴシエーションを使用するという提案されたソリューションをどのように達成するかについてです。

フレームワークのコンテンツネゴシエーションのようです
。http://django-rest-framework.org/api-guide/content-negotiation/ は、受け入れられたMIMEタイプに基づいて目的の値を自動的に返すようにすでに構成されています。カスタムタイプにAcceptヘッダーを使い始めると、フレームワークのこの利点が失われます。

フレームワークでこれを達成するためのより良い方法はありますか?

4

3 に答える 3

49

アップデート:

バージョニングが適切にサポートされるようになりました。


あなたのリンクからいくつかの答えがあります:

URLにバージョンを含めることが実用的で便利であることがわかりました。何を使っているのか一目でわかります。受け入れられた答えが示唆するように、使いやすさ、より短い/よりクリーンなURLなどのために/fooを/foo /(最新バージョン)にエイリアスします。下位互換性を永久に維持することは、多くの場合、コストが高く、および/または非常に困難です。非推奨、ここで提案されているようなリダイレクト、ドキュメント、およびその他のメカニズムについて事前に通知することをお勧めします。

そのため、このアプローチに加えて、クライアントがリクエストヘッダー(X-Version)でバージョンを指定できるようにしました。これは、次のように行いました。

APIアプリ側の構造:

.
├── __init__.py
├── middlewares.py
├── urls.py
├── v1
│   ├── __init__.py
│   ├── account
│   │   ├── __init__.py
│   │   ├── serializers.py
│   │   └── views.py
│   └── urls.py
└── v2
    ├── __init__.py
    ├── account
    │   ├── __init__.py
    │   ├── serializers.py
    │   └── views.py
    └── urls.py

プロジェクトurls.py:

url(r'^api/', include('project.api.urls', namespace='api')),

apiアプリレベルのurls.py:

from django.conf.urls import *

urlpatterns = patterns('',
    url(r'', include('project.api.v2.urls', namespace='default')),
    url(r'^v1/', include('project.api.v1.urls', namespace='v1')),
)

バージョンレベルのurls.py

from django.conf.urls import *
from .account import views as account_views
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('account', account_views.AccountView)
router.register('myaccount', account_views.MyAccountView)
urlpatterns = router.urls

path_infoを変更して正しいコードに切り替えるミドルウェアを作成します。プロジェクトレベルのURLで定義された名前空間(「api」)は柔軟ではなく、ミドルウェアで認識されている必要があることに注意してください。

from django.core.urlresolvers import resolve
from django.core.urlresolvers import reverse


class VersionSwitch(object):

    def process_request(self, request):
        r = resolve(request.path_info)
        version = request.META.get('HTTP_X_VERSION', False)
        if r.namespace.startswith('api:') and version:
            old_version = r.namespace.split(':')[-1]
            request.path_info = reverse('{}:{}'.format(r.namespace.replace(old_version, version), r.url_name), args=r.args, kwargs=r.kwargs)

サンプルURL:

curl -H "X-Version: v1" http://your.domain:8000/api/myaccount/
于 2014-02-17T21:42:45.153 に答える
35

これを行う1つの方法は、メディアタイプの一部としてバージョン管理を指定することです。

これは、GitHubが現在APIに対して行っていることです

メディアタイプパラメータをacceptヘッダーに含めることもできます。たとえばAccept: application/json; version=beta、はと正常に一致しJSONRendererます。次に、受け入れられたメディアタイプに応じて異なる動作をするようにビューをコーディングできます。ここを参照してください。

APIでのバージョン管理にはさまざまなパターンがあり、正しいアプローチについてはまだ大きなコンセンサスがあるとは言えませんが、それは1つの合理的な可能性です。


2015年1月の更新:3.1.0リリースでは、より優れたバージョン管理サポートが提供される予定です。[このプルリクエスト]を参照してください

2015年3月の更新:バージョニングAPIのドキュメントが利用可能になりました。

https://github.com/tomchristie/django-rest-framework/pull/2285)詳細については。

于 2013-01-17T13:17:22.653 に答える
1

@ジェームスリンは素晴らしい答えを出しました。HyperlinkedRelatedField回答へのコメントで、@Mar0uxは壊れたフィールドをどうするかを尋ねました。

HyperlinkedRelatedField私はこれをに変更して、非常に明白ではないが、追加のパラメーターを渡してSerializerMethodField呼び出すことでこれを修正しました。reversecurrent_app

たとえば、名前空間バージョンが「v1」、「v2」のアプリ「fruits_app」があります。そして、Fruitモデルのシリアライザーを持っています。したがって、URLをシリアル化するために、フィールドを作成します

url = serializers.SerializerMethodField()

そして対応する方法:

def get_url(self, instance):
    reverse.reverse('fruits_app:fruit-detail',
        args=[instance.pk],
        request=request,
        current_app=request.version)

ネストされた名前空間では、それらの名前空間をcurrent_appに追加するように変更する必要があります。たとえば、名前空間バージョンが「v1」、「v2」、インスタンス名前空間が「bananas」のアプリ「fruits_app」がアプリ内にある場合、Fruiturlをシリアル化するメソッドは次のようになります。

def get_url(self, instance):
    reverse.reverse('fruits_app:fruit-detail',
        args=[instance.pk],
        request=request,
        current_app='bananas:{}'.format(request.version))
于 2021-04-13T14:50:54.070 に答える