Версия на REST API

1. Проблемът

Разработването на REST API е труден проблем - такъв, за който са налични много опции. Тази статия разглежда някои от тези опции.

2. Какво има в договора?

Преди всичко друго, трябва да отговорим на един прост въпрос: Какъв е Договорът между API и Клиента?

2.1. URI част от договора?

Нека първо разгледаме структурата на URI на REST API - това част от договора ли е? Трябва ли клиентите да поставят отметки, твърди кодове и като цяло да разчитат на URI на API?

Ако случаят е такъв, тогава взаимодействието на Клиента с REST услугата вече няма да се управлява от самата Услуга, а от това, което Рой Филдинг нарича информация извън обхвата :

Трябва да се въведе REST API без предварителни познания извън първоначалния URI (отметка) и набор от стандартизирани типове медии, които са подходящи за целевата аудитория ... Неуспехът тук означава, че извънбандовата информация води до взаимодействие вместо хипертекст.

Така че очевидно URI не са част от договора ! Клиентът трябва да знае само един URI - входната точка към API. Всички други URI трябва да бъдат открити, докато консумирате API.

2.2. Типове носители част от договора?

Какво ще кажете за информацията за типа медия, използвана за представяне на ресурси - това част от договора между клиента и услугата ли е?

За да използва успешно API, клиентът трябва да има предварителни познания за тези типове медии . Всъщност определението за тези типове медии представлява целия договор.

Следователно това е мястото, където REST услугата трябва да се фокусира най-много:

API REST трябва да прекара почти всичките си описателни усилия в дефиниране на типа (ите) на медиите, използвани за представяне на ресурси и задвижване на състоянието на приложението, или в дефинирането на разширени имена на релации и / или надценка с активиран хипертекст за съществуващите стандартни типове медии.

Така че определенията на Media Type са част от договора и трябва да бъдат предварителни познания за клиента, който използва API. Тук идва стандартизацията.

Сега имаме добра представа какъв е договорът, нека преминем към това как всъщност да се справим с проблема с версиите.

3. Опции на високо ниво

Нека сега обсъдим подходите на високо ниво за създаване на версии на REST API:

  • URI Versioning - версия на URI пространството, използвайки индикатори за версия
  • Версия на носителя на носителя - версия, представляваща представянето на ресурса

Когато въведем версията в пространството на URI, Представянията на ресурси се считат за неизменни. Така че, когато трябва да се въведат промени в API, трябва да се създаде ново пространство на URI.

Например, да кажем, че API публикува следните ресурси - потребители и привилегии:

//host/v1/users //host/v1/privileges

Сега, нека помислим, че пробивната промяна в потребителския API изисква въвеждането на втора версия:

//host/v2/users //host/v2/privileges

Когато модифицираме Media Type и разширим езика, преминаваме през Договаряне на съдържание въз основа на този заглавие. API REST ще използва персонализирани типове медийни носители на MIME вместо общи типове медии като application / json . Ще опровергаем тези типове медии вместо URI.

Например:

===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v1+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v1+json { "user": { "name": "John Smith" } }

Можем да разгледаме статията „Персонализирани типове носители за API за почивка“ за допълнителна информация и примери по този въпрос.

Тук е важно да се разбере, че клиентът не прави предположения за структурата на отговора извън определеното в типа медия.

Ето защо родовите типове медии не са идеални. Те не предоставят достатъчно семантична информация и принуждават клиента да използва изискват допълнителни подсказки за обработка на действителното представяне на ресурса.

Изключение от това е използването на друг начин за еднозначно идентифициране на семантиката на съдържанието - например XML схема.

4. Предимства и недостатъци

Сега, след като имаме ясна концепция за това какво е част от Договора между Клиента и Услугата, както и общ преглед на опциите за версия на API, нека обсъдим предимствата и недостатъците на всеки подход.

Първо, въвеждането на идентификатори на версии в URI води до много голям отпечатък на URI. Това се дължи на факта, че всяка пробивна промяна в който и да е от публикуваните API ще въведе изцяло ново дърво на представяния за целия API. С течение на времето това се превръща в тежест за поддържане, както и в проблем за клиента - който сега има повече възможности за избор.

Идентификаторите на версиите в URI също са силно негъвкави . Няма начин просто да се развие API на отделен ресурс или малко подмножество на цялостния API.

Както споменахме по-рано, това е подход или всичко или нищо. Ако част от API се премести в новата версия, тогава целият API трябва да се движи заедно с него. Това също прави надграждането на клиентите от v1 до v2 голямо начинание - което води до по-бавни надстройки и много по-дълги периоди на залез за старите версии.

HTTP кеширането също е основна грижа по отношение на версирането.

От гледна точка на прокси кешовете в средата, всеки подход има предимства и недостатъци. Ако URI е версия, тогава кешът ще трябва да съхранява множество копия на всеки ресурс - по едно за всяка версия на API. Това натоварва кеша и намалява честотата на удара в кеша, тъй като различните клиенти ще използват различни версии.

Also, some cache invalidation mechanisms will no longer work. If the media type is the one that is versioned, then both the Client and the Service need to support the Vary HTTP header to indicate that there are multiple versions being cached.

From the perspective of client caching however, the solution that versions the media type involves slightly more work than the one where URIs contain the version identifier. This is because it's simply easier to cache something when its key is an URL than a media type.

Let's end this section with defining some goals (straight out of API Evolution):

  • keep compatible changes out of names
  • avoid new major versions
  • makes changes backwards-compatible
  • think about forwards-compatibility

5. Possible Changes to the API

Next, let's consider the types of changes to the REST API – these are introduced here:

  • representation format changes
  • resource changes

5.1. Adding to the Representation of a Resource

The format documentation of the media type should be designed with forward compatibility in mind. Specifically, a client should ignore information that it doesn't understand (which JSON does better than XML).

Now, adding information in the Representation of a resource will not break existing clients if these are correctly implemented.

To continue our earlier example, adding the amount in the representation of the user will not be a breaking change:

{ "user": { "name": "John Smith", "amount": "300" } }

5.2. Removing or Changing an Existing Representation

Removing, renaming or generally restructuring information in the design of existing representations is a breaking change for clients. This is because they already understand and rely on the old format.

This is where Content Negotiation comes in. For such changes, we can add a new vendor MIME media type.

Let's continue with the previous example. Say we want to break the name of the user into firstname and lastname:

===> GET /users/3 HTTP/1.1 Accept: application/vnd.myname.v2+json <=== HTTP/1.1 200 OK Content-Type: application/vnd.myname.v2+json { "user": { "firstname": "John", "lastname": "Smith", "amount": "300" } }

As such, this does represent an incompatible change for the Client – which will have to request the new Representation and understand the new semantics. However, the URI space will remain stable and will not be affected.

5.3. Major Semantic Changes

These are changes in the meaning of the Resources, the relations between them or what the map to in the backend. This kind of changes may require a new media type, or they may require publishing a new, sibling Resource next to the old one and making use of linking to point to it.

While this sounds like using version identifiers in the URI all over again, the important distinction is that the new Resource is published independently of any other Resources in the API and will not fork the entire API at the root.

The REST API should adhere to the HATEOAS constraint. According to this, most of the URIs should be DISCOVERED by Clients, not hardcoded. Changing such an URI should not be considered an incompatible change. The new URI can replace the old one and Clients will be able to re-discover the URI and still function.

It's worth noting however that, while using version identifiers in the URI is problematic for all of these reasons, it is not un-RESTful in any way.

6. Conclusion

This article tried to provide an overview of the very diverse and difficult problem of evolving a REST Service. We discussed the two common solutions, advantages and disadvantages of each one, and ways to reason about these approaches in the context of REST.

Статията завършва, като обосновава второто решение - версия на типовете носители, като същевременно разглежда възможните промени в RESTful API.

Пълното изпълнение на този урок може да бъде намерено в проекта GitHub.

7. Допълнително четене

Обикновено тези ресурси за четене са свързани в цялата статия, но в този случай просто има твърде много добри:

    • REST API трябва да бъдат управлявани от хипертекст
    • API Evolution
    • Свързване за HTTP API
    • Стратегии за съвместимост