HelloGitHub- dream chasers

An API cannot stand still, and any addition or removal of an existing API will have an impact on the client that calls it. Without management of the addition and removal of apis, as the API is added and subtracted, the client calling it becomes increasingly confused as to which API is available. Why are previously available apis unavailable again? What new apis can be used? To facilitate API management, we introduced versioning functionality.

Putting a version number on an API means that an existing API is always available under a particular version. If you want to make a major change to an API, you can release a new version of the API and remind users of the change and urge users to migrate to the new API. This will provide a buffer for the client to avoid the API that was available yesterday and suddenly fails today.

Django-rest-framework provides several API versioning helper classes to implement different API versioning methods. Some practical ones are:

AcceptHeaderVersioning

This class requires the client to add a version number to the HTTP Accept request header to indicate the version of the API it wants to request, for example:

GET /bookings/ HTTP/1.1 Host: example.com Accept: application/json; Version = 1.0Copy the code

This requests an interface with a version number of 1.0.

URLPathVersioning

This class requires the client to specify the version number in the requested URL. One drawback is that you must include the version keyword when writing the URL schema.

urlpatterns = [
    url(
        r'^(? P
      
       (v1|v2))/bookings/$'
      ,
        bookings_list,
        name='bookings-list'
    ),
    url(
        r'^(? P
      
       (v1|v2))/bookings/(? P
       
        [0-9]+)/$'
       
      ,
        bookings_detail,
        name='bookings-detail')]Copy the code

It’s inconvenient, so we don’t usually use it.

NamespaceVersioning

This is similar to URLPathVersioning mentioned above, except that the version number is not specified in the URL schema, but through the namespace parameter (we’ll see how this is used later).

Django-rest-framework also provides other version management helper classes such as HostNameVersioning and QueryParameterVersioning. www.django-rest-framework.org/api-guide/v…

Taken together, the NamespaceVersioning pattern makes it easier to design and manage urls, so our blog application decided to use this API versioning approach.

To enable API version management, add the following configuration to the project configuration:

settings/common.py

REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning'.'DEFAULT_VERSION': 'v1'
}
Copy the code

The preceding two Settings specify the API version management mode used globally and the API version requested by default in the case of the default client version. Although these configuration items can also be specified within the scope of a single view or set of views, a uniform version management mode is preferred, so we specify them in the global configuration.

Then place the version number before the registered API:

blogproject/urls.py

urlpatterns = [
    #...
    path("api/v1/", include((router.urls, "api"), namespace="v1")),]Copy the code

Note that there is an additional namespace parameter, v1, indicating that the contained URL schema belongs to the v1 namespace. Also note that for the include function, if you specify a namespace value, the first parameter must be a tuple of the form :(url_patterns, app_name), where we specify app_name as API.

Once versioning is enabled, all request objects have an additional attribute version, which is the version number requested by the user (if not specified, the default value of DEFAULT_VERSION). Therefore, we can perform different code logic in the request for different versions of the request. For example, if our blog modifies the article list API, the serializer makes some changes to the returned data field, and publishes it in version V2, then it can return different data according to the version requested by the user. That is to add the API, but keep the original API compatible:

if request.version == 'v1':
	return PostSerializerV1()
return PostSerializer
Copy the code

If branch can be regarded as a temporary code, we can remind users that the API has changed, please migrate to the new version v2 as soon as possible, and at a future time, after verifying that most users have successfully migrated to the new version of the API, remove this code, and set the default version to V2. The original V1 version of the API was scrapped.

, of course, our current blog interface also no need to modify the upgrade temporarily, but in order to test the Settings take effect if the API version management, we believe that adding a test set of views, according to different versions of the request processing, inside do see different versions of the request would return to the different content in line with expectations.

Add a simple set of test views to blog/views.py. This set has a test interface. The interface processing logic returns different contents according to different version numbers:

class ApiVersionTestViewSet(viewsets.ViewSet):
    @action(
        methods=["GET"], detail=False, url_path="test", url_name="test".)def test(self, request, *args, **kwargs):
        if request.version == "v1":
            return Response(
                data={
                    "version": request.version,
                    "warning": "The version v1 of this interface is deprecated. Please migrate to version V2 as soon as possible.",})return Response(data={"version": request.version})
Copy the code

Of course, don’t forget to register the view set with the Router:

blogproject/urls.py

Use only for API version management testing
router.register(
    r"api-version", blog.views.ApiVersionTestViewSet, basename="api-version"
)
Copy the code

This is equivalent to an interface version update, and we add the interface to the v2 namespace:

urlpatterns = [
    path("api/v1/", include((router.urls, "api"), namespace="v1")),
    path("api/v2/", include((router.urls, "api"), namespace="v2")),]Copy the code

As you can see, the urls are the same except that namespace is v2.

Mysql > select * from test server where version = v1; mysql > select * from test server where version = v1

GET /api/v1/api-version/test/ HTTP 200 OK Allow: GET, HEAD, OPTIONS Content-Type: application/json Vary: Accept {"version": "v1", "warning": "The v1 version of this interface is deprecated, please migrate to v2 as soon as possible"}Copy the code

If you access the test interface with version number v2, you will return v2.

GET /api/v2/api-version/test/

HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "version": "v2"
}
Copy the code

All other interfaces, no matter v1 or V2, can be accessed. In this way, a compatible interface upgrade is completed.


Pay attention to the public account to join the communication group