so googling around it appears that the general consensus is that embedding version numbers in REST URIs is a bad practice and a bad idea.
even on SO there are strong proponents supporting this.
e.g. Best practices for API versioning?
My question is about how to accomplish the proposed solution of using the accept header / content negotiation in the django-rest-framework to accomplish this.
It looks like content negotiation in the framework,
http://django-rest-framework.org/api-guide/content-negotiation/ is already configured to automatically return intended values based on accepted MIME types. If I start using the Accept header for custom types, I'll lose this benefit of the framework.
Is there a better way to accomplish this in the framework?
API versioning allows you to alter behavior between different clients. REST framework provides for a number of different versioning schemes. Versioning is determined by the incoming client request, and may either be based on the request URL, or based on the request headers.
API versioning is the practice of transparently managing changes to your API. Managing an API boils down to defining and evolving data contracts and dealing with breaking changes. The most effective way to evolve your API without breaking changes is to follow effective API change management principles.
Django REST framework (DRF) is a powerful and flexible toolkit for building Web APIs. Its main benefit is that it makes serialization much easier. Django REST framework is based on Django's class-based views, so it's an excellent option if you're familiar with Django.
UPDATE:
versioning is now properly supported.
There are some answers from your link:
We found it practical and useful to put the version in the URL. It makes it easy to tell what you're using at a glance. We do alias /foo to /foo/(latest versions) for ease of use, shorter / cleaner URLs, etc, as the accepted answer suggests. Keeping backwards compatibility forever is often cost-prohibitive and/or very difficult. We prefer to give advanced notice of deprecation, redirects like suggested here, docs, and other mechanisms.
So we took this approach, plus allowing clients to specify the version in request header (X-Version), here is how we did it:
Structure in side the API app:
. ├── __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
project urls.py:
url(r'^api/', include('project.api.urls', namespace='api')),
api app level 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')), )
version level 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
create a middleware to switch to the correct code by changing the path_info, please note there is a caveat that namespace ('api') defined in project level urls is not flexible and needs to be known in middleware:
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)
Sample url:
curl -H "X-Version: v1" http://your.domain:8000/api/myaccount/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With