Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tym artykule opisano najlepsze rozwiązania dotyczące implementowania, wdrażania i obsługi internetowego interfejsu API RESTful. Koncentruje się on na obsłudze żądań, wydajności, niezawodności, publikowaniu i monitorowaniu. Aby uzyskać wskazówki dotyczące modelowania zasobów i projektowania interfejsu API, zobacz Projektowanie interfejsu Web API.
Wskazówki dotyczące architektury mają zastosowanie do języków implementacji i frameworków. Przykłady kontrolerów ASP.NET Core w języku C#.
Przetwarzanie żądań
Podczas implementowania kodu przeznaczonego do obsługi żądań należy wziąć pod uwagę poniższe kwestie.
Zachowaj semantykę metody HTTP i zaimplementuj idempotentność
Użyj polecenia GET, aby zwrócić dane zasobów i HEAD, aby zwrócić nagłówki odpowiedzi bez treści. Nie używaj żadnej z tych metod do tworzenia, aktualizowania ani usuwania zasobów.
Żądania GET, HEAD, PUT i DELETE muszą być idempotentne, więc powtarzanie identycznego żądania ma taki sam zamierzony efekt jak wykonanie go raz. Na przykład funkcja DELETE zmienia stan zasobu, ale powtarzanie go nie powoduje usunięcia dodatkowych zasobów. Pierwsze żądanie DELETE może zwrócić 204 (Brak zawartości), podczas gdy kolejne żądanie zwraca wartość 404 (Nie znaleziono).
POST i PATCH nie są z natury idempotentne. Operacja PATCH, która ustawia właściwość na wartość, może być idempotentna, ale taka, która zwiększa licznik, nie jest. Udokumentuj, które operacje zmieniające stan klienci mogą bezpiecznie ponawiać. Jeśli obsługujesz ponawianie prób dla operacji, które nie są naturalnie idempotentne, zaimplementuj i udokumentuj sposób, w jaki usługa zapobiega zduplikowanym efektom. Aby uzyskać informacje na temat semantyki metod, zobacz Metody żądań HTTP.
Uwaga
Aby uzyskać wskazówki dotyczące implementacji, zobacz Wzorzec idempotentnego konsumenta. W praktyce najpierw zidentyfikuj, czy operacja jest naturalnie idempotentna. Jeśli tak nie jest, zastosuj sztuczną idempotentność, śledząc przetworzone identyfikatory komunikatów i obsługując duplikaty aktywnie lub reaktywnie.
Weryfikowanie danych żądania i kontrola dostępu do pól
Zdefiniuj modele żądań i odpowiedzi, które uwidaczniają tylko pola w kontrakcie interfejsu API. Zamapuj te modele na przechowywane dane zamiast wiązać jednostki żądań bezpośrednio z jednostkami bazy danych lub zwracać wszystkie zapisane właściwości. Zapobiegaj wywołującym zmianie właściwości chronionych przez przesyłanie dodatkowych pól i pomijaj właściwości, których nie są oni upoważnieni odczytywać. Obiekty transferu danych (DTO) to jeden ze sposobów wymuszania tej granicy. Aby uzyskać więcej informacji, zobacz Zapobieganie nadmiernemu publikowaniu w ASP.NET Core.
Przed zapisaniem zmian zweryfikuj wymagane pola, typy, zakresy i reguły biznesowe. Jeśli obsługujesz wybór pól, przed projekcją odpowiedzi zweryfikuj żądane pola na podstawie listy dozwolonych pól oraz uprawnień osoby wywołującej. Zastosuj udokumentowane limity do filtrowania i sortowania. Odpowiednie opcje umowy można znaleźć w temacie Implement pagination and filtering (Implementowanie stronicowania i filtrowania).
Ogranicz skutki uboczne akcji POST, które tworzą zasoby
Gdy żądanie POST utworzy zasób, ogranicz jego skutki do tego zasobu i bezpośrednio powiązanych zasobów. Na przykład utworzenie zamówienia może również zaktualizować stan magazynowy i wygenerować informacje o rozliczeniach. Nie należy modyfikować niepowiązanych informacji.
Unikaj implementowania operacji POST, PUT i DELETE, które są zbyt rozbudowane lub zbyt szczegółowe.
Unikaj projektowania interfejsu API na podstawie operacji czatty . Każde żądanie ma obciążenie związane z protokołem, siecią i obliczeniami. Na przykład uruchomienie 100 mniejszych żądań zamiast jednego większego żądania wsadowego powoduje naliczenie dodatkowego obciążenia na kliencie, w sieci i na serwerze zasobów. W przypadku obciążeń wymagających operacji zbiorczych rozważ ograniczone żądania wsadowe zamiast wielu pojedynczych żądań.
- Żądanie GET do kolekcji może jednocześnie pobrać wiele zasobów.
- Żądanie POST może zawierać szczegóły dla wielu nowych zasobów i dodać je wszystkie do tej samej kolekcji.
- Żądanie PUT może zastąpić cały zestaw zasobów w kolekcji.
- Żądanie DELETE może usunąć całą kolekcję.
Zdefiniuj maksymalne rozmiary partii, rozmiary danych wejściowych, czasy wykonywania i współbieżność. Określ, czy partia jest niepodzielna, czy może częściowo zakończyć się powodzeniem, i zwróć wynik dla każdej operacji. Autoryzuj i zweryfikuj każdą operację w partii.
Jeśli używasz usługi OData, możesz użyć ASP.NET Core OData do implementowania żądań wsadowych. Aby uzyskać więcej informacji, zobacz omówienie ASP.NET Core OData.
Wysyłanie odpowiedzi zgodnie ze specyfikacją protokołu HTTP
Zwróć odpowiedni kod stanu HTTP, nagłówki i format treści, aby klienci mogli interpretować i obsługiwać odpowiedź.
Na przykład operacja POST, która tworzy zasób synchronicznie, powinna zwrócić wartość 201 (Utworzono) i zidentyfikować zasób w nagłówku odpowiedzi Location. Operacja akceptująca pracę na potrzeby późniejszego przetwarzania może zwrócić wartość 202 (Accepted), ale ta odpowiedź nie wskazuje, że praca się powiodła. Nie dołączaj treści odpowiedzi dla kodów 204 (brak zawartości) lub 304 (niezmodyfikowane). Aby uzyskać więcej informacji, zobacz Metody żądań HTTP.
Wsparcie dla negocjowania zawartości
Treść odpowiedzi może zawierać dane w różnych formatach, takich jak JSON lub XML. Klienci mogą używać Accept nagłówka żądania do określania obsługiwanych typów multimediów. Na przykład klient żądający obrazu może określić wartość image/jpeg, image/gif, image/png. Zwróć obsługiwaną reprezentację i zidentyfikuj typ nośnika w nagłówku Content-Type odpowiedzi.
Jeśli klient nie określi nagłówka Accept , użyj udokumentowanego formatu domyślnego. Kontrolery ASP.NET Core domyślnie używają formaterów JSON. Zdefiniuj, co się stanie, gdy interfejs API nie może utworzyć żądanej reprezentacji. Na przykład skonfiguruj interfejs API tak, aby zwracał wartość 406 (Nie do przyjęcia), a nie bez komunikatu o błędzie zwracał nieobsługiwany format. Aby uzyskać więcej informacji, zobacz Formatowanie danych odpowiedzi w ASP.NET Core Web API.
W przypadku żądań z treścią sprawdź element Content-Type żądania i zaakceptuj tylko formaty obsługiwane przez operację. Zwróć 415 (Nieobsługiwany typ multimediów) dla nieobsługiwanego formatu. Ta kontrola jest niezależna od negocjacji odpowiedzi za pośrednictwem metody Accept. W ASP.NET Core skonfiguruj formatery danych wejściowych i użyj [Consumes], gdy musisz ograniczyć formaty żądań dla operacji. Aby uzyskać więcej informacji, zobacz Tworzenie internetowych interfejsów API za pomocą ASP.NET Core.
Zaimplementuj wybraną strategię wersjonowania interfejsu API
Zastosuj strategię przechowywania wersji wybraną podczas projektowania spójnie w aplikacji i bramie. Kieruj żądania do programu obsługi i schematu odpowiedzi dla wybranej wersji. Zdefiniuj zachowanie pomijanych, źle sformułowanych i nieobsługiwanych identyfikatorów wersji zamiast dyskretnie zmieniać kontrakt odbierany przez istniejącego wywołującego.
Zachowaj zgodność żądań i odpowiedzi w obsługiwanych wersjach. Po wprowadzeniu zmiany powodującej niezgodność podaj nową wersję i zachowaj obsługiwane wcześniejsze wersje. Obsługa żądań ze starszych wywołań, które pomijają nowo dodane pola opcjonalne. Aby uzyskać więcej informacji, zobacz Obsługa wersji interfejsu API i zgodność.
Zachowaj wygenerowane linki zasobów i buforowane reprezentacje zgodne z wybraną wersją. W przypadku przechowywania wersji opartych na nagłówku upewnij się, że pamięci podręczne rozróżniają żądania dla różnych wersji zamiast polegać tylko na identyfikatorze URI.
Ustalanie i autoryzacja kontekstu dzierżawy
W przypadku wielodzierżawnego interfejsu API określ dzierżawcę dla każdego żądania i sprawdź, czy uwierzytelniony obiekt wywołujący może uzyskać dostęp do tego dzierżawcy. Traktuj identyfikatory dzierżawy w ścieżkach, nazwach hostów i nagłówkach jako tylko dane wejściowe routingu. Aby uzyskać więcej informacji, zobacz Mapuj żądania do dzierżawcy w rozwiązaniu multitenantowym.
Spójnie używaj autoryzowanego kontekstu dzierżawcy podczas wykonywania zapytań lub aktualizowania danych oraz podczas przekazywania pracy do procesorów w tle. Jeśli pamięć podręczna aplikacji lub bramy przechowuje dane specyficzne dla dzierżawcy, dołącz identyfikator dzierżawcy do klucza pamięci podręcznej. Aby uzyskać wskazówki specyficzne dla bramy, zobacz Funkcje usługi API Management, które obsługują wielodzierżawność.
Zapewnienie linków umożliwiających obsługę nawigacji w stylu architektury HATEOAS oraz odnajdywania zasobów
Hipertekst jako silnik stanu aplikacji (HATEOAS) używa linków, aby ułatwić klientom odnajdywanie i nawigowanie po zasobach. Na przykład odpowiedź zawierająca szczegóły klienta może zawierać linki do zamówień tego klienta. Opisz operacje obsługiwane przez każdy połączony zasób, takie jak GET, POST, PUT i DELETE, i podaj odpowiednie identyfikatory URI. Aby uzyskać więcej informacji, zobacz Projektowanie interfejsu API.
W poniższym przykładzie użyto reprezentacji łącza zdefiniowanego przez aplikację. Udokumentuj te konwencje, zamiast zakładać, że klienci mogą interpretować niestandardowe pola linków. Żądanie GET dla klienta zwraca linki do zamówień tego klienta:
GET https://api.contoso.com/customers/2 HTTP/1.1
Accept: application/json
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
...
Content-Length: ...
{"customerId": 2,"customerName": "Bert","links": [
{"rel": "self",
"href": "https://api.contoso.com/customers/2",
"action": "GET",
"types": ["text/xml","application/json"]},
{"rel": "self",
"href": "https://api.contoso.com/customers/2",
"action": "PUT",
"types": ["application/x-www-form-urlencoded"]},
{"rel": "self",
"href": "https://api.contoso.com/customers/2",
"action": "DELETE",
"types": []},
{"rel": "orders",
"href": "https://api.contoso.com/customers/2/orders",
"action": "GET",
"types": ["text/xml","application/json"]},
{"rel": "orders",
"href": "https://api.contoso.com/customers/2/orders",
"action": "POST",
"types": ["application/x-www-form-urlencoded"]}
]}
Poniższy Customer rekord reprezentuje dane klienta. Jego Links właściwość zawiera linki HATEOAS.
public sealed record Customer(
int CustomerId,
string CustomerName,
IReadOnlyList<Link> Links);
public sealed record Link(
string Rel,
string Href,
string Action,
IReadOnlyList<string> Types);
Operacja GET pobiera dane klienta z magazynu, tworzy Customer rekord z linkami i zwraca odpowiedź JSON. Każdy link zawiera następujące pola:
- Relacja (
Rel) między zwróconym zasobem a połączonym zasobem. W tym przykładzieselfodwołuje się do klienta iordersodwołuje się do kolekcji zamówień klienta. - Hiperlink (
Href) dla obiektu opisywanego przez łącze w postaci identyfikatora URI. - Typ żądania HTTP (
Action), który można wysłać do URI. - Format dowolnych danych (
Types), który powinien zostać podany w żądaniu HTTP lub może zostać zwrócony w odpowiedzi — w zależności od typu żądania.
Linki HATEOAS przedstawione w przykładowej odpowiedzi HTTP oznaczają, że aplikacja kliencka może wykonywać następujące operacje:
- Żądanie HTTP GET do
https://api.contoso.com/customers/2, aby pobrać szczegóły klienta jako XML lub JSON. - Żądanie HTTP PUT do
https://api.contoso.com/customers/2w celu zmodyfikowania szczegółów klienta. Treść żądania używa następującego formatu:application/x-www-form-urlencoded. - Żądanie HTTP DELETE w celu
https://api.contoso.com/customers/2usunięcia rekordu klienta. Ta operacja nie wymaga treści żądania i nie zwraca żadnej treści odpowiedzi. - Żądanie HTTP GET do
https://api.contoso.com/customers/2/ordersw celu pobrania zamówień klienta jako XML lub JSON. - Żądanie HTTP POST do
https://api.contoso.com/customers/2/ordersw celu utworzenia zamówienia dla klienta. Treść żądania używa następującego formatu:application/x-www-form-urlencoded.
Obsługa wyjątków
Jeśli operacja zgłasza wyjątek, który nie jest obsługiwany, rozważ poniższe wskazówki.
Przechwytywanie wyjątków i zwracanie klientom zrozumiałej odpowiedzi
Obsługuj wyjątki spójnie i zwracaj sensowną odpowiedź o błędzie, którą można bezpiecznie udostępnić klientom. Nie zwracaj obiektów wyjątków, śladów stosu ani wewnętrznych szczegółów implementacji w odpowiedziach produkcyjnych. Zwróć kod stanu HTTP, który odzwierciedla błąd zamiast zwracać kod stanu 500 dla każdej sytuacji. Jeśli na przykład żądanie użytkownika powoduje aktualizację bazy danych, która narusza ograniczenie (takie jak próba usunięcia klienta, który ma zaległe zamówienia), zwróć wartość 409 (konflikt) i bezpieczne wyjaśnienie. Zwróć wartość 400 (Nieprawidłowe żądanie) dla nieprawidłowych danych wejściowych lub błędów walidacji żądania, a nie w przypadku nieoczekiwanych błędów serwera. Aby zapoznać się z definicjami stanu HTTP, zobacz Kody stanu. Aby uzyskać wskazówki dotyczące implementacji, zobacz Obsługa błędów w ASP.NET Core.
Poniższy kontroler mapuje oczekiwane wyniki domeny na odpowiedzi HTTP. Operacja w repozytorium sprawdza autoryzację i zaległe zamówienia w ramach operacji usuwania. Nieoczekiwane błędy są propagowane do scentralizowanej obsługi wyjątków, zamiast być po cichu przechwytywane w każdej akcji.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Authorize]
[Route("customers")]
public sealed class CustomersController(ICustomerRepository repository) : ControllerBase
{
[HttpDelete("{id:int}")]
public async Task<IActionResult> DeleteCustomer(int id, CancellationToken cancellationToken)
{
var outcome = await repository.DeleteAuthorizedAsync(id, User, cancellationToken);
return outcome switch
{
DeleteOutcome.Deleted => NoContent(),
DeleteOutcome.NotFound => NotFound(),
DeleteOutcome.HasOutstandingOrders => Conflict(new ProblemDetails
{
Status = StatusCodes.Status409Conflict,
Title = "The customer has outstanding orders."
}),
_ => throw new InvalidOperationException("Unknown deletion outcome.")
};
}
}
public enum DeleteOutcome
{
Deleted,
NotFound,
HasOutstandingOrders
}
Important
Nie dołączaj informacji, które mogą być przydatne dla osoby atakującej próbującej uzyskać nieautoryzowany dostęp do interfejsu API.
Warstwa hostingu może odrzucać żądania, zanim dotrą do interfejsu API. Na przykład błędy uwierzytelniania mogą spowodować 401 (nieautoryzowane) odpowiedzi. Po uwierzytelnieniu sprawdź, czy wywołujący ma autoryzację dostępu do żądanego zasobu. Zwróć błąd 403 (Forbidden), gdy autoryzacja się nie powiedzie.
Spójna obsługa wyjątków i rejestrowania informacji o błędach
Aby spójnie obsługiwać wyjątki, rozważ zaimplementowanie globalnej strategii obsługi błędów w całym interfejsie API sieci Web. Rejestruj informacje diagnostyczne na serwerze, ale wyklucz tajne informacje, tokeny dostępu i poufne dane osobowe. Usuń poufne wartości przed zapisaniem wpisów dziennika i ogranicz dostęp do dziennika do autoryzowanych użytkowników. Aby uzyskać więcej informacji, zobacz Strategie architektury monitorowania i wykrywania zagrożeń.
W ASP.NET Core użyj oprogramowania pośredniego obsługującego wyjątki i odpowiedzi Problem Details dla spójnego kontraktu błędów. Skonfiguruj odpowiedzi produkcyjne w celu pominięcia szczegółów diagnostycznych i dołącz identyfikator korelacji, za pomocą którego zespoły pomocy technicznej mogą zlokalizować odpowiednie dzienniki. Aby uzyskać więcej informacji, zobacz Obsługa błędów na platformie ASP.NET Core.
Rozróżnienie błędów po stronie klienta i błędów po stronie serwera
Użyj kodów stanu HTTP 4xx dla błędów klienta i kodów stanu HTTP 5xx w przypadku błędów serwera. Zachowaj to rozróżnienie w komunikatach o błędach.
Optymalizowanie dostępu do danych po stronie klienta
Częste żądania i duże odpowiedzi mogą sprawić, że sieć stanie się wąskim gardłem. Zmniejsz niepotrzebne transfery podczas implementowania pobierania zasobów i aktualizacji.
Wspieranie cache'u po stronie klienta
Użyj nagłówka Cache-Control odpowiedzi, aby określić, czy klienci i serwery pośrednie mogą przechowywać odpowiedź i jak długo pozostaje świeża.
Poniższy przykład przedstawia żądanie HTTP GET i powiązaną z nim odpowiedź, która zawiera nagłówek Cache-Control:
GET https://api.contoso.com/orders/2 HTTP/1.1
HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: application/json
Content-Length: 59
{"orderId":2,"productId":4,"quantity":2,"orderValue":10.00}
W tym przykładzie odpowiedź pozostaje świeża przez 600 sekund. Dyrektywa private zezwala na przechowywanie w prywatnej pamięci podręcznej, takiej jak pamięć podręczna przeglądarki, ale uniemożliwia przechowywanie we współdzielonej pamięci podręcznej. Używaj public tylko w przypadku odpowiedzi, które są bezpieczne do udostępniania między wywołującymi. Użyj no-store do obsługi odpowiedzi poufnych, które nie mogą być przechowywane.
W kontrolerze ASP.NET Core ustaw nagłówki pamięci podręcznej przed zwróceniem odpowiedzi:
Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue
{
Private = true,
MaxAge = TimeSpan.FromMinutes(10)
};
Uwaga
Dyrektywa no-cache zezwala na przechowywanie, ale wymaga ponownej weryfikacji przed ponownym użyciem. Użyj polecenia no-store , aby uniemożliwić przechowywanie.
Interfejs API kontroluje zasady pamięci podręcznej i walidatory w odpowiedziach. Klienci i serwery pośrednie używają tych informacji do zarządzania pamięciami podręcznymi. Ustawiaj te nagłówki spójnie, aby osoby wywołujące mogły ponownie używać odpowiedzi bez niepotrzebnych transferów danych.
Aby określić max-age, należy wziąć pod uwagę, ile nieaktualności może tolerować obciążenie. Zasób może ulec zmianie, gdy buforowana odpowiedź jest nadal świeża. Po wygaśnięciu świeżości pamięć podręczna może ponownie zweryfikować odpowiedź zamiast odrzucać ją i ponownie pobierać całą reprezentację. Nie używaj długiego okresu świeżości dla danych, które wymagają natychmiastowej widoczności zmian.
Optymalizowanie przetwarzania zapytań dzięki podaniu elementu ETags
Znacznik encji (ETag) jest nieprzezroczystym walidatorem wybranej reprezentacji zasobu. Klienci mogą zachować ją z odpowiedzią z pamięci podręcznej i wysłać ją w If-None-Match, aby sprawdzić, czy reprezentacja uległa zmianie. Pasujące żądanie GET lub HEAD zwraca wartość 304 (Nie zmodyfikowano) bez treści odpowiedzi.
Wygeneruj walidator, który pozostaje spójny między instancjami aplikacji i zmienia się po zmianie reprezentacji.
Poniższy kontroler tworzy skrót z dokładnych bajtów JSON, które zwraca, i ponownie używa skonfigurowanych opcji JSON aplikacji.
FileContentResult stosuje obsługę odpowiedzi warunkowych w ASP.NET Core bez niestandardowych otoczek wyników. Repozytorium zwraca tylko zamówienie, do którego wywołujący ma uprawnienia odczytu. Konfigurowanie uwierzytelniania i autoryzacji w aplikacji hosta.
using System.Security.Cryptography;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
[ApiController]
[Authorize]
[Route("orders")]
public sealed class OrdersController(IOrderRepository repository,IOptions<JsonOptions> jsonOptions) : ControllerBase
{
[HttpGet("{id:int}")]
[HttpHead("{id:int}")]
public async Task<IActionResult> GetOrder(int id, CancellationToken cancellationToken)
{
var order = await repository.FindAuthorizedAsync(id, User, cancellationToken);
if (order is null)
{
return NotFound();
}
var payload = JsonSerializer.SerializeToUtf8Bytes(order, jsonOptions.Value.JsonSerializerOptions);
var etag = new EntityTagHeaderValue($"\"{Convert.ToHexString(SHA256.HashData(payload))}\"");
Response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue
{
Private = true,
MaxAge = TimeSpan.FromMinutes(10)
};
return new FileContentResult(payload, "application/json")
{
EntityTag = etag
};
}
}
Ten przykład buforuje reprezentację i odczytuje kolejność bajtów nawet dla żądania warunkowego. Użyj go tylko dla odpowiedzi ograniczonych zakresem i niewrażliwych. W przypadku dużych zasobów rozważ użycie przechowywanego tokenu wersji, który zmienia się za każdym razem, gdy wybrana reprezentacja zmieni się, aby można było zweryfikować żądanie bez generowania pełnej treści odpowiedzi. Jeśli negocjacje zawartości, kompresja lub przekształcenia bramy zmieniają reprezentację, upewnij się, że walidatory i zróżnicowanie pamięci podręcznej pozostaną poprawne dla tej reprezentacji.
Odpowiedź zawiera znacznik ETag i politykę pamięci podręcznej. W poniższych przykładach pokazano pierwsze 16 znaków szesnastkowych (64 bity) skrótu SHA-256 wyświetlanej treści JSON, a następnie ... w celu zwięzłości. Nagłówki rzeczywiste zawierają pełny hash bez wielokropka.
HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: application/json
ETag: "3097D1FD51082205..."
Content-Length: 59
{"orderId":2,"productId":4,"quantity":2,"orderValue":10.00}
Napiwek
Użyj Cache-Control: no-store w przypadku odpowiedzi zawierających poufne dane. W przypadku innych odpowiedzi specyficznych dla użytkownika, które mogą być buforowane, użyj polecenia private , aby zapobiec przechowywaniu w udostępnionych pamięciach podręcznych. Aby zapoznać się z zagadnieniami dotyczącymi implementacji, zobacz oprogramowanie pośredniczące buforowania odpowiedzi w ASP.NET Core.
Obsługa warunkowych żądań GET, dzięki czemu obiekty wywołujące mogą ponownie zweryfikować odpowiedzi z pamięci podręcznej bez konieczności pobierania niezmienionej reprezentacji. Zaimplementuj następujące zachowanie żądania i odpowiedzi:
Zaakceptuj warunkowe żądania GET, które zawierają znacznik ETag w nagłówku
If-None-Match, takie jak:GET https://api.contoso.com/orders/2 HTTP/1.1 If-None-Match: "3097D1FD51082205..."Porównaj bieżący element ETag dla żądanego zasobu (zasobu nr 2 w tym przykładzie) z warunkiem
If-None-Match.Jeśli warunek zostanie spełniony, zwróć wartość 304 (Nie zmodyfikowano) bez treści odpowiedzi.
Jeśli warunek nie pasuje, zwróć wartość 200 (OK) z bieżącą reprezentacją.
Jeśli żądany zasób już nie istnieje, zwróć wartość 404 (Nie znaleziono).
Uwaga
Element ETag nie sprawia, że odpowiedź nadaje się do bezpiecznego buforowania. Stosuj spójnie dyrektywy pamięci podręcznej, w tym w odpowiedziach warunkowych i błędów, oraz używaj no-store w przypadku poufnych danych.
W miarę możliwości używaj obsługi frameworka dla żądań warunkowych. Jeśli samodzielnie zaimplementujesz porównania, obsłuż wszystkie dostarczone znaczniki encji (ETag), warunki z symbolami wieloznacznymi oraz reguły porównania słabego i silnego, zamiast porównywać tylko pierwszy znacznik. Uwzględnij odpowiednie nagłówki ETag i pamięci podręcznej w odpowiedziach 304 (Nie zmodyfikowano) i wykonaj autoryzację przed zwróceniem metadanych zasobów.
Napiwek
Przechowywany walidator umożliwia sprawdzenie, czy reprezentacja została zmieniona bez pobierania pełnego zasobu. Takie podejście może zmniejszyć transfer danych i przetwarzanie, gdy zasób jest duży lub kosztowny do pobrania.
Używanie elementów ETag do obsługi optymistycznej współbieżności
Aby zapobiec utracie aktualizacji, należy ocenić If-Match warunki wstępne dotyczące żądań zmiany stanu względem bieżącego znacznika ETag zasobu. Dzięki temu interfejs API odrzuci zmianę na podstawie reprezentacji, która została już zmodyfikowana przez inną operację. Zaimplementuj następujące zachowanie:
Akceptuje żądania aktualizacji identyfikujące oczekiwaną wersję w nagłówku
If-Match. Następujące żądanie PUT aktualizuje zamówienie 2. Używa elementu ETag z wcześniejszej odpowiedzi GET, skróconego w ten sam sposób. Tag identyfikuje istniejącą reprezentację, a nie nową treść żądania:PUT https://api.contoso.com/orders/2 HTTP/1.1 If-Match: "3097D1FD51082205..." Content-Type: application/x-www-form-urlencoded Content-Length: 37 productId=3&quantity=5&orderValue=250Porównaj bieżący element ETag dla żądanego zasobu (zamówienie 2 w tym przykładzie) z warunkiem
If-Match.Jeśli warunek wstępny jest spełniony, wykonaj aktualizację atomowo i zwróć udokumentowaną odpowiedź powodzenia, taką jak 204 (Brak zawartości). Nagłówek
Locationnie jest wymagany wyłącznie dlatego, że zaktualizowano istniejący zasób. Zwracaj element ETag dla pomyślnej odpowiedzi PUT tylko wtedy, gdy spełnia wymagania walidatora HTTP dla przechowywanej reprezentacji.Jeśli warunek nie jest spełniony, zwróć wartość 412 (Warunek wstępny nie powiódł się). W tym przykładzie nie jest zwracana żadna treść odpowiedzi.
Jeśli zasób do zaktualizowania już nie istnieje, zwróć wartość 404 (Nie znaleziono).
Udokumentuj odpowiedzi powodzenia i konfliktów, aby osoby wywołujące mogły określić, czy aktualizacja zakończyła się pomyślnie, czy też muszą pobrać bieżącą reprezentację przed przesłaniem kolejnej zmiany.
W przypadku operacji, które muszą zapobiegać utracie aktualizacji, należy wymagać warunku wstępnego If-Match i zwrócić wartość 428 (Wymagany warunek wstępny), gdy go brakuje. Użyj porównania ścisłego dla elementu If-Match.
Sprawdzanie wersji w pamięci aplikacji, a następnie bezwarunkowe zapisywanie tworzy stan wyścigu. Wymuś porównanie wersji i zapis atomowy w magazynie danych. Na przykład użyj tokenu współbieżności bazy danych, aby aktualizacja powiodła się tylko wtedy, gdy przechowywana wersja nadal jest zgodna z wersją używaną do oceny żądania.
W programie Entity Framework (EF) Core skonfiguruj token współbieżności i obsłuż DbUpdateConcurrencyException zamiast nadpisywać współbieżną zmianę. W kodzie dostępu do danych interfejsu API nie należy automatycznie zastępować oczekiwanej wersji i ponawiać próby konfliktowej aktualizacji bazy danych, chyba że semantyka operacji na to zezwala. Aby uzyskać więcej informacji, zobacz Obsługa konfliktów współbieżności.
Obsługa dużych żądań i odpowiedzi
Duże treści żądań i odpowiedzi zużywają pamięć interfejsu API, przepustowość i pojemność połączenia. Zaimplementuj limity przesyłania strumieniowego i zasobów, aby duże transfery nie przeciążały usługi. Podczas obsługi znacznej ilości danych należy wziąć pod uwagę następujące kwestie:
Optymalizowanie żądań i odpowiedzi dotyczących dużych obiektów
W przypadku dużych obiektów, takich jak obrazy lub inne dane binarne, obsługuj przesyłanie strumieniowe i pobieranie.
Użyj asynchronicznych interfejsów API przesyłania strumieniowego i pozwól serwerowi wybrać sposób ramkowania transferu dla wynegocjowanej wersji protokołu HTTP. Nie należy ręcznie dodawać Transfer-Encoding: chunked do każdego elementu odpowiedzi. Przesyłanie strumieniowe nie jest ograniczone do kodowania fragmentowanego HTTP/1.1. Unikaj buforowania całego nieograniczonego żądania lub odpowiedzi w pamięci aplikacji.
Ustaw limity treści żądań, limity przesyłania, limity czasu i zasady anulowania w warstwach aplikacji i hostingu. Przed rozpoczęciem odpowiedzi odrzuć zbyt duże żądanie kodem 413 (Content Too Large).
Kompresja HTTP może zmniejszyć rozmiar transferu kosztem dodatkowego przetwarzania na serwerze i kliencie. Na przykład klient może wysłać Accept-Encoding: gzip. Jeśli serwer zwraca skompresowaną zawartość gzip, zidentyfikuj kodowanie przy użyciu polecenia Content-Encoding: gzip.
Skonfiguruj kompresję za pośrednictwem platformy hostingu lub oprogramowania pośredniczącego i zmierz kompromis między użyciem CPU a przepustowością zarówno po stronie klienta, jak i serwera.
- Unikaj rekompresowania formatów, które są już skompresowane.
- Przed włączeniem kompresji dla dynamicznie generowanych odpowiedzi HTTPS należy ocenić czynniki ryzyka kompresji, takie jak BREACH, i zastosować odpowiednie środki zaradcze. Nawet jeśli wyłączysz kompresję HTTPS w programie pośredniczącym, serwer hostingu może być skonfigurowany do stosowania kompresji.
- Upewnij się, że zróżnicowanie pamięci podręcznej i ETagi pozostają poprawne po zmianie kodowania zawartości.
Aby uzyskać wskazówki dotyczące implementacji kompresji i zagadnienia dotyczące zabezpieczeń, zobacz Kompresja odpowiedzi w ASP.NET Core.
Obsługa częściowych pobrań dużych zasobów
Jeśli interfejs API obsługuje częściowe pobieranie dużych zasobów, anonsuj tę funkcję za pomocą Accept-Ranges. Obsługuj żądania HEAD, zwracając metadane zasobu, w tym jego całkowity Content-Length, bez treści odpowiedzi. W przypadku żądań GET określających obsługiwany zakres bajtów zwróć wartość 206 (Częściowa zawartość), wartość Content-Length dla zwróconej części i element Content-Range identyfikujący tę część zasobu. Ten kontrakt odpowiedzi umożliwia wywołującym pobieranie części zasobu bez konieczności zwracania całego obiektu przez interfejs API.
Żądania HTTP HEAD i częściowe odpowiedzi zostały szczegółowo opisane w temacie Projektowanie interfejsu API.
Obsługa 100-Continue w przypadku dużych przesyłań
W przypadku dużego przesyłania HTTP/1.1 klient może wysłać nagłówki żądania z Expect: 100-continue i czekać przed przesłaniem treści. Serwer może wysłać tymczasową odpowiedź 100 (Kontynuuj) lub odrzucić żądanie bez otrzymania pełnej treści. Ta wymiana jest częścią jednego żądania, a nie częścią wstępnego pustego żądania, po którym następuje drugie pełne żądanie.
Skonfiguruj i przetestuj sposób, w jaki stos hostingu i brama obsługują żądania z i bez Expect: 100-continue. Sprawdź zachowanie interfejsu API, gdy akceptuje lub odrzuca przesyłanie, i zmierz wpływ na użycie zasobów serwera i opóźnienie żądań.
Obsługa stronicowania dla żądań, które mogą zwracać dużą liczbę obiektów
Pobieranie całej dużej kolekcji może zwiększyć przetwarzanie serwera, ruch sieciowy i opóźnienie.
Zweryfikuj dane wejściowe stronicowania na serwerze i wymuś maksymalny rozmiar strony. Poniższy kontroler ilustruje walidację żądań. Domyślnie jest to 10 wyników i odrzuca limity poza 1 do 100 lub przesunięcia poza 0 do 10 000. Te granice są orientacyjne. Wybierz limity na podstawie obciążenia i testów obciążeniowych. W przypadku użycia [ApiController]błędy weryfikacji generują odpowiedź 400. Aby uzyskać więcej informacji, zobacz Tworzenie internetowych interfejsów API za pomocą ASP.NET Core.
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Authorize]
[Route("orders")]
public sealed class OrderCollectionsController(IOrderRepository repository) : ControllerBase
{
[HttpGet]
public async Task<ActionResult<IReadOnlyList<Order>>> GetOrders(
[FromQuery, Range(1, 100)] int limit = 10,
[FromQuery, Range(0, 10000)] int offset = 0,
CancellationToken cancellationToken = default)
{
var orders = await repository.ListAuthorizedAsync(limit, offset, User, cancellationToken);
return Ok(orders);
}
}
Klient może zażądać 30 zamówień rozpoczynających się od offsetu 50 przy użyciu polecenia https://api.contoso.com/orders?limit=30&offset=50.
Poza weryfikacją żądania pokazaną w poprzednim przykładzie dołącz link następnej strony lub token kontynuacji w kontrakcie odpowiedzi. Na przykład zwróć zamówienia z właściwością nextLink ustawioną na https://api.contoso.com/orders?limit=30&offset=80, gdy jest dostępna inna strona. Zachowaj filtry, kolejność sortowania i wersję interfejsu API w linku. Pomiń nextLink lub ustaw go na null na ostatniej stronie. Aby zapoznać się z przykładem kontraktu nextLink, zobacz stronicowanie w Microsoft Graph.
Zastosuj filtrowanie i stronicowanie w repozytorium danych, a nie po załadowaniu całej kolekcji. Użyj deterministycznej kolejności sortowania. W przypadku dużych lub często zmieniających się kolekcji oceń stronicowanie oparte na kursorach, aby uniknąć kosztownych przesunięć i niestabilnych granic stron.
Napiwek
Wymuszanie udokumentowanych ograniczeń dotyczących złożoności zapytania, wyrażeń filtru i rozmiaru żądania. Przetestuj limity każdej bramy, serwera proxy i serwera w ścieżce żądania.
Zapewnienie dostępności, skalowalności i responsywności
Zdefiniuj cele opóźnienia, przepływności i dostępności dla krytycznych operacji interfejsu API. Projektuj i testuj pod kątem przewidywanego ruchu, skoków ruchu i awarii usług zależnych. Skalowanie w poziomie i bezstanowość wspierają te cele. Rozważ następujące wymagania implementacyjne.
Zapewnienie asynchronicznej obsługi długotrwałych żądań
W przypadku pracy, która trwa dłużej niż normalne żądanie HTTP, zweryfikuj i autoryzuj żądanie, trwale zarejestruj pracę i zwróć kod stanu 202 (Zaakceptowane). Nie należy polegać na nieśledzonym zadaniu w procesie, które ma zakończyć się po wysłaniu odpowiedzi: ponowne uruchamianie aplikacji, skalowanie i wdrażanie mogą je przerwać. Używaj trwałej kolejki i workera lub trwałego przepływu pracy, gdy praca musi być kontynuowana pomimo tych zdarzeń. Aby uzyskać pełne wskazówki dotyczące semantyki i implementacji protokołu HTTP, zobacz Asynchroniczny wzorzec Request-Reply.
Zapewnij wywołującym sposób pobierania stanu i wyniku operacji. Interfejs API może również powiadamiać osoby wywołujące po zakończeniu przetwarzania.
Możesz podać zasób stanu, który klienci sondują:
- Klient przesyła żądanie. Przed zaakceptowaniem pracy zweryfikuj jego dane wejściowe i uprawnienia.
- Utrwalić identyfikator operacji i stan w magazynie trwałym, takim jak Azure Table Storage, i umieścić zadanie w kolejce dla oddzielnego procesora, na przykład przy użyciu Azure Service Bus. Koordynuj utrwalanie i kolejkowanie, aby niepowodzenie między tymi dwoma akcjami nie powodowało utraty zaakceptowanej pracy.
- Zwraca 202 z nagłówkiem
Location, który identyfikuje punkt końcowy stanu iRetry-Afternagłówek, który kontroluje częstotliwość sondowania. Zaakceptuj żądanie dopiero po trwałym zapisaniu pracy. - Worker przetwarza operację i aktualizuje swój trwały stan. Utwórz idempotentne przetwarzanie, ponieważ worker może odbierać tę samą pracę więcej niż raz. Zastosuj ograniczone ponawianie prób do przejściowych niepowodzeń.
- Klient odpytuje endpoint statusu. Wymuś autoryzację na poziomie zasobu zarówno w punktach końcowych statusu, jak i wyniku. Identyfikator trudny do odgadnięcia nie jest autoryzacją.
- Zwróć udokumentowaną reprezentację stanu, gdy praca jest w toku. Po zakończeniu podaj wynik lub przekierowanie 303 (See Other) do zasobu z wynikiem. Zgłaszaj niepowodzenia za pomocą spójnego, bezpiecznego formatu obsługi błędów.
- Zdefiniuj przechowywanie i czyszczenie rekordów operacji i wyników. Wygaśnięcie operacji dokumentu, duplikaty zgłoszeń oraz wszelkie obsługiwane sposoby anulowania.
W przypadku połączonych klientów rozważ użycie ASP.NET Core SignalR na potrzeby powiadomień ukończenia. Zachowaj dostępny zasób stanu trwałego, aby klienci mogli wznowić działanie po rozłączeniu lub nieodebranym powiadomieniu. Aby uzyskać więcej informacji, zobacz Omówienie usługi ASP.NET Core SignalR.
Zagwarantowanie bezstanowości każdego żądania
Nie wymagaj, aby późniejsze żądanie trafiało do tego samego wystąpienia aplikacji co wcześniejsze żądanie. Zachowaj stan przepływu pracy i zasobu w odpowiednich współdzielonych magazynach danych, a nie wyłącznie w pamięci procesowej. Żądania mogą zależeć od trwałego stanu zasobu bez tworzenia powiązania z instancją serwera. Bezstanowa obsługa żądań wspiera skalowanie w poziomie.
Poznaj zależności downstream
Ustaw limit czasu dla każdego kolejnego wywołania. Zachowaj łączny czas wywołań, ponownych prób i opóźnień w limicie czasu żądania API i propaguj anulowanie żądania do operacji podrzędnych.
Ponawiaj próby tylko w przypadku przejściowych awarii i operacji, które można bezpiecznie powtórzyć. Użyj ograniczonych ponownych prób z wycofywaniem i zakłóceniami. Ogranicz łączną liczbę ponowień prób dla każdej zależności, a także liczbę prób dla każdego żądania. Aby uzyskać więcej informacji, zobacz Obsługa błędów przejściowych.
Użyj circuit breaker, gdy powtarzające się błędy zależności mogą wyczerpać pojemność interfejsu API.
Śledzenie klientów i implementowanie ograniczania przepustowości w celu zmniejszenia prawdopodobieństwa ataków typu DoS
Zastosuj limity liczby żądań, współbieżności, ładunku i czasu wykonywania. Partycjonuj limity według uwierzytelnionego obiektu wywołującego lub dzierżawcy, jeśli to możliwe. Zwróć 429 (zbyt wiele żądań), gdy obiekt wywołujący przekroczy skonfigurowany limit, i 503 (usługa niedostępna) w przypadku tymczasowej niedostępności usługi. Uwzględnij Retry-After tam, gdzie jest to konieczne. Jeśli interfejs API ponawia próby wywołań do zależności podrzędnych, należy ograniczyć liczbę tych ponowień, aby nie zwiększały przeciążenia.
Limity Gateway i aplikacji służą różnym celom. Limity gatewaya mogą chronić współdzieloną przepustowość. Limity aplikacji mogą odzwierciedlać koszt operacji i przepustowość systemów zależnych. Ani nie zastępuje ochrony przed atakami DDoS warstwy sieciowej ani zapory aplikacji internetowej. Aby uzyskać więcej informacji, zobacz Wzorzec ograniczania przepustowości.
Staranne zarządzanie trwałymi połączeniami HTTP
W przypadku połączeń przychodzących skonfiguruj limity czasu utrzymania aktywności serwera i bramy, limity czasu żądań i współbieżne limity połączeń dla obciążenia i ścieżki sieciowej. Nie wymuszaj rutynowo zamykania połączeń po każdej odpowiedzi. Aby uzyskać informacje na temat ustawień serwera ASP.NET Core, zobacz Konfigurowanie opcji serwera internetowego Kestrel.
Jeśli sam interfejs API wywołuje zależne usługi HTTP, użyj ponownie tych połączeń wychodzących. W implementacji interfejsu API .NET użyj długotrwałych HttpClient wystąpień z odpowiednim PooledConnectionLifetime lub użyj klientów utworzonych za pomocą IHttpClientFactory. Usuwaj wychodzące komunikaty odpowiedzi i strumienie, gdy nie są już potrzebne. Te ustawienia dotyczą wywołań interfejsu API do usług zależnych, a nie przychodzących połączeń klientów. Aby uzyskać szczegółowe informacje i zastrzeżenia dotyczące obsługi plików cookie, zobacz Wytyczne dotyczące korzystania z klienta HttpClient.
Publikowanie internetowego interfejsu API i zarządzanie nim
Wdróż interfejs API w środowisku hostingu, które spełnia wymagania operacyjne. Rozważ zastosowanie następujących strategii:
- Użyj protokołu HTTPS. Uwierzytelnianie żądań do chronionych punktów końcowych i wymuszanie autoryzacji dla każdego zasobu i operacji.
- Skaluj środowisko hostingu w celu spełnienia zobowiązań dotyczących czasu odpowiedzi w miarę zmian zapotrzebowania.
- Jednostka rozliczeniowa jest wymagana, jeśli wymaga tego model rozliczeń interfejsu API.
- Regulować ruch i egzekwować limity klienta tam, gdzie jest to wymagane.
- Rejestruj zdarzenia audytu, których wymaga zgodność i reagowanie na incydenty, ale nie rejestruj poświadczeń, tokenów ani wrażliwych treści żądań i odpowiedzi.
- Monitoruj kondycję hosta i zdefiniuj akcje odzyskiwania, takie jak ponowne uruchamianie niezdrowego hosta.
Rozważ użycie oddzielnej fasady, która obsługuje wspólne kwestie związane z zarządzaniem i przekazuje zweryfikowane żądania do interfejsu API. Fasada może zapewnić następujące możliwości:
- Funkcjonowanie jako punkt integracji wielu internetowych interfejsów API.
- Przekształcanie komunikatów i tłumaczenie protokołów komunikacyjnych dla klientów utworzonych przy użyciu różnych technologii.
- Buforowanie żądań i odpowiedzi w celu zmniejszenia obciążenia na serwerze hostującym internetowy interfejs API.
Zachowaj kontrakty interfejsu API i konfigurację bramy pod kontrolą wersji, zweryfikuj zmiany w potokach wdrażania i zdefiniuj procedury wycofywania. Dostosuj hosting, tożsamość, sieci i ład do podejścia landing zone organizacji. Aby uzyskać więcej informacji, zobacz Co to jest strefa docelowa platformy Azure?.
Testowanie internetowego interfejsu API
Użyj testów jednostkowych, aby zweryfikować logikę interfejsu API, a testów integracyjnych, aby sprawdzić jego zachowanie w protokole HTTP.
Uwzględnij następujące kontrole:
Przetestuj wszystkie ścieżki, aby sprawdzić, czy wywołują poprawne operacje. Należy szczególnie pamiętać o nieoczekiwanym zwracaniu kodu stanu HTTP 405 (Metoda niedozwolona). Ten kod stanu może wskazywać niezgodność między trasą a metodami HTTP (GET, POST, PUT, DELETE), które mogą być obsłużone przez tę trasę.
Wysyłaj żądania korzystające z metod, które nie są obsługiwane dla każdej trasy, i sprawdź udokumentowaną odpowiedź 405 (Metoda niedozwolona) i nagłówek
Allow.Sprawdź, czy wszystkie trasy są prawidłowo chronione oraz podlegają odpowiedniej kontroli uwierzytelniania i autoryzacji.
Uwaga
Niektóre aspekty zabezpieczeń, takie jak uwierzytelnianie użytkowników, prawdopodobnie należą do odpowiedzialności środowiska hosta, a nie internetowego interfejsu API, ale nadal trzeba uwzględnić testy zabezpieczeń w ramach procesu wdrażania.
Przetestuj obsługę wyjątków w ramach każdej operacji i sprawdź, czy do aplikacji klienckiej jest przekazywana odpowiednia i czytelna odpowiedź HTTP.
Sprawdź, czy komunikaty żądań i odpowiedzi są poprawnie sformułowane. Na przykład w przypadku żądania POST, które tworzy zasób, sprawdź deserializację, analizowanie, tworzenie zasobu i nagłówek odpowiedzi
Location.Przetestuj obsługiwane i nieobsługiwane typy multimediów żądań niezależnie od negocjacji zawartości odpowiedzi. Sprawdź odpowiedzi 415 (nieobsługiwany typ nośnika) pod kątem nieobsługiwanych formatów żądań oraz udokumentowane błędy walidacji dla nieprawidłowych treści żądania.
Przećwiczyć każdą obsługiwaną wersję interfejsu API, w tym routing, weryfikację żądań i schemat odpowiedzi. Przetestuj pominięte, źle sformułowane i nieobsługiwane identyfikatory wersji oraz sprawdź, czy buforowane odpowiedzi nie przenikają między wersjami.
Sprawdź poprawność wszystkich linków i identyfikatorów URI w komunikatach odpowiedzi. Na przykład komunikat HTTP POST powinien zwrócić identyfikator URI nowo utworzonego zasobu. Wszystkie linki HATEOAS powinny być prawidłowe.
Upewnij się, że każda operacja zwraca prawidłowe kody stanu dla różnych kombinacji danych wejściowych. Na przykład:
- Jeśli zapytanie zakończy się pomyślnie, powinno zwrócić kod stanu 200 (OK).
- Jeśli zasób nie zostanie znaleziony, operacja powinna zwrócić kod stanu HTTP 404 (Nie znaleziono).
- Jeśli klient wysyła żądanie, które powoduje pomyślne usunięcie zasobu, powinien zostać zwrócony kod stanu 204 (Brak zawartości).
- Jeśli klient wysyła żądanie, które tworzy nowy zasób, kod stanu powinien mieć wartość 201 (Utworzono).
Zbadaj nieoczekiwane odpowiedzi 5xx z warstwy aplikacji lub hostingu.
Przetestuj różne kombinacje nagłówków żądań, które może określić aplikacja kliencka, i sprawdź, czy internetowy interfejs API zwraca w komunikacie odpowiedzi oczekiwane informacje.
Przetestuj ciągi zapytań. Jeśli operacja może przyjmować opcjonalne parametry (takie jak żądania stronicowania), przetestuj różne kombinacje i kolejność parametrów.
Sprawdź, czy operacje asynchroniczne zostały ukończone pomyślnie. W przypadku odpowiedzi przesyłanych strumieniowo sprawdź, czy interfejs API nie buforuje całego ładunku i czy obsługuje rozłączenia klienta, i czy prawidłowo obsługuje anulowanie. Jeśli interfejs API udostępnia zasoby stanu dla długotrwałych operacji, sprawdź, czy raportują postęp operacji i wyniki prawidłowo.
Przetestuj przypadki awarii i przypadki brzegowe, które wpływają na poprawność:
- Zweryfikuj minimalne, maksymalne, ujemne, nieprawidłowe i nadmierne wartości stronicowania i wsadu.
- Użyj linków stronicowania aż do ostatniej strony. Sprawdź, czy łącza zachowują opcje zapytania i czy jest zwracany udokumentowany sygnał ukończenia.
- Sprawdź, czy dodatkowe pola wejściowe nie mogą zmieniać chronionych właściwości i że zaznaczenie pola nie może uwidocznić nieautoryzowanych danych.
- Testowanie dopasowania, niezgodności, słabych tagów ETag, wielokrotnych i tagów ETag z symbolami wieloznacznymi oraz sprawdź, czy odpowiedzi 304 nie zawierają treści.
- Przećwiczyć współbieżne aktualizacje i potwierdzić, że magazyn danych atomowo odrzuca przestarzałe zapisy.
- Sprawdź, czy wywołujący nie mogą odczytać zasobów innego wywołującego, rekordów stanu ani buforowanych odpowiedzi.
- W przypadku wielodzierżawnych interfejsów API przetestuj sfałszowane lub niezgodne identyfikatory dzierżawcy. Po włączeniu buforowania przetestuj żądania dla tego samego identyfikatora URI zasobu od różnych dzierżawców.
- Symuluj przekroczenia limitu czasu, anulowanie klienta, błędy zależności, ponowne uruchomienia workera i zduplikowane dostarczanie pracy w tle.
- Sprawdź limity czasu usług zależnych, budżety ponowień, anulowanie i odzyskiwanie po zadziałaniu mechanizmu circuit breaker.
- Sprawdź odpowiedzi 429 (zbyt wiele żądań) i 503 (usługa niedostępna), zachowanie ponawiania prób i koszt odrzucania przeciążonych żądań.
Uruchamianie testów obciążeniowych w środowisku infrastrukturalnym zbliżonym do produkcyjnego i przy realistycznych kombinacjach operacji. Zdefiniuj kryteria zaliczenia i niezaliczenia dla przepływności, percentyli opóźnienia, współczynników błędów i nasycenia zasobów. Użyj Azure Load Testing i zintegruj kontrole regresji w potoku wdrażania.
Korzystanie z usługi API Management
Rozważ użycie Azure API Management, gdy potrzebna jest brama zarządzana z wymuszaniem zasad. Dodaje koszt, kolejny przeskok sieciowy i zależność w ścieżce żądania. Wybierz warstwę usługi i topologię na podstawie pojemności, sieci, dostępności i wymagań regionalnych; funkcje różnią się w zależności od warstwy. Warstwa deweloperska nie jest przeznaczona dla obciążeń produkcyjnych. Postępuj zgodnie ze wskazówkami usługi Well-Architected dotyczącymi API Management.
Aby opublikować internetowy interfejs API i zarządzać nim, wykonaj następujące kroki:
Wdróż internetowy interfejs API na platformie hostingowej, takiej jak Azure App Service, Azure Container Apps, Azure Kubernetes Service (AKS) lub Azure Virtual Machines.
Skonfiguruj usługę API Management, aby kierować żądania z adresu URL bramy do interfejsu API zaplecza. Jedno wystąpienie usługi API Management może kierować żądania do wielu interfejsów API. Wiele wystąpień może również udostępniać ten sam interfejs API zaplecza, gdy aplikacje potrzebują różnych granic dostępu.
Uwaga
Linki HATEOAS w odpowiedziach powinny odwoływać się do adresu URL bramy usługi API Management, a nie adresu URL serwera zaplecza.
Zaimportuj definicję OpenAPI kontrolowaną wersjami i sprawdź, czy zaimportowane operacje, parametry, wymagania dotyczące zabezpieczeń i schematy odpowiedzi są zgodne z implementacją. Sprawdź obsługiwane wersje interfejsu OpenAPI i ograniczenia importu. Aby uzyskać więcej informacji, zobacz Importowanie specyfikacji interfejsu OpenAPI.
Skonfiguruj uwierzytelnianie i autoryzację zarówno dla komunikacji typu klient-brama, jak i z bramy do backendu. Zweryfikuj tokeny dostępu OAuth 2.0 i wymuś uprawnienia wymagane dla żądań klientów. W przypadku zapleczy obsługujących uwierzytelnianie Microsoft Entra ID preferuj tożsamość zarządzaną usługi API Management tylko z wymaganymi uprawnieniami. Uwierzytelnianie certyfikatu to kolejna opcja. Unikaj uwierzytelniania podstawowego, gdy są dostępne bardziej bezpieczne opcje. Aby uzyskać więcej informacji, zobacz Uwierzytelnianie i autoryzacja interfejsów API w Azure API Management.
Uwaga
Jeśli potrzebujesz kluczy subskrypcji usługi API Management, nie używaj ich jako zamiennika uwierzytelniania i autoryzacji na poziomie zasobów.
Skonfiguruj zasady uwierzytelniania, dostępu między źródłami, przekształceń komunikatów, filtrowania adresów IP, limitów szybkości i kwot zgodnie z potrzebami. Skonfiguruj buforowanie odpowiedzi tylko dla danych, które są bezpieczne do buforowania. Zastosuj zasady w odpowiednim zakresie i zweryfikuj ich skuteczne zachowanie dla każdej operacji.
Aby uzyskać więcej informacji, zobacz dokumentację usługi API Management.
Important
Brama nie powoduje, że zaplecze jest prywatne ani nie zastępuje autoryzacji na poziomie zasobów. Zaprojektuj mechanizmy kontroli dostępu backendu, obsługę awarii, przepustowość i obserwowalność obciążenia jako całości, w tym bramy sieciowej. Oceń strefową i regionalną nadmiarowość względem możliwości wybranej warstwy. Uwzględnij opłaty za bramę/gateway, pozyskiwanie i przechowywanie danych telemetrycznych oraz transfer danych w modelu kosztów.
Wsparcie dla deweloperów po stronie klienta
Zapewnij użytkownikom interfejsu API dokumentację dotyczącą uwierzytelniania, operacji, parametrów, typów danych i kodów odpowiedzi.
Udokumentuj operacje REST dla internetowego interfejsu API
Opublikuj definicję interfejsu OpenAPI, która opisuje operacje, parametry, schematy żądań i odpowiedzi interfejsu API oraz wymagania dotyczące zabezpieczeń. Przechowuj to w systemie kontroli wersji wraz z implementacją i sprawdź, czy jest ono zgodne z rzeczywistym zachowaniem interfejsu API.
Uzupełnij dokumentację interfejsu API o wskazówki, których użytkownicy potrzebują, aby prawidłowo korzystać z interfejsu API:
- Wymagania dotyczące uwierzytelniania i uprawnienia dla każdej operacji.
- Przykładowe żądania, pomyślne odpowiedzi i odpowiedzi na błędy.
- Reguły walidacji danych wejściowych, limity ładunku danych, stronicowanie i limity liczby żądań.
- Zachowanie limitu czasu, bezpieczeństwo ponawiania prób i wszelkie wymagania dotyczące idempotentności.
- Stan i pobieranie wyników dla długotrwałych operacji.
- Oczekiwania dotyczące wersjonowania i zgodności.
Zachowaj aktualność dokumentacji i przykładów, gdy zmienia się kontrakt interfejsu API.
Implementowanie klienckiego zestawu SDK
Rozważ dostarczenie zestawu SDK dla użytkowników interfejsu API. Zestaw SDK może uwidaczniać metody typizowane, które konstruują żądania, analizują odpowiedzi i zwracają udokumentowane błędy interfejsu API.
Rozważ wygenerowanie typizowanych klientów z definicji OpenAPI interfejsu API, ale zweryfikuj wygenerowane zachowanie względem usługi. uwierzytelnianie dokumentów, przekroczenia limitu czasu, anulowanie, stronicowanie, odpowiedzi na błędy i które operacje są bezpieczne, aby ponowić próbę. Utrzymuj spójność zasad zgodności klienta i interfejsu API. Wygenerowany kod nie eliminuje potrzeby testów integracji.
Monitorowanie internetowego interfejsu API
Monitoruj interfejs API, jego zależności i dowolną bramę razem. Zdefiniuj cele i alerty na poziomie usługi na podstawie wpływu na klienta, a nie polegaj tylko na kondycji hosta.
Monitorowanie bezpośrednie internetowego interfejsu API
Instrumentacja OpenTelemetry umożliwia zbieranie śladów, metryk i dzienników oraz eksportowanie sygnałów do usługi Application Insights lub innego obsługiwanego zaplecza. W przypadku instrumentacji .NET, Node.js i Python postępuj zgodnie ze wskazówkami dotyczącymi dystrybucji Microsoft OpenTelemetry. Java używa dystrybucji Azure Monitor OpenTelemetry. Postępuj zgodnie ze wskazówkami dotyczącymi włączania OpenTelemetry dla danego języka.
Monitoruj sygnały opisujące zachowanie interfejsu API:
- Liczba żądań, wskaźnik awarii i percentyle opóźnień według operacji
- Opóźnienia zależności, awarie i przekroczenia limitu czasu
- Odrzucenia z powodu limitu szybkości, współbieżna praca i nasycenie zasobów
- Czas oczekiwania kolejki operacji w tle, czas ukończenia, błędy i ponawianie prób
- Skuteczność pamięci podręcznej, gdy buforowanie jest częścią projektu
Dane telemetryczne zbierane przez narzędzie do monitorowania są automatycznie różne w zależności od języka, bibliotek i konfiguracji. Dodaj instrumentowanie operacji domenowych i pracy w tle. Nie zakładaj, że sesje przeglądarki, role użytkownika lub wszystkie szczegóły żądania są automatycznie dostępne. Propaguj kontekst śledzenia między bramą, aplikacją i procesami roboczymi. Konfigurowanie próbkowania i przechowywania w celu zrównoważenia potrzeb diagnostycznych, poufności danych i kosztów telemetrii.
Użyj standardowych testów dostępności usługi Application Insights na potrzeby cyklicznych testów zewnętrznych, jeśli jest to konieczne. Zapewnij zrozumiały endpoint kondycji lub działania bez ujawniania poufnych danych, definiuj alerty umożliwiające podjęcie działania i utrzymuj runbook reagowania.
Monitorowanie internetowego interfejsu API przy użyciu usługi API Management
Użyj metryk Azure Monitor, dzienników, analiz i diagnostyki obsługiwanych przez usługę API Management, aby odróżnić błędy bramy od błędów zaplecza. Koreluj dane telemetryczne bramy i aplikacji oraz monitoruj błędy zasad, pojemność, opóźnienie żądań i ograniczanie przepustowości. Dostępne sygnały zależą od poziomu i konfiguracji bramy.
Postępuj zgodnie z zaleceniami dotyczącymi obserwowalności i wydajności w wskazówkach usługi Well-Architected dotyczących usługi API Management.
Następne kroki
- Omówienie OData w ASP.NET Core opisuje sposób tworzenia usług interfejsu API REST zgodnych z protokołem OData przy użyciu ASP.NET Core.
- ASP.NET Core najlepsze rozwiązania obejmują asynchroniczne operacje we/wy, użycie zasobów i obsługę odpowiedzi.
- Wzorzec konsumenta idempotentnego zawiera wytyczne dotyczące śledzenia identyfikatorów przetworzonych wiadomości i obsługi duplikatów dostarczenia.
- Semantyka HTTP definiuje kody stanu HTTP.
- Asynchroniczny wzorzec Request-Reply opisuje trwałą pracę w tle i zasoby stanu.
- Najlepsze rozwiązania dotyczące architektury dla Azure API Management obejmują niezawodność, zabezpieczenia, koszty, operacje i wydajność.
- Azure Load Testing opisuje generowanie obciążenia i zautomatyzowane kontrole wydajności.
- Włącz OpenTelemetry zapewnia instrumentację specyficzną dla języka i wskazówki dotyczące eksportowania.