Rozwijanie interfejsu API przy użyciu kontroli wersji

Łączniki niestandardowe dla aplikacji Azure Logic Apps, Microsoft Power Automate lub Microsoft Power Apps muszą zawierać plik specyfikacji OpenAPI. Ta specyfikacja interfejsu OpenAPI definiuje pojedyncze punkty wejścia zwane operacjami. Każda operacja ma unikatowy identyfikator operationId i unikatową kombinację parametrów urlPath i HttpVerb.

{
    "/items": {
        "get": {
            "summary": "Get rows",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems"
        },
        "post": {
            "summary": "Insert row",
            "description": "This operation inserts an item.",
            "operationId": "PostItem"
        }
    }
}

Te operacje mogą rosnąć i zmieniać się wraz z upływem czasu, gdy funkcje są dodawane lub rozszerzane. Niektóre zmiany polegają jedynie na dodawaniu elementów i nie muszą przerywać kontraktu, który istnieje między klientami i serwerami. Do tej kategorii mogą należeć operacje, takie jak dodawanie nowych parametrów, zwracanie większej ilości danych lub zezwalanie na bardziej elastyczne dane wejściowe.

Jednak wiele zmian może w rzeczywistości przerwać kontrakt opisany w specyfikacji interfejsu OpenAPI. Usuwanie parametrów, które nie obsługują już niektórych danych wejściowych, zmiana znaczenia i zachowania danych wejściowych, wyjściowych bądź samej operacji stanowią zmiany „powodujące niezgodność”.

Aby bezpiecznie rozwijać interfejs API, należy postępować zgodnie ze wzorcem, według którego mogą postępować klienci. Po stronie interfejsu API jest zapewnienie zgodności z poprzednimi wersjami, sygnalizowanie intencji i odróżnianie atrybutów obsługi wersji. Klient jest odpowiedzialny za pokazywanie lub ukrywanie operacji przestarzałych, wygasłych lub takich, które mogą mieć dostępne nowsze wersje. W ten sposób operacje mogą rosnąć i rozwijać się wraz z upływem czasu bez powodowania nadmiernej wrażliwości na aplikacje, które na nich bazują.

Adnotacja interfejsu API

Interfejs OpenAPI nie ma wewnętrznej obsługi wersji operacji. Zadanie to jest realizowane w większości przez obiekt x-ms-api-annotation, który jest stosowany zarówno w zakresie globalnym, jak i w zakresie operacji. Obiekt globalny zawiera właściwości, które mają zastosowanie do interfejsu API jako całości:

{
    "x-ms-api-annotation": {
        "status": "Preview"
    }
}
Właściwości Wartości Wartość domyślna Opis
status "Preview" "Production" "Preview" Stan interfejsu API jako całości — na początku ma on wartość Preview (wersja zapoznawcza) i stopniowo eskaluje do stanu Production (produkcja) w miarę zwiększania się użyteczności i stabilności

W zakresie operacji ten obiekt zawiera bardziej szczegółowe właściwości. Istnieją również dodatkowe właściwości poza obiektem, które mają zastosowanie i uczestniczą w procesie ewolucji wersji:

{
    "deprecated": true,
    "x-ms-api-annotation": {
        "status": "Production",
        "family": "MyOperation",
        "revision": 2
    }
}
Właściwość Wartości Wartość domyślna Opis
deprecated null false true false Wskazuje, czy operacja jest przestarzała
x-ms-visibility null "" "Important" "Advanced" "Internal" "" Zamierzony wgląd i wyeksponowanie operacji, gdzie null lub "" oznacza stan normalny
stan "Preview" "Production" "Production" Stan operacji — może się różnić od stanu samego interfejsu API, ale jeśli nie zostanie określony, będzie dziedziczyć stan interfejsu API najwyższego poziomu
family {nazwa pospolita operacji} operationName Nazwa odnosząca się do każdej wersji tej operacji
revision wartość liczbowa (1, 2, 3...) 1 Wersja określonej rodziny operacji
expires Data ISO8601 (brak) Opcjonalna wskazówka dla klienta wskazująca przewidywany koniec wsparcia technicznego

Właściwość deprecated można ustawić na wartość true, gdy nie jest już pożądane, aby klienci korzystali z tej operacji. Ta właściwość istnieje w specyfikacji pól stałych interfejsu OpenAPI.

Właściwość Visibility jest wskaźnikiem zamierzonego względnego wyeksponowania operacji. Widoczność o wartości "Important" wskazuje, że operacja powinna znajdować się na początku listy i powinna być wyświetlana w widocznym miejscu. Widoczność normalna (wskazywana przez wartość null lub pusty ciąg "") jest widocznością domyślną i oznacza, że operacja zostanie wyświetlona na liście prawdopodobnie po operacjach z widocznością o wartości Important. Widoczność o wartości "Advanced" wskazuje, że operacja może znajdować się w dolnej części listy lub nawet może być początkowo ukryta za kontrolką expando. Operacje o widoczności Advanced mogą być trudniejsze do wykonania, mniej popularne lub mogą mieć węższe zastosowanie. Widoczność o wartości "Internal" wskazuje, że operacja nie powinna być widoczna dla użytkowników i powinna być używana tylko wewnętrznie. Operacje wewnętrzne są programowo przydatne i cenne, ale nie są przeznaczone dla użytkowników końcowych. Operacje wewnętrzne mogą być również oznaczane jako takie w celu ich ukrycia przed wszystkimi interfejsami użytkowników w procesie wycofywania, bez ich rzeczywistego usunięcia z interfejsu API, co w przeciwnym razie spowodowałoby zmianę powodującą niezgodność.

Właściwość status wskazuje stabilność interfejsu API lub operacji. Wartość "Preview" wskazuje, że operacja lub interfejs API jest nowy i potencjalnie niesprawdzony. Wartość Preview oznacza, że systemy produkcyjne powinny być ostrożne przy zakładaniu zależności. Po pomyślnym ustanowieniu operacji (lub interfejsu API) i sprawdzeniu, że spełnia ona standardy niezawodności, ma wysoki współczynnik powodzeń i skalowalności, można celowo podwyższyć jej stan do wartości "Production".

Poniższe wymagania dotyczące metryk są ogólnie stosowane do operacji, których stan ma zostać podwyższony do wartości "Production":

  • Współczynnik powodzeń na poziomie 80% przez okres trzech tygodni
    • zdefiniowany jako procent kodów odpowiedzi HTTP w zakresie 2xx
  • Niezawodność na poziomie 99,9% utrzymująca się przez trzy tygodnie
    • zdefiniowana jako procent kodów odpowiedzi HTTP w zakresie innym niż 5xx (kody 502, 504 i 520 są wykluczone z tego obliczenia)

Właściwość family opisuje relację między operacjami, które są koncepcyjnie takie same, ale mają różne wersje i mogą występować między nimi zmiany powodujące niezgodność. Jedną nazwą rodziny może być określanych wiele operacji, jeśli powinny one być uznawane za wersje tej samej operacji. W takim przypadku są one rozróżniane za pomocą kolejno nadawanych numerów wersji.

Właściwość revision wskazuje kolejność rozwijania operacji w ramach rodziny operacji. Poszczególne operacje w ramach rodziny mają wersje będące integralnym indeksem określającym ich kolejność. Wersja pusta zostanie uznana za wersję numer 1. Jeśli są dostępne nowsze wersje operacji, klienci powinny je wyświetlać w pierwszej kolejności i celowo zalecać ich użycie, zezwalając jednak nadal na wybór potencjalnie starszych wersji, które nie zostały jeszcze uznane za przestarzałe.

Właściwość Expires jest opcjonalna i wskazuje potencjalny termin końca okresu istnienia, od którego wsparcie dla operacji nie jest już gwarantowane. Tę właściwość należy ustawiać tylko dla operacji przestarzałych. Obecnie nie jest ona odzwierciedlana w żadnym interfejsie.

Okres istnienia operacji

Operacje mają przewidywalny okres istnienia, który można pokazać na przykładzie.

Punkt początkowy

Początkowo operacje nie muszą mieć żadnych wersji. Dla takich operacji są stosowane wartości domyślne, zatem przyjmuje się dla nich, że mają wersję numer 1 z nazwą rodziny pełniącą rolę identyfikatora operationId.

{
    "/{list}/items": {
        "get": {
            "summary": "Get rows",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems"
        }
    }
}

Taka definicja w pełniejszej wersji wygląda następująco:

{
    "/{list}/items": {
        "get": {
            "summary": "Get rows",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems",
            "deprecated": false,
            "x-ms-api-annotation": {
                "status": "Production",
                "family": "GetItems",
                "revision": 1
            }
        }
    }
}

Inicjacja operacji

Większość działań rozwijających interfejs API polega na dodawaniu operacji. Są to na przykład nowe metody i nowe wersje istniejących metod. Aby bezpiecznie zainicjować nową wersję, należy dostosować specyfikację interfejsu OpenAPI w następujący sposób:

{
    "/{list}/items": {
        "get": {
            "summary": "Get rows (V1 - downplayed)",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems",
            "deprecated": false,
            "x-ms-visibility": "advanced",
            "x-ms-api-annotation": {
                "status": "Production",
                "family": "GetItems",
                "revision": 1
            }
        }
    }
    "/v2/{list}/items": {
        "get": {
            "summary": "Get rows (V2 - new hotness)",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems_V2",
            "deprecated": false,
            "x-ms-api-annotation": {
                "status": "Preview",
                "family": "GetItems",
                "revision": 2
            }
        }
    }
}

Ważne

Zwróć uwagę, że element GetItems V2 ma unikatowy identyfikator operationId i początkowo ma stan podglądu. Zauważ również, że element GetItems v1 ma teraz widoczność o wartości advanced, dlatego nie będzie wyświetlany w widocznym miejscu.

Przestarzała operacja

Czasami istniejące punkty wejścia wersji V1 pozostają na stałe, jeśli ciągle zapewniają wartość i nie ma żadnych istotnych powodów, aby je zakończyć. Jednak wiele punktów wejścia wersji V2 celowo zastępuje punkt wejścia V1. Aby można było to bezpiecznie wykonać, cały ruch oryginalnej operacji powinien zostać zatrzymany do zera. Gdy dane telemetryczne potwierdzą osiągnięcie takiego stanu, można wprowadzić następujące zmiany:

{
    "/{list}/items": {
        "get": {
            "summary": "Get rows (deprecated)",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems",
            "deprecated": true,
            "x-ms-api-annotation": {
                "status": "Production",
                "family": "GetItems",
                "revision": 1
            }
        }
    }
    "/v2/{list}/items": {
        "get": {
            "summary": "Get rows",
            "description": "This operation gets a list of items.",
            "operationId": "GetItems_V2",
            "deprecated": false,
            "x-ms-api-annotation": {
                "status": "Production",
                "family": "GetItems",
                "revision": 2
            }
        }
    }
}

Ważne

Zauważ, że element GetItems V1 jest teraz oznaczony jako przestarzały. Jest to końcowe przekształcenie prowadzące do wycofania operacji. Element GetItems V2 całkowicie zastąpił element GetItems V1.

Po co zawracać sobie tym głowę?

Istnieje wiele powodów, dla których warto stosować obsługę wersji operacji. Podstawową zaletą stosowania tego podejścia jest to, że klienci, tacy jak usługi Azure Logic Apps i Power Automate, będą nadal działały prawidłowo, gdy użytkownicy zintegrują operacje łącznika w swoich przepływach danych. Operacje powinny być oznaczane wersjami za pomocą powyższej metody zawsze wtedy, gdy zachodzą następujące sytuacje:

  • Zostanie dodana nowa wersja operacji
  • Istniejąca operacja dodaje lub usuwa parametry
  • Istniejąca operacja w znaczący sposób zmienia dane wejściowe lub wyjściowe

Mówiąc dokładnie

Istnieją sytuacje, w których można sobie poradzić bez obsługi wersji — należy jednak wtedy postępować ostrożnie i wykonywać wiele testów, aby się upewnić, że nie przeoczono żadnych przypadków brzegowych, w których użytkownicy mogą nieoczekiwanie napotkać problemy. Tutaj przedstawiono krótką listę przypadków, w których można się obyć bez kontroli wersji:

  • Dodawana jest nowa operacja.

    Nie powinno to spowodować odłączenia istniejących klientów.

  • Dodawany jest nowy, opcjonalny parametr do istniejącej operacji.

    Nie powinno to spowodować zerwania istniejących wywołań, ale należy to dokładnie przemyśleć.

  • Istniejąca operacja nieznacznie zmienia zachowanie.

    Biorąc pod uwagę charakter zmiany i uzależnienie od niej użytkowników, prawdopodobnie nie spowoduje to zerwania istniejących obiektów wywołujących. Jest to najbardziej niepewna sytuacja ze wszystkich opisanych, ponieważ znacząca różnica w akceptowaniu danych wejściowych, generowaniu danych wyjściowych, chronometrażu lub przetwarzaniu może mieć wpływ na zachowanie operacji w sposób, który może spowodować, że trudno będzie ocenić ryzyko towarzyszące zmianie.

W przypadku wprowadzania jakiejkolwiek znaczącej zmiany interfejsu API zawsze zaleca się daleko idącą ostrożność i iterowanie wersji.

Przekazywanie opinii

Jesteśmy wdzięczni za opinie na temat problemów z platformą łączników oraz pomysły na nowe funkcje. Aby przekazać opinię, przejdź na stronę Przesyłanie problemów lub uzyskiwanie pomocy dotyczącej łączników i wybierz typ opinii.