Sdílet prostřednictvím


Implementace webového rozhraní API

Pečlivě navržené webové rozhraní API RESTful definuje prostředky, relace a navigační schémata, která jsou přístupná klientským aplikacím. Při implementaci a nasazení webového rozhraní API byste měli zvážit fyzické požadavky prostředí, které webové rozhraní API hostuje, a zaměřit se spíš na způsob konstrukce webového rozhraní API než na logickou strukturu dat. Tyto pokyny se zaměřují na osvědčené postupy pro implementaci webového rozhraní API a jeho publikování, aby byly dostupné klientským aplikacím. Podrobné informace o návrhu webového rozhraní API najdete v tématu Návrh webového rozhraní API.

Zpracování požadavků

Při implementaci kód pro zpracování požadavků vezměte v úvahu následující body.

Akce GET, PUT, DELETE, HEAD a PATCH by měly být idempotentní

Kód, který tyto požadavky implementuje, by neměl mít žádné vedlejší účinky. Stejný požadavek opakovaný ve stejném prostředku by měl vést ke stejnému stavu. Například odeslání více požadavků DELETE na stejný identifikátor URI by mělo mít stejný účinek, i když stavový kód HTTP ve zprávách odpovědí se může lišit. První požadavek DELETE může vrátit stavový kód 204 (žádný obsah), kdežto další požadavek DELETE může vrátit stavový kód 404 (nenalezeno).

Poznámka:

Článek Idempotentní vzory na blogu Jonathana Olivera poskytuje přehled idempotence a to, jak souvisí s operacemi správy dat.

Akce POST pro vytvoření nových prostředků by neměly mít nesouvisející vedlejší účinky

Pokud má požadavek POST vytvořit nový prostředek, měly by být účinky požadavku omezené na nový prostředek (a případně i jakékoli přímo související prostředky, pokud existuje nějaký druh propojení). Například v systému elektronického obchodování může požadavek POST, který vytvoří novou objednávku pro zákazníka, také změnit úrovně zásob a generovat fakturační údaje, ale neměl by upravovat informace, které přímo nesouvisí s objednávkou nebo mají jiné vedlejší účinky na celkový stav systému.

Vyhněte se implementaci upovídaných operací POST, PUT a DELETE

Vyhněte se návrhu rozhraní API na základě chatovacích operací. Každý požadavek má režijní náklady na protokol, síť a výpočetní prostředky. Například při provádění 100 menších požadavků místo jednoho většího dávkového požadavku se v klientovi, v síti a na serveru prostředků účtují další režijní náklady. Kdykoli je to možné, poskytněte podporu příkazů HTTP pro kolekce prostředků místo jenom jednotlivých prostředků.

  • Požadavek GET na kolekci může načíst více prostředků najednou.
  • Požadavek POST může obsahovat podrobnosti o několika nových prostředcích a přidat je všechny do stejné kolekce.
  • Požadavek PUT může nahradit celou sadu prostředků v kolekci.
  • Požadavek DELETE může odebrat celou kolekci.

Podpora protokolu OData (Open Data Protocol) zahrnutá ve webovém rozhraní API 2 ASP.NET poskytuje možnost dávkových požadavků. Klientská aplikace může zabalit několik požadavků webového rozhraní API a odeslat je na server v jednom požadavku HTTP a přijmout jednu odpověď HTTP, která obsahuje odpovědi na každý požadavek. Další informace najdete v tématu Povolení služby Batch ve službě OData webového rozhraní API.

Při odesílání odpovědi dodržujte specifikaci HTTP

Webové rozhraní API musí vracet zprávy, které obsahují správný stavový kód HTTP, aby mohl klient určit, jak zpracovat výsledek, správné hlavičky HTTP, aby klient rozuměl povaze výsledku, a vhodně naformátovaný text, který klientovi umožní výsledek analyzovat.

Například operace POST by měla vrátit stavový kód 201 (vytvořeno) a zpráva odpovědi by měla v hlavičce Location obsahovat identifikátor URI nově vytvořeného prostředku.

Podpora vyjednávání obsahu

Text odpovědi může obsahovat data v různých formátech. Požadavek HTTP GET může například vracet data ve formátu JSON nebo XML. Požadavek odeslaný klientem může obsahovat hlavičku Accept určující formáty dat, které dokáže zpracovat. Tyto formáty jsou zadané jako typy médií. Například klient, který vydává požadavek GET, který načte image, může zadat hlavičku Accept, která obsahuje seznam typů médií, které může klient zpracovat, například image/jpeg, image/gif, image/png. Když webové rozhraní API vrátí výsledek, mělo by data naformátovat pomocí některého z těchto typů médií a určit formát v hlavičce Content-Type odpovědi.

Pokud klient neurčí hlavičku Accept, použijte v textu odpovědi přiměřený výchozí formát. Například webové rozhraní API ASP.NET používá pro textová data výchozí formát JSON.

Přístup HATEOAS umožňuje klientovi navigaci v prostředcích a jejich zjišťování z počátečního bodu. Toho se dosahuje pomocí odkazů obsahujících identifikátory URI. Když klient vydá požadavek HTTP GET na získání prostředku, odpověď by měla obsahovat identifikátory URI, které klientovi umožní rychle vyhledat přímo související prostředky. Například ve webovém rozhraní API, které podporuje řešení elektronického obchodování, mohl některý zákazník zadat velké množství objednávek. Když klientská aplikace načte podrobnosti daného zákazníka, odpověď by měla obsahovat odkazy, které klientské aplikaci umožní odeslat požadavky HTTP GET pro načtení těchto objednávek. Kromě toho byl měly odkazy typu HATEOAS popisovat ostatní operace (POST, PUT, DELETE atd.) podporované jednotlivými odkazovanými prostředky a odpovídající identifikátory URI pro provedení každého požadavku. Tento přístup je podrobněji popsán v návrhu rozhraní API.

V současné době neexistují žádné standardy, kterými by se implementace přístupu HATEOAS řídila, ale následující příklad dokládá jeden z možných způsobů. V tomto příkladu požadavek HTTP GET, který najde podrobnosti pro zákazníka, vrátí odpověď, která obsahuje odkazy HATEOAS, které odkazují na objednávky pro daného zákazníka:

GET https://adventure-works.com/customers/2 HTTP/1.1
Accept: text/json
...
HTTP/1.1 200 OK
...
Content-Type: application/json; charset=utf-8
...
Content-Length: ...
{"CustomerID":2,"CustomerName":"Bert","Links":[
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"GET",
    "types":["text/xml","application/json"]},
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"PUT",
    "types":["application/x-www-form-urlencoded"]},
    {"rel":"self",
    "href":"https://adventure-works.com/customers/2",
    "action":"DELETE",
    "types":[]},
    {"rel":"orders",
    "href":"https://adventure-works.com/customers/2/orders",
    "action":"GET",
    "types":["text/xml","application/json"]},
    {"rel":"orders",
    "href":"https://adventure-works.com/customers/2/orders",
    "action":"POST",
    "types":["application/x-www-form-urlencoded"]}
]}

V tomto příkladu jsou údaje zákazníka reprezentované třídou Customer v následujícím fragmentu kódu. Odkazy HATEOAS jsou uložené ve vlastnosti kolekce Links:

public class Customer
{
    public int CustomerID { get; set; }
    public string CustomerName { get; set; }
    public List<Link> Links { get; set; }
    ...
}

public class Link
{
    public string Rel { get; set; }
    public string Href { get; set; }
    public string Action { get; set; }
    public string [] Types { get; set; }
}

Operace HTTP GET načte údaje zákazníka z úložiště, vytvoří objekt Customer a potom naplní kolekci Links. Výsledek má formát zprávy s odpovědí JSON. Každý odkaz obsahuje následující pole:

  • Vztah (Rel) mezi vráceným objektem a objektem popsaným propojením. V tomto případě self označuje, že odkaz je odkaz zpět na samotný objekt (podobný this ukazatel v mnoha objektově orientovaných jazycích) a orders je název kolekce obsahující související informace o objednávce.
  • Hypertextový odkaz (Href) na objekt popisovaný odkazem v podobě identifikátoru URI.
  • Typ požadavku HTTP (Action), který se dá na tento identifikátor URI odeslat.
  • Formát dat (Types), která má obsahovat požadavek HTTP nebo která můžou být vrácená v odpovědi, v závislosti na typu požadavku.

Odkazy HATEOAS uvedená v ukázkové odpovědi HTTP udávají, že klientská aplikace může provést následující operace:

  • Požadavek HTTP GET zaslaný na identifikátor URI https://adventure-works.com/customers/2 k načtení podrobností o zákazníkovi (znovu). Vrácená data můžou mít formát XML nebo JSON.
  • Požadavek HTTP PUT zaslaný na identifikátor URI https://adventure-works.com/customers/2 k úpravě podobností o zákazníkovi. Nová data je potřeba zadat do zprávy požadavku ve formátu x-www-form-urlencoded.
  • Požadavek HTTP DELETE na identifikátor URI https://adventure-works.com/customers/2 k odstranění zákazníka. Požadavek neočekává v textu odpovědi žádné další informace ani vrácená data.
  • Požadavek HTTP GET na identifikátor URI https://adventure-works.com/customers/2/orders k vyhledání všech objednávek daného zákazníka. Vrácená data můžou mít formát XML nebo JSON.
  • Požadavek HTTP POST na identifikátor URI https://adventure-works.com/customers/2/orders pro vytvoření nové objednávky pro tohoto zákazníka. Data je potřeba zadat do zprávy požadavku ve formátu x-www-form-urlencoded.

Zpracování výjimek

Pokud operace vrátí nezachycenou výjimku, zamyslete se nad následujícími body.

Zachycení výjimek a vrácení smysluplné odpovědi klientovi

Kód, který implementuje operaci HTTP, by měl zajišťovat kompletní zpracování výjimek, aby se nezachycené výjimky nešířily do rozhraní. Pokud některá výjimka znemožní úspěšné dokončení operace, dá se předat zpátky do zprávy s odpovědí, ale měla by obsahovat smysluplný popis chyby, která výjimku způsobila. Výjimka by měla obsahovat také odpovídající stavový kód HTTP a nevracet v každé situaci jenom stavový kód 500. Pokud například požadavek uživatele způsobí aktualizaci databáze, která porušuje určité omezení (třeba se pokusí odstranit zákazníka s nevyřízenými objednávkami), měli byste vrátit stavový kód 409 (konflikt) a uvést v textu zprávy příčinu konfliktu. Pokud je požadavek kvůli nějaké další podmínce nedosažitelný, můžete vrátit stavový kód 400 (chybný požadavek). Úplný seznam stavových kódů HTTP najdete na stránce definice stavového kódu na webu W3C.

Následující příklad kódu zachycuje různé podmínky a vrací odpovídající odpověď.

[HttpDelete]
[Route("customers/{id:int}")]
public IHttpActionResult DeleteCustomer(int id)
{
    try
    {
        // Find the customer to be deleted in the repository
        var customerToDelete = repository.GetCustomer(id);

        // If there is no such customer, return an error response
        // with status code 404 (Not Found)
        if (customerToDelete == null)
        {
            return NotFound();
        }

        // Remove the customer from the repository
        // The DeleteCustomer method returns true if the customer
        // was successfully deleted
        if (repository.DeleteCustomer(id))
        {
            // Return a response message with status code 204 (No Content)
            // To indicate that the operation was successful
            return StatusCode(HttpStatusCode.NoContent);
        }
        else
        {
            // Otherwise return a 400 (Bad Request) error response
            return BadRequest(Strings.CustomerNotDeleted);
        }
    }
    catch
    {
        // If an uncaught exception occurs, return an error response
        // with status code 500 (Internal Server Error)
        return InternalServerError();
    }
}

Tip

Nezahrnujte informace, které by mohly být užitečné pro útočníka, který se pokouší proniknout do vašeho rozhraní API.

Řada webových serverů sama zachytává chybové stavy dřív, než dorazí do webového rozhraní API. Například pokud pro určitý web nakonfigurujete ověřování a uživatel nezadá správné údaje pro ověření, webový server by měl odpovědět stavovým kódem 401 (neoprávněný přístup). Po ověření klienta může váš kód provést vlastní kontroly a ověřit, zda má mít klient přístup k požadovanému prostředku. Pokud se tato autorizace nezdaří, měli byste vrátit stavový kód 403 (zakázáno).

Zajistěte konzistentní zpracování výjimek a protokolování informací o chybách

Pokud chcete konzistentně zpracovávat výjimky, zvažte implementaci globální strategie zpracování chyb napříč celým webovým rozhraním API. Měli byste také zavést zaznamenávání chyb do protokolu, který bude zachytávat všechny podrobné údaje o každé výjimce. Tento protokol chyb může obsahovat podrobné informace, ale nesmí být přes web přístupný pro klienty.

Rozlišujte mezi chybami na straně klienta a chybami na straně serveru

Protokol HTTP rozlišuje mezi chybami, ke kterým dojde kvůli klientské aplikaci (stavové kódy HTTP 4xx), a chybami způsobenými vinou serveru (stavové kódy HTTP 5xx). Dbejte na to, abyste tuto konvenci dodržovali ve všech zprávách s odpovědí na chyby.

Optimalizace přístupu k datům na straně klienta

V distribuovaném prostředí, třeba v prostředí zahrnujícím webový server a klientské aplikace, patří mezi hlavní zdroje případných potíží síť. Ta může představovat významný kritický bod, zejména pokud klientská aplikace často odesílá požadavky nebo přijímá data. Proto bychom se měli snažit omezit objem přenosů, které přes síť proudí. Při implementaci kód pro načítání a uchovávání dat vezměte v úvahu následující body:

Podpora ukládání do mezipaměti na straně klienta

Protokol HTTP 1.1 podporuje ukládání do mezipaměti v klientech a na zprostředkujících serverech, přes které požadavek prochází, prostřednictvím hlavičky Cache-Control. Když klientská aplikace odešle do webového rozhraní API požadavek HTTP GET, odpověď může obsahovat hlavičku Cache-Control, která určuje, jestli může klient nebo zprostředkující server, přes který požadavek prochází, bezpečně uložit text odpovědi do mezipaměti a za jak dlouho má vypršet jeho platnost a má se považovat za zastaralý.

Následující příklad ukazuje požadavek HTTP GET a odpovídající odpověď, která obsahuje hlavičku Cache-Control:

GET https://adventure-works.com/orders/2 HTTP/1.1
HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: text/json; charset=utf-8
Content-Length: ...
{"orderID":2,"productID":4,"quantity":2,"orderValue":10.00}

V tomto příkladu hlavička Cache-Control určuje, že platnost vrácených dat má vypršet po 600 sekundách, že jsou data určená jenom pro jednoho klienta a nesmí se ukládat do sdílené mezipaměti používané jinými klienty (tedy že mají vlastnost private). Hlavička Cache-Control může místo vlastnosti private určovat vlastnost public a v takovém případě se data dají uložit do sdílené mezipaměti, případně vlastnost no-store, která znamená, že klient nesmí data uložit do mezipaměti. Následující příklad kódu ukazuje, jak v odpovědi vytvořit hlavičku Cache-Control:

public class OrdersController : ApiController
{
    ...
    [Route("api/orders/{id:int:min(0)}")]
    [HttpGet]
    public IHttpActionResult FindOrderByID(int id)
    {
        // Find the matching order
        Order order = ...;
        ...
        // Create a Cache-Control header for the response
        var cacheControlHeader = new CacheControlHeaderValue();
        cacheControlHeader.Private = true;
        cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);
        ...

        // Return a response message containing the order and the cache control header
        OkResultWithCaching<Order> response = new OkResultWithCaching<Order>(order, this)
        {
            CacheControlHeader = cacheControlHeader
        };
        return response;
    }
    ...
}

Tento kód používá vlastní IHttpActionResult třídu s názvem OkResultWithCaching. Tato třída umožňuje kontroleru nastavit obsah hlavičky mezipaměti:

public class OkResultWithCaching<T> : OkNegotiatedContentResult<T>
{
    public OkResultWithCaching(T content, ApiController controller)
        : base(content, controller) { }

    public OkResultWithCaching(T content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
        : base(content, contentNegotiator, request, formatters) { }

    public CacheControlHeaderValue CacheControlHeader { get; set; }
    public EntityTagHeaderValue ETag { get; set; }

    public override async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response;
        try
        {
            response = await base.ExecuteAsync(cancellationToken);
            response.Headers.CacheControl = this.CacheControlHeader;
            response.Headers.ETag = ETag;
        }
        catch (OperationCanceledException)
        {
            response = new HttpResponseMessage(HttpStatusCode.Conflict) {ReasonPhrase = "Operation was cancelled"};
        }
        return response;
    }
}

Poznámka:

Protokol HTTP taky definuje pokyn no-cache (bez uložení) pro hlavičku Cache-Control. Tento pokyn překvapivě neznamená „neukládat do mezipaměti“, ale spíš „před vrácením znovu ověřit informace uložené v mezipaměti u serveru“. Data se smějí uložit do mezipaměti, ale při každém použití se kontroluje, jestli jsou ještě aktuální.

Správu mezipaměti má na starost klientská aplikace nebo zprostředkující server, při správné implementaci ale může šetřit šířku pásma a zlepšovat výkon, protože není potřeba načítat data, která se už jednou načetla.

Hodnota max-age v hlavičce Cache-Control je jenom orientační a nezaručuje, že se odpovídající data během zadané doby nezmění. Webové rozhraní API by mělo nastavit vhodnou hodnotu max-age podle očekávané volatility dat. Po uplynutí této doby by měl klient daný objekt odstranit z mezipaměti.

Poznámka:

Většina moderních webových prohlížečů podporuje ukládání do mezipaměti na straně klienta tím, že přidává do požadavků příslušné hlavičky Cache-Control a sleduje hlavičky výsledků, jak jsme popsali výše. Některé starší prohlížeče ale neukládají do mezipaměti hodnoty vrácené z adres URL, které obsahují řetězce dotazu. Pro vlastní klientské aplikace, které implementují vlastní strategie správy mezipaměti založené na popsaném protokolu, to většinou není problém.

Stejně se chovají i některé starší proxy servery a nemusejí ukládat do mezipaměti požadavky z adres URL s řetězci dotazu. To může být problematické pro vlastní klientské aplikace, které se přes takový proxy server připojují k webovému serveru.

V zájmu optimalizace zpracování dotazu poskytujte značky ETag

Když klientská aplikace načte objekt, může zpráva odpovědi obsahovat také značku entity (ETag). ETag je neprůhlený řetězec, který označuje verzi prostředku; při každé změně značky ETag se změní také prostředek. Tato značka ETag by se měla ukládat do mezipaměti spolu s daty klientské aplikace. Následující příklad kódu ukazuje, jak přidat značku ETag do odpovědi na požadavek HTTP GET. Tento kód pomocí metody GetHashCode objektu generuje číselnou hodnotu, která objekt identifikuje (tuto metodu můžete v případě potřeby potlačit a vygenerovat vlastní hodnotu hash pomocí algoritmu, jako je například MD5):

public class OrdersController : ApiController
{
    ...
    public IHttpActionResult FindOrderByID(int id)
    {
        // Find the matching order
        Order order = ...;
        ...

        var hashedOrder = order.GetHashCode();
        string hashedOrderEtag = $"\"{hashedOrder}\"";
        var eTag = new EntityTagHeaderValue(hashedOrderEtag);

        // Return a response message containing the order and the cache control header
        OkResultWithCaching<Order> response = new OkResultWithCaching<Order>(order, this)
        {
            ...,
            ETag = eTag
        };
        return response;
    }
    ...
}

Odpověď zaslaná webovým rozhraním API vypadá takto:

HTTP/1.1 200 OK
...
Cache-Control: max-age=600, private
Content-Type: text/json; charset=utf-8
ETag: "2147483648"
Content-Length: ...
{"orderID":2,"productID":4,"quantity":2,"orderValue":10.00}

Tip

Z bezpečnostních důvodů nepovolujte ukládání citlivých dat nebo dat vrácených přes ověřené připojení (HTTPS) do mezipaměti.

Klientská aplikace může kdykoli později vydat další požadavek GET na načtení stejného prostředku, a pokud se prostředek změnil (má jinou značku ETag), verzi v mezipaměti je potřeba zahodit a uložit místo ní novou verzi. Pokud je prostředek velký a vyžaduje pro přenos zpátky do klienta značné množství šířky pásma, opakované požadavky na načtení stejných dat můžou být neefektivní. Z toho důvodu protokol HTTP definuje následující proces optimalizace požadavků GET, který byste měli ve webovém rozhraní API podporovat:

  • Klient vytvoří požadavek GET obsahující značku ETag verze prostředku aktuálně uložené v mezipaměti, na kterou odkazuje hlavička HTTP If-None-Match:

    GET https://adventure-works.com/orders/2 HTTP/1.1
    If-None-Match: "2147483648"
    
  • Operace GET ve webovém rozhraní API získá aktuální značku ETag požadovaných dat (v předchozím příkladu objednávka 2) a porovná ji s hodnotou v hlavičce If-None-Match.

  • Pokud aktuální značka ETag požadovaných dat odpovídá značce ETag zadané v požadavku, prostředek se nezměnil a webové rozhraní API by mělo vrátit odpověď HTTP s prázdným textem zprávy a stavovým kódem 304 (neupraveno).

  • Pokud aktuální značka ETag požadovaných dat neodpovídá značce ETag zadané v požadavku, znamená to, že se data změnila a webové rozhraní API by mělo vrátit odpověď HTTP s textem zprávy obsahujícím nová data a stavovým kódem 200 (OK).

  • Pokud už požadovaná data neexistují, webové rozhraní API by mělo vrátit odpověď HTTP se stavovým kódem 404 (nenalezeno).

  • Klient používá stavový kód k údržbě mezipaměti. Pokud se data nezměnila (stavový kód 304), může daný objekt zůstat v mezipaměti a klientská aplikace by měla dál používat tuto verzi objektu. Pokud se data změnila (stavový kód 200), je potřeba objekt uložený v mezipaměti zahodit a nahradit ho novým objektem. Pokud už data nejsou dostupná (stavový kód 404), je potřeba objekt z mezipaměti odebrat.

Poznámka:

Pokud hlavička odpovědi obsahuje v hlavičce Cache-Control vlastnost no-store, objekt je potřeba vždy odebrat z mezipaměti bez ohledu na stavový kód HTTP.

Následující kód ukazuje metodu rozšířenou FindOrderByID tak, aby podporovala hlavičku If-None-Match. Všimněte si, že v případě vynechání hlavičky If-None-Match vždycky dojde k načtení příslušné objednávky:

public class OrdersController : ApiController
{
    [Route("api/orders/{id:int:min(0)}")]
    [HttpGet]
    public IHttpActionResult FindOrderByID(int id)
    {
        try
        {
            // Find the matching order
            Order order = ...;

            // If there is no such order then return NotFound
            if (order == null)
            {
                return NotFound();
            }

            // Generate the ETag for the order
            var hashedOrder = order.GetHashCode();
            string hashedOrderEtag = $"\"{hashedOrder}\"";

            // Create the Cache-Control and ETag headers for the response
            IHttpActionResult response;
            var cacheControlHeader = new CacheControlHeaderValue();
            cacheControlHeader.Public = true;
            cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);
            var eTag = new EntityTagHeaderValue(hashedOrderEtag);

            // Retrieve the If-None-Match header from the request (if it exists)
            var nonMatchEtags = Request.Headers.IfNoneMatch;

            // If there is an ETag in the If-None-Match header and
            // this ETag matches that of the order just retrieved,
            // then create a Not Modified response message
            if (nonMatchEtags.Count > 0 &&
                String.CompareOrdinal(nonMatchEtags.First().Tag, hashedOrderEtag) == 0)
            {
                response = new EmptyResultWithCaching()
                {
                    StatusCode = HttpStatusCode.NotModified,
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag
                };
            }
            // Otherwise create a response message that contains the order details
            else
            {
                response = new OkResultWithCaching<Order>(order, this)
                {
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag
                };
            }

            return response;
        }
        catch
        {
            return InternalServerError();
        }
    }
...
}

Tento příklad zahrnuje další vlastní třídu IHttpActionResult s názvem EmptyResultWithCaching. Tato třída slouží jednoduše jako obálka kolem objektu HttpResponseMessage a neobsahuje text odpovědi:

public class EmptyResultWithCaching : IHttpActionResult
{
    public CacheControlHeaderValue CacheControlHeader { get; set; }
    public EntityTagHeaderValue ETag { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public Uri Location { get; set; }

    public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        HttpResponseMessage response = new HttpResponseMessage(StatusCode);
        response.Headers.CacheControl = this.CacheControlHeader;
        response.Headers.ETag = this.ETag;
        response.Headers.Location = this.Location;
        return response;
    }
}

Tip

V tomto příkladu se značka ETag pro data vygeneruje pomocí algoritmu hash z dat načtených ze základního zdroje dat. Pokud se dá značka ETag vypočítat jakýmkoli jiným způsobem, je možné proces dál optimalizovat a data je potřeba načítat ze zdroje dat jenom v případě, že došlo k jejich změně. Tento přístup je zvlášť užitečný v případě, že jsou data velká nebo že může přístup ke zdroji dat způsobit velkou latenci (například pokud je zdrojem dat vzdálená databáze).

Podpora optimistické souběžnosti pomocí značek ETag

Pokud chcete povolit aktualizace dat uložených v mezipaměti, protokol HTTP podporuje strategii optimistické souběžnosti. Pokud po načtení a uložení prostředku do mezipaměti klientská aplikace následně odešle požadavek PUT nebo DELETE na změnu nebo odebrání prostředku, měla by obsahovat hlavičku If-Match, která odkazuje na značku ETag. Webové rozhraní API potom může na základě těchto informací zjistit, jestli prostředek po jeho načtení změnil jiný uživatel, a vrátit klientské aplikaci příslušnou odpověď, jak je vidět v tomto příkladu:

  • Klient vytvoří požadavek PUT obsahující nové podrobnosti o prostředku a značku ETag verze prostředku aktuálně uložené v mezipaměti, na kterou odkazuje hlavička HTTP If-Match. Následující příklad ukazuje požadavek PUT, která aktualizuje objednávku:

    PUT https://adventure-works.com/orders/1 HTTP/1.1
    If-Match: "2282343857"
    Content-Type: application/x-www-form-urlencoded
    Content-Length: ...
    productID=3&quantity=5&orderValue=250
    
  • Operace PUT ve webovém rozhraní API získá aktuální značku ETag požadovaných dat (v předchozím příkladu objednávka 1) a porovná ji s hodnotou v hlavičce If-Match.

  • Pokud aktuální značka ETag požadovaných dat odpovídá značce ETag zadané v požadavku, prostředek se nezměnil a webové rozhraní API by mělo provést aktualizaci a v případě úspěchu vrátit zprávu se stavovým kódem HTTP 204 (žádný obsah). Odpověď může obsahovat hlavičky Cache-Control a ETag aktualizované verze prostředku. Odpověď by měla vždycky obsahovat hlavičku Location, která odkazuje na identifikátor URI nově aktualizovaného prostředku.

  • Pokud aktuální značka ETag požadovaných dat neodpovídá značce ETag zadané v požadavku, znamená to, že data po načtení změnil jiný uživatel a webové rozhraní API by mělo vrátit odpověď HTTP s prázdným textem zprávy a stavovým kódem 412 (předběžná podmínka je neplatná).

  • Pokud už prostředek k aktualizaci neexistuje, webové rozhraní API by mělo vrátit odpověď HTTP se stavovým kódem 404 (nenalezeno).

  • Klient používá stavový kód a hlavičky odpovědi k údržbě mezipaměti. Pokud proběhla aktualizace dat (stavový kód 204), může objekt zůstat v mezipaměti (pokud hlavička Cache-Control neobsahuje vlastnost no-store), ale je potřeba aktualizovat značku ETag. Pokud data změnil jiný uživatel (stavový kód 412) nebo nebyla nalezena (stavový kód 404), měl by být objekt uložený v mezipaměti zahozen.

Následující příklad kódu ukazuje implementaci operace PUT pro kontroler Orders:

public class OrdersController : ApiController
{
    [HttpPut]
    [Route("api/orders/{id:int}")]
    public IHttpActionResult UpdateExistingOrder(int id, DTOOrder order)
    {
        try
        {
            var baseUri = Constants.GetUriFromConfig();
            var orderToUpdate = this.ordersRepository.GetOrder(id);
            if (orderToUpdate == null)
            {
                return NotFound();
            }

            var hashedOrder = orderToUpdate.GetHashCode();
            string hashedOrderEtag = $"\"{hashedOrder}\"";

            // Retrieve the If-Match header from the request (if it exists)
            var matchEtags = Request.Headers.IfMatch;

            // If there is an ETag in the If-Match header and
            // this ETag matches that of the order just retrieved,
            // or if there is no ETag, then update the Order
            if (((matchEtags.Count > 0 &&
                String.CompareOrdinal(matchEtags.First().Tag, hashedOrderEtag) == 0)) ||
                matchEtags.Count == 0)
            {
                // Modify the order
                orderToUpdate.OrderValue = order.OrderValue;
                orderToUpdate.ProductID = order.ProductID;
                orderToUpdate.Quantity = order.Quantity;

                // Save the order back to the data store
                // ...

                // Create the No Content response with Cache-Control, ETag, and Location headers
                var cacheControlHeader = new CacheControlHeaderValue();
                cacheControlHeader.Private = true;
                cacheControlHeader.MaxAge = new TimeSpan(0, 10, 0);

                hashedOrder = order.GetHashCode();
                hashedOrderEtag = $"\"{hashedOrder}\"";
                var eTag = new EntityTagHeaderValue(hashedOrderEtag);

                var location = new Uri($"{baseUri}/{Constants.ORDERS}/{id}");
                var response = new EmptyResultWithCaching()
                {
                    StatusCode = HttpStatusCode.NoContent,
                    CacheControlHeader = cacheControlHeader,
                    ETag = eTag,
                    Location = location
                };

                return response;
            }

            // Otherwise return a Precondition Failed response
            return StatusCode(HttpStatusCode.PreconditionFailed);
        }
        catch
        {
            return InternalServerError();
        }
    }
    ...
}

Tip

Použití hlavičky If-Match je naprosto volitelné, a pokud se nepoužije, webové rozhraní API se vždycky pokusí zadanou objednávku aktualizovat, možná ale při tom dojde k přepsání aktualizace provedené jiným uživatelem. Pokud chcete zabránit potížím způsobeným ztrátou určitých aktualizací, vždycky zadejte hlavičku If-Match.

Zpracování velkých požadavků a odpovědí

Může docházet k případům, kdy klientská aplikace potřebuje vydávat požadavky, které odesílají nebo přijímají data, která můžou mít velikost několika megabajtů (nebo větších). Čekání na přenos takového množství dat by mohlo vést k tomu, že klientská aplikace přestane reagovat. Pokud potřebujete zpracovávat požadavky zahrnující značné množství dat, zamyslete se nad následujícími body:

Optimalizujte požadavky a odpovědi zahrnující velké objekty

Některé prostředky můžou být velké objekty nebo obsahují velká pole, jako jsou grafické obrázky nebo jiné typy binárních dat. Webové rozhraní API by mělo podporovat optimalizované nahrávání a stahování těchto prostředků.

Protokol HTTP poskytuje kódovací mechanismus kódování v bloku pro streamování velkých datových objektů zpátky do klienta. Když klient odešle požadavek HTTP GET na velký objekt, webové rozhraní API může odpověď vrátit v menších blocích pomocí připojení HTTP. Délka dat v odpovědi nemusí být zpočátku známá (může se vygenerovat), takže server, který je hostitelem webového rozhraní API, by měl odeslat zprávu odpovědi s jednotlivými bloky dat, které místo hlavičky Content-Length určuje Transfer-Encoding: Chunked záhlaví. Klientská aplikace může po přijetí všech bloků sestavit celou odpověď. Přenos dat se dokončí ve chvíli, kdy server pošle zpátky poslední blok dat s nulovou velikostí.

Výsledkem jednoho požadavku by mohl být velmi velký objekt, který bude spotřebovávat značné množství prostředků. Pokud během procesu streamování webové rozhraní API zjistí, že množství dat v požadavku překročilo některé přijatelné hranice, může operaci přerušit a vrátit zprávu odpovědi se stavovým kódem 413 (entita požadavku je příliš velká).

Velikost velkých objektů přenášených přes síť můžete zmenšit pomocí komprese HTTP. Tento přístup pomáhá snížit objem provozu v síti a související latenci sítě, jenže za cenu toho, že vyžaduje další zpracování u klienta a na serveru hostujícím webové rozhraní API. Například klientská aplikace, která očekává příjem komprimovaných dat, může obsahovat hlavičku Accept-Encoding: gzip požadavku (lze také zadat jiné algoritmy komprese dat). Pokud server podporuje kompresi, měl by reagovat s obsahem uchovávaným ve formátu gzip v textu zprávy a Content-Encoding: gzip hlavičce odpovědi.

Kódovanou kompresi můžete zkombinovat se streamováním. Před streamováním nejdřív zkomprimujte data a určete v hlavičkách zpráv kódování obsahu gzip a kódování v bloku. Vezměte taky na vědomí, že některé webové servery (třeba server Internetové informační služby) se dají nakonfigurovat tak, aby automaticky komprimovaly odpovědi HTTP bez ohledu na to, jestli webové rozhraní API komprimuje data, nebo ne.

Implementace částečných odpovědí pro klienty, kteří nepodporují asynchronní operace

Vedle asynchronního streamování si může klientská aplikace explicitně vyžádat data velkých objektů po blocích. To se označuje jako částečné odpovědi. Klientská aplikace odešle požadavek HTTP HEAD, aby získala informace o objektu. Pokud webové rozhraní API podporuje částečné odpovědi, měla by odpovědět na požadavek HEAD zprávou s odpovědí, která obsahuje Accept-Ranges hlavičku a hlavičku Content-Length , která označuje celkovou velikost objektu, ale text zprávy by měl být prázdný. Klientská aplikace může na základě této informace vytvořit sérii požadavků GET, které určují velikost v bajtech pro přijetí. Webové rozhraní API by mělo vrátit zprávu odpovědi se stavem HTTP 206 (částečný obsah), hlavičkou Content-Length, která určuje skutečné množství dat zahrnutých do textu zprávy odpovědi, a hlavičku Content-Range, která označuje, která část (například bajty 4000 ) 8000objektu tato data představuje.

Požadavky HTTP HEAD a částečné odpovědi jsou podrobněji popsány v návrhu rozhraní API.

Vyhněte se v klientských aplikacích odesílání nepotřebných stavových zpráv 100-Continue

Klientská aplikace, která se chystá odeslat na server velké množství dat, může nejdřív zjistit, jestli je server ochotný požadavek přijmout. Před odesláním dat může klientská aplikace odeslat požadavek HTTP s hlavičkou Expect: 100-Continue, hlavičkou Content-Length udávající velikost dat a prázdným textem zprávy. Pokud je server ochotný požadavek zpracovat, měl by odpovědět zprávou, která udává stav HTTP 100 (pokračovat). Klientská aplikace potom může pokračovat a odeslat kompletní požadavek, který v textu zpráva obsahuje příslušná data.

Pokud hostujete službu pomocí Internetová informační služba (IIS), ovladač HTTP.sys automaticky rozpozná a zpracuje hlavičky Expect: 100-Continue před předáním požadavků webové aplikaci. To znamená, že tyto hlavičky pravděpodobně neuvidíte v kódu své aplikace a můžete předpokládat, že služba IIS už odfiltrovala všechny zprávy, které považuje za nevhodné nebo příliš velké.

Pokud vytváříte klientské aplikace pomocí rozhraní .NET Framework, všechny zprávy POST a PUT budou ve výchozím nastavení odesílat zprávy s hodnotou Expect: 100-Continue hlavičky. Stejně jako na straně serveru tento proces transparentně zpracovává rozhraní .NET Framework. Výsledkem tohoto procesu je ale to, že každý požadavek POST a PUT způsobí dva přenosy tam a zpátky na server, i v případě malých požadavků. Pokud vaše aplikace neodesílá požadavky s velkými objemy dat, můžete tuto funkci zakázat pomocí třídy ServicePointManager a vytvářet v klientské aplikaci objekty ServicePoint. Objekt ServicePoint zpracovává připojení, která klient naváže se serverem, na základě schématu a fragmentů hostitele identifikátorů URI, které označují prostředky na serveru. Vlastnost Expect100Continue objektu ServicePoint můžete nastavit na hodnotu false. Všechny následné požadavky POST a PUT odeslané klientem za použití identifikátoru URI, který odpovídá schématu a fragmentům hostitele objektu ServicePoint, se odešlou bez hlaviček Expect: 100-Continue. Následující kód ukazuje, jak nakonfigurovat objekt ServicePoint, který konfiguruje všechny požadavky odeslané na identifikátory URI se schématem http a hostitelem www.contoso.com.

Uri uri = new Uri("https://www.contoso.com/");
ServicePoint sp = ServicePointManager.FindServicePoint(uri);
sp.Expect100Continue = false;

Můžete také nastavit statickou Expect100Continue vlastnost ServicePointManager třídy určit výchozí hodnotu této vlastnosti pro všechny následně vytvořené ServicePoint objekty.

Podporujte stránkování u požadavků, které můžou vrátit velký počet objektů

Pokud určitá kolekce obsahuje velký počet prostředků, zaslání požadavku GET na odpovídající identifikátor URI může mít za následek velký objem operací zpracování na serveru, který hostuje webové rozhraní API, což může mít vliv na výkon a vygenerovat značný objem provozu v síti. Výsledkem může být vyšší latence.

Pro takové případy by mělo webové rozhraní API podporovat řetězce dotazů, které umožňují klientské aplikaci zpřesnit požadavky nebo načíst data v samostatných blocích (neboli stránkách), které se lépe zpracovávají. Následující kód ukazuje metodu GetAllOrders Orders v kontroleru. Tato metoda načítá podrobnosti o objednávkách. Kdyby byla tato metoda neomezená, mohla by případně vrátit velké množství dat. Cílem parametrů limit a offset je snížit objem dat na menší podmnožinu, v tomto případě ve výchozím nastavení jenom na prvních 10 objednávek:

public class OrdersController : ApiController
{
    ...
    [Route("api/orders")]
    [HttpGet]
    public IEnumerable<Order> GetAllOrders(int limit=10, int offset=0)
    {
        // Find the number of orders specified by the limit parameter
        // starting with the order specified by the offset parameter
        var orders = ...
        return orders;
    }
    ...
}

Klientská aplikace může odeslat požadavek pro načtení 30 objednávek s posunem 50 pomocí identifikátoru URI https://www.adventure-works.com/api/orders?limit=30&offset=50.

Tip

Nedoporučujeme povolovat klientským aplikacím zadávání řetězců dotazů, jejichž výsledkem je identifikátor URI delší než 2000 znaků. Mnoho webových klientů a serverů nedokáže zpracovat identifikátory URI, které jsou tak dlouhé.

Zachování rychlosti odezvy, škálovatelnosti a dostupnosti

Stejné webové rozhraní API může používat mnoho klientských aplikací běžících kdekoli na světě. Je potřeba mít jistotu, že je webové rozhraní API implementované tak, aby si při velké zátěži uchovalo rychlost odezvy, aby bylo škálovatelné v zájmu podpory velmi proměnlivé zátěže a aby zaručovalo dostupnost klientům, kteří provádějí důležité obchodní operace. Při zjišťování toho, jak těmto požadavkům vyhovět, se zamyslete nad následujícími body:

Poskytujte asynchronní podporu pro dlouhodobé požadavky

Požadavek, jehož zpracování může trvat dlouhou dobu, by se měl provádět tak, aby neblokoval klienta, který požadavek odeslal. Webové rozhraní API může provést počáteční kontrolu, která požadavek ověří, zahájit samostatnou úlohu, která provede samotnou práci, a potom vrátit odpověď s kódem HTTP 202 (přijato). Úloha může běžet asynchronně v rámci zpracování webového rozhraní API, nebo se dá přesunout do úlohy na pozadí.

Webové rozhraní API by taky mělo poskytovat mechanismus pro vrácení výsledků zpracování do klientské aplikace. Toho můžete dosáhnout tím, že poskytnete klientským aplikacím dotazovací mechanismus, který se bude pravidelně dotazovat, jestli už se zpracování dokončilo, a získá výsledek, nebo povolíte webovému rozhraní API odeslat oznámení po dokončení operace.

Jednoduchý dotazovací mechanismus můžete implementovat tak, že zadáte identifikátor URI polling, který funguje jako virtuální prostředek a používá následující postup:

  1. Klientská aplikace odešle do webového rozhraní API počáteční požadavek.
  2. Webové rozhraní API ukládá informace o požadavku v tabulce uložené ve službě Azure Table Storage nebo Microsoft Azure Cache a generuje jedinečný klíč pro tuto položku, pravděpodobně ve formě globálně jedinečného identifikátoru (GUID). Případně může být zpráva obsahující informace o požadavku a jedinečný klíč odeslána také přes Azure Service Bus .
  3. Webové rozhraní API zahájí zpracování jako samostatný úkol nebo knihovnu, jako je Hangfire. Webové rozhraní API zaznamená do tabulky stav úlohy Spuštěno.
    • Pokud používáte Azure Service Bus, zpracování zpráv by se provádělo odděleně od rozhraní API, případně pomocí Azure Functions nebo AKS.
  4. Webové rozhraní API vrátí zprávu odpovědi se stavovým kódem HTTP 202 (přijato) a identifikátorem URI obsahujícím jedinečný klíč vygenerovaný – například /polling/{guid}.
  5. Po dokončení úlohy webové rozhraní API uloží výsledky do tabulky a nastaví stav úkolu na Dokončeno. Upozorňujeme, že pokud se úloha nezdaří, webové rozhraní API může taky uložit informace o selhání a nastavit stav Neúspěšné.
    • Zvažte použití technik opakování k řešení možných přechodných selhání.
  6. Zatímco úloha běží, klient může pokračovat ve vlastních úlohách zpracování. Může pravidelně odesílat požadavek na identifikátor URI, který obdržel dříve.
  7. Webové rozhraní API v identifikátoru URI se dotazuje na stav odpovídajícího úkolu v tabulce a vrátí zprávu odpovědi se stavovým kódem HTTP 200 (OK) obsahujícím tento stav (Spuštěno, Dokončeno nebo Selhání). Pokud se úloha dokončí nebo se nezdaří, odpověď může taky obsahovat výsledky zpracování nebo dostupné informace o příčině selhání.
    • Pokud má dlouhotrvající proces více přechodných stavů, je lepší použít knihovnu, která podporuje model saga, jako je NServiceBus nebo MassTransit.

Některé možnosti, jak implementovat oznámení:

Zajistěte, aby byl každý požadavek bezstavový

Každý požadavek je vhodné brát jako atomický. Mezi jedním požadavkem odeslaným klientskou aplikací a pozdějšími požadavky odeslanými stejným klientem by neměly existovat žádné závislosti. Tento přístup pomáhá zajistit škálovatelnost. Instance webové služby se dají nasadit na různé servery. Klientské požadavky je možné směrovat na kteroukoli z těchto instancí, a to pokaždé se stejným výsledkem. Ze stejného důvodu se tím taky zlepšuje dostupnost. V případě selhání webového serveru je možné směrovat požadavky do jiné instance (pomocí služby Azure Traffic Manager), zatímco se server restartuje, takže se selhání nijak neprojeví na klientských aplikacích.

Sledujte klienty a implementujte omezování, abyste snížili riziko útoků DoS.

Pokud určitý klient odešle v daném období velký počet požadavků, může si zabrat celou službu pro sebe a ovlivnit tak výkon jiných klientů. Tento problém se dá zmírnit tím, že webové rozhraní API monitoruje volání z klientských aplikací, a to buď sledováním IP adres všech příchozích požadavků, nebo protokolováním veškerých ověřených přístupů. Tyto informace můžete použít k omezení přístupu k prostředkům. Pokud klient překročí stanovený limit, může webové rozhraní API vrátit odpověď se stavem 503 (služba není k dispozici) a hlavičkou Retry-After, která určuje, kdy může klient odeslat další požadavek, aby nedošlo k jeho odmítnutí. Tato strategie může pomoct snížit riziko útoku DoS (DoS) ze sady klientů, kteří systém zastavují.

Pečlivě spravujte trvalá připojení HTTP

Protokol HTTP podporuje trvalá připojení HTTP, pokud jsou dostupná. Specifikace HTTP 1.0 přidala hlavičku Connection:Keep-Alive, která umožňuje klientské aplikaci indikovat na server, že může použít stejné připojení k odesílání následných požadavků místo otevření nových požadavků. Pokud klient během doby stanovené hostitelem připojení znovu nevyužije, připojení se automaticky zavře. Toto chování je ve specifikaci HTTP 1.1 používané službami Azure nastavené jako výchozí, takže není nutné přidávat do zpráv hlavičky Keep-Alive.

Zachování otevřeného připojení může pomoct zlepšit rychlost reakcí díky snížení latence a uvolnění sítě, ale zároveň se může negativně projevit na škálovatelnosti, protože připojení můžou zbytečně zůstávat otevřená delší dobu, než je potřeba, což omezuje schopnost připojení jiných souběžných klientů. Také se to projevuje na výdrži baterie, pokud klientská aplikace běží na mobilním zařízení. Pokud aplikace odesílá požadavky na server jenom občas, udržování otevřeného připojení může baterii rychleji vybíjet. Pokud se chce klient ujistit, že při použití specifikace HTTP 1.1 nebude připojení trvalé, může do zpráv zahrnout hlavičku Connection:Close, která výchozí chování potlačí. Stejně tak pokud server obsluhuje velmi velké množství klientů, může do odpovědí zahrnout hlavičku Connection:Close, která zajistí zavření připojení a šetření prostředků serveru.

Poznámka:

Trvalá připojení HTTP jsou čistě volitelná funkce, která snižuje zátěž sítě spojenou s opakovaným vytvářením komunikačního kanálu. Webové rozhraní API ani klientská aplikace by neměly být závislé na tom, jestli je dostupné trvalé připojení HTTP. Nepoužívejte trvalá připojení HTTP k implementaci systémů oznámení ve stylu Comet; místo toho byste měli používat sokety (nebo webové sokety, pokud jsou k dispozici) ve vrstvě PROTOKOLU TCP. Nakonec připomínáme, že pokud klientská aplikace komunikuje se serverem přes proxy server, je použití hlaviček Keep-Alive omezené. Trvalé bude jenom připojení mezi klientem a proxy serverem.

Publikování a správa webového rozhraní API

Webové rozhraní API je dostupné pro klientské aplikace až po nasazení do hostitelského prostředí. Tímto prostředím je většinou webový server, i když se může jednat i o jiný typ hostitelského procesu. Při publikování webového rozhraní API byste se měli zamyslet nad následujícími body:

  • Všechny požadavky musí být ověřený a oprávněné a je potřeba vynucovat odpovídající úroveň řízení přístupu.
  • Na komerční webové rozhraní API se můžou vztahovat různé záruky kvality týkající se doby odezvy. Je důležité zajistit, aby hostitelské prostředí bylo škálovatelné, pokud se zatížení v průběhu času může výrazně lišit.
  • Požadavky může být potřeba za účelem finančního zhodnocení měřit.
  • Může být potřeba regulovat tok provozu do webového rozhraní API a implementovat omezení šířky pásma pro konkrétní klienty, kteří vyčerpali přidělenou kvótu.
  • Zákonné požadavky můžou ukládat povinnost protokolovat a auditovat všechny požadavky a odpovědi.
  • K zajištění dostupnosti může být potřeba monitorovat stav serveru, který hostuje webové rozhraní API, a v případě potřeby ho restartovat.

Je užitečné oddělit tyto problémy od technických problémů týkajících se implementace webového rozhraní API. Z tohoto důvodu zvažte vytvoření fasády tvořené samostatným procesem, který přesměrovává požadavky do webového rozhraní API. Tato fasáda může poskytovat operace správy a předávat potvrzené požadavky do webového rozhraní API. Použití fasády může přinést řadu funkčních výhod, jako jsou tyto:

  • Funguje jako bod integrace pro několik webových rozhraní API.
  • Transformuje zprávy a překládá komunikační protokoly pro klienty sestavené pomocí různých technologií.
  • Ukládání požadavky a odpovědi do mezipaměti, aby snížila zátěž serveru hostujícího webové rozhraní API.

Testování webového rozhraní API

Webové rozhraní API je potřeba podrobit stejně důkladnému testování jako jakýkoli jiný software. Měli byste zvážit vytvoření testů jednotek pro ověření funkčnosti.

Povaha webového rozhraní API přináší vlastní dodatečné požadavky na ověření, že funguje správně. Zvláštní pozornost byste měli věnovat následujícím aspektům:

  • Otestujte všechny trasy, abyste si ověřili, jestli volají správné operace. Věnujte pozornost hlavně tomu, když se neočekávaně vrátí stavový kód HTTP 405 (metoda není povolena), protože to může upozorňovat na neshodu mezi trasou a metodami HTTP (GET, POST, PUT, DELETE), které se dají na tuto trasu odeslat.

    Odešlete požadavky HTTP na trasy, které je nepodporují, například odeslání požadavku POST do konkrétního prostředku (požadavky POST by se měly odesílat jenom do kolekcí prostředků). V takových případech by měl být jedinou platnou odpovědí stavový kód 405 (nepovoleno).

  • Ověřte, jestli jsou všechny trasy správně chráněné a jestli se na nich provádějí příslušné kontroly ověřování a autorizace.

    Poznámka:

    Některé aspekty zabezpečení, třeba ověřování uživatelů, má pravděpodobně na starost spíš hostitelské prostředí než webové rozhraní API, ale i přesto je potřeba v rámci procesu nasazení zabezpečení otestovat.

  • Otestujte zpracování výjimek prováděné jednotlivými operacemi a ověřte, jestli se do klientské aplikace vrací odpovídající a smysluplná odpověď HTTP.

  • Ověřte, jestli jsou zprávy požadavků a odpovědí správně vytvořené. Například pokud požadavek HTTP POST obsahuje data pro nový prostředek ve formátu x--www-form-urlencoded, zkontrolujte, jestli odpovídající operace správně analyzuje data, vytvoří prostředky a vrátí odpověď obsahující podrobnosti o novém prostředku včetně správné hlavičky Location.

  • Zkontrolujte všechny odkazy a identifikátory URI v odpovědích. Například zpráva HTTP POST by měla vrátit identifikátor URI nově vytvořeného prostředku. Všechny odkazy HATEOAS by měly být platné.

  • Ujistěte se, že jednotlivé operace vracejí při různých kombinacích vstupu správný stavový kód. Příklad:

    • Pokud je dotaz úspěšný, měl by vrátit stavový kód 200 (OK).
    • Pokud prostředek není možné najít, operace by měla vrátit stavový kód HTTP 404 (nenalezeno).
    • Pokud klient odešle požadavek, který úspěšně provede odstranění prostředku, musí se vrátit stavový kód 204 (žádný obsah).
    • Pokud klient odešle požadavek, který vytvoří nový prostředek, měl by být stavový kód 201 (vytvořen).

Věnujte pozornost neočekávaným stavovým kódům odpovědí v rozsahu 5xx. Tyto zprávy většinou hlásí hostitelský server, aby oznámil, že se mu nepodařilo provést platný požadavek.

  • Otestujte různé kombinace hlaviček požadavků, které může klientská aplikace použít, a zkontrolujte, jestli webové rozhraní API vrací v odpovědích očekávané informace.

  • Otestujte řetězce dotazů. Pokud může určitá operace používat volitelné parametry (například požadavky na stránkování), otestujte různé kombinace a pořadí parametrů.

  • Zkontrolujte, jestli se úspěšně dokončují asynchronní operace. Pokud webové rozhraní API podporuje streamování u požadavků, které vracejí velké binární objekty (třeba video nebo zvukové soubory), ujistěte se, že při streamování dat nedochází k blokování klientských požadavků. Pokud webové rozhraní API implementuje dotazování na dlouhotrvající operace úpravy dat, ověřte, že operace hlásí jejich stav správně, jak budou pokračovat.

Doporučujeme taky vytvořit a spustit testy výkonu, abyste zkontrolovali, jestli webové rozhraní API uspokojivě funguje při větší zátěži. Pomocí sady Visual Studio Ultimate můžete vytvořit projekt testování výkonu webu a zátěžového testu.

Použití služby Azure API Management

V Azure zvažte použití služby Azure API Management k publikování a správě webového rozhraní API. Pomocí Azure API Managementu můžete vygenerovat službu, která funguje jako fasáda pro jedno nebo víc webových rozhraní API. Služba je sama škálovatelnou webovou službou, kterou můžete vytvořit a nakonfigurovat pomocí webu Azure Portal. Pomocí této služby můžete následujícím způsobem publikovat a spravovat webové rozhraní API:

  1. Nasaďte webové rozhraní API na web, do cloudové služby Azure nebo na virtuální počítač Azure.

  2. Připojte k webovému rozhraní API službu API Management. Požadavky odeslané na adresu URL služby API Management procházejí mapováním na identifikátory URI ve webovém rozhraní API. Stejná služba API Management může směrovat požadavky do několika webových rozhraní API. Díky tomu můžete agregovat několik webových rozhraní API do jedné služby správy. Podobně latí, že pokud potřebujete omezit nebo rozdělit funkce dostupné různým aplikacím, může na stejné webové rozhraní API odkazovat víc než jedna služba API Management.

    Poznámka:

    Identifikátory URI v odkazech HATEOAS, které se generují jako součást odpovědi na požadavky HTTP GET, by měly odkazovat na adresu URL služby API Management, a ne na webový server, který je hostitelem webového rozhraní API.

  3. Pro každé webové rozhraní API určete, jaké operace HTTP má webové rozhraní API zpřístupňovat, a všechny volitelné parametry, které jednotlivé operace připouštějí jako vstupy. Můžete taky nastavit, jestli má služba API Management ukládat odpovědi přijaté z webového rozhraní API do mezipaměti za účelem optimalizace opakovaných požadavků na stejná data. Poznamenejte si podrobné údaje o odpovědích HTTP, které může každá operace vygenerovat. Tyto informace slouží ke generování dokumentace pro vývojáře, takže je důležité, aby byly přesné a úplné.

    Operace můžete buď definovat ručně pomocí průvodců poskytovaných webem Azure Portal, nebo je můžete importovat ze souboru obsahujícího definice ve formátu WADL nebo Swagger.

  4. Nakonfigurujte nastavení zabezpečení pro komunikaci mezi službou API Management a webovým serverem, který je hostitelem webového rozhraní API. Služba API Management v současné době podporuje základní ověřování a vzájemné ověřování pomocí certifikátů a autorizaci uživatelů OAuth 2.0 (Open Authorization).

  5. Vytvoření produktu. Produkt je jednotka publikování. Do produktu můžete přidat webová rozhraní API, která jste předtím připojili ke službě správy. Po publikování produktu budou webová rozhraní API dostupná pro vývojáře.

    Poznámka:

    Před publikováním produktu můžete také určit skupiny uživatelů, které můžou získat k produktu přístup, a přidat do nich uživatele. Získáte tak kontrolu nad tím, kteří vývojáři a aplikace můžou webové rozhraní API používat. Pokud webové rozhraní API podléhá schválení, musí vývojář, který chce k němu získat přístup, nejdřív zaslat požadavek správci produktu. Správce může vývojáři udělit nebo odepřít přístup. Při změně okolností je taky možné zablokovat stávající vývojáře.

  6. Nakonfigurujte zásady pro jednotlivá webová rozhraní API. Zásady určují různé aspekty, třeba to, jestli mají být povolená volání napříč doménami, jak ověřovat klienty, jestli má probíhat transparentní převod mezi formáty dat XML a JSON, jestli se mají omezit volání z daného rozsahu IP adres, jestli se mají uplatňovat kvóty využití a jestli má být omezený počet volání. Zásady je možné uplatňovat globálně v rámci celého produktu, pro jedno webové rozhraní API v produktu nebo pro jednotlivé operace ve webovém rozhraní API.

Další informace najdete v dokumentaci ke službě API Management.

Tip

Azure poskytuje nástroj Azure Traffic Manager, který umožňuje implementovat převzetí služeb při selhání a vyrovnávání zatížení a snižuje latenci ve více instancích webu, které mají hostitele v různých geografických umístěních. Azure Traffic Manager můžete používat v kombinaci se službou API Management. Služba API Management může pomocí Azure Traffic Manageru směrovat požadavky na instance určitého webu. Další informace najdete v tématu Metody směrování Traffic Manageru.

Pokud v této struktuře použijete vlastní názvy DNS pro vaše weby, měli byste nakonfigurovat odpovídající záznam CNAME pro každý web tak, aby odkazovat na název DNS webu Azure Traffic Manageru.

Podpora vývojářů na straně klienta

Vývojáři, kteří vytvářejí klientské aplikace, většinou potřebují informace o tom, jak získat přístup k webovému rozhraní API, a dokumentaci týkající se parametrů, datových typů, návratových typů a návratových kódů, která popisuje různé požadavky a odpovědi odesílané mezi webovou službou a klientskou aplikací.

Zdokumentujte operace REST webového rozhraní API

Služba Azure API Management obsahuje portál pro vývojáře, který popisuje operace REST vystavené prostřednictvím webového rozhraní API. Každý publikovaný produkt se zobrazí na tomto portálu. Vývojáři si můžou na tomto portálu zaregistrovat přístup a správce může jejich požadavek schválit nebo odmítnout. V případě schválení dostane vývojář přiřazený klíč předplatného sloužící k ověřování volání z klientských aplikací, které daný vývojář vyvíjí. Tento klíč je potřeba zadat do každého volání webového rozhraní API, jinak dojde k odmítnutí volání.

Portál navíc poskytuje následující prvky:

  • Dokumentace k produktu obsahující seznam operací, které produkt zpřístupňuje, požadované parametry a různé odpovědi, které se můžou vrátit. Všimněte si, že se tyto informace generují z údajů poskytnutých v kroku 3 seznamu v části Publikování webového rozhraní API pomocí služby Microsoft Azure API Management.
  • Fragmenty kódu, které ukazují, jak vyvolat operace z různých jazyků, včetně jazyků JavaScript, C#, Java, Ruby, Python a PHP.
  • Konzola pro vývojáře, která vývojáři umožňuje odesláním požadavku HTTP otestovat jednotlivé operace v produktu a zobrazit výsledky.
  • Stránka, na které může vývojář nahlásit případné problémy nebo potíže, se kterými se setkal.

Azure Portal umožňuje přizpůsobit vývojářský portál a změnit styl a rozložení tak, aby odpovídalo značce vaší organizace.

Implementace sady SDK klienta

Při sestavování klientské aplikace, která za účelem přístupu k webovému rozhraní API volá požadavky REST, je potřeba napsat značné množství kódu tvořícího jednotlivé požadavky a vhodně ho naformátovat, odeslat požadavek na server, který hostuje danou webovou službu, a analyzovat odpověď, aby se zjistilo, jestli byl požadavek úspěšný, nebo ne, případně aby se extrahovala vrácená data. Pokud chcete klientskou aplikaci od těchto úkonů izolovat, můžete poskytnout sadu SDK, která zabalí rozhraní REST a abstrahuje tyto podrobnosti nízké úrovně do funkčnější sady metod. Klientská aplikace bude používat tyto metody, které transparentně převádějí volání na požadavky REST, a převádět odpovědi zpátky na návratové hodnoty metod. Jedná se o běžný postup implementovaný řadou služeb včetně sady Azure SDK.

Vytvoření sady SDK na straně klienta je významný krok, který je potřeba konzistentně implementovat a důkladně otestovat. Velkou část tohoto procesu je ale možné zmechanizovat a řada dodavatelů poskytuje nástroje, které dokážou mnohé z těchto úloh automatizovat.

Monitorování webového rozhraní API

V závislosti na způsobu publikování a nasazení vašeho webového rozhraní API můžete webové rozhraní API monitorovat buď přímo, nebo shromažďovat údaje o využití a stavu prostřednictvím analýzy přenosů procházejících přes službu API Management.

Přímé monitorování webového rozhraní API

Pokud jste webové rozhraní API implementovali pomocí šablony ASP.NET Web API (buď jako projekt webového rozhraní API, nebo jako webovou roli v cloudové službě Azure) a sady Visual Studio 2013, můžete shromažďovat údaje o dostupnosti, výkonu a využití pomocí ASP.NET Application Insights. Application Insights je balíček, který v případě nasazení webového rozhraní API do cloudu transparentně sleduje a zaznamenává informace o požadavcích a odpovědích. Po instalaci a konfiguraci balíčku vůbec nemusíte upravovat kód webového rozhraní API, abyste mohli balíček používat. V případě nasazení webového rozhraní API na webový server probíhá zkoumání veškerých přenosů a shromažďování následujících statistik:

  • Doba odezvy serveru
  • Počet požadavků na server a podrobnosti o každém z nich
  • Nejpomalejší požadavky z hlediska průměrné doby odezvy
  • Podrobnosti o všech neúspěšných požadavcích
  • Počet relací spuštěných různými prohlížeči a uživatelskými agenty
  • Nejčastěji zobrazované stránky (užitečné spíš pro webové aplikace než pro webová rozhraní API)
  • Různé role uživatelů, kteří získávají přístup k webovému rozhraní API

Tato data můžete zobrazit v reálném čase na webu Azure Portal. Můžete také vytvořit webové testy, které monitorují stav webového rozhraní API. Webový test odešle pravidelný požadavek na zadaný identifikátor URI ve webovém rozhraní API a zachytí odpověď. Můžete zadat definici úspěšné odpovědi (třeba stavový kód HTTP 200), a pokud požadavek tuto odpověď nevrátí, můžete nastavit odeslání výstrahy správci. V případě potřeby může správce restartovat server, který je hostitelem webového rozhraní API, pokud na něm došlo k chybě.

Další informace najdete v tématu Application Insights – začínáme s rozhraním ASP.NET.

Monitorování webového rozhraní API prostřednictvím služby API Management

Pokud jste webové rozhraní API publikovali pomocí služby API Management, stránka API Management na webu Azure Portal obsahuje řídicí panel, který umožňuje zobrazit celkový výkon služby. Na stránce Analýza můžete přejít k podrobnostem o využití produktu. Tato stránka obsahuje následující karty:

  • Využití. Tato karta obsahuje informace o počtu provedených volání rozhraní API a šířce pásma, která se v průběhu času využívala ke zpracování těchto volání. Údaje o využití můžete filtrovat podle produktu, rozhraní API a operace.
  • Stav. Na této kartě se můžete podívat na výsledky požadavků na rozhraní API (vrácené stavové kódy HTTP), efektivitu zásady ukládání do mezipaměti, dobu odezvy rozhraní API a dobu odezvy služby. Údaje o stavu můžete zase filtrovat podle produktu, rozhraní API a operace.
  • Aktivita. Tato karta obsahuje textové shrnutí počtu úspěšných volání, neúspěšných volání, zablokovaných volání, průměrné doby odezvy a dob odezvy jednotlivých produktů, webového rozhraní API a operací. Tato stránka obsahuje taky počet volání provedených jednotlivými vývojáři.
  • Na první pohled. Tato karta obsahuje shrnutí dat výkonu včetně vývojářů zodpovědných za většinu volání rozhraní API a produktů, webových rozhraní API a operací, které tato volání přijaly.

Na základě těchto informací můžete zjistit, jestli určité webové rozhraní API nebo operace způsobuje kritický bod, a v případě potřeby provést škálování hostitelského prostředí a přidat další servery. Můžete taky zjistit, jestli jedna nebo několik aplikací využívá neúměrné množství prostředků, a uplatnit odpovídající zásady pro nastavení kvót a omezení počtu volání.

Poznámka:

Můžete změnit podrobné údaje týkající se publikovaného produktu a tyto změny se okamžitě projeví. Můžete například přidat operaci do webového rozhraní API nebo ji z něj odebrat, aniž byste museli znovu publikovat produkt, ve kterém se toto webové rozhraní API nachází.

Další kroky

  • V článku Protokol OData a webové rozhraní API ASP.NET najdete příklady a další informace o implementaci webového rozhraní API s protokolem OData pomocí rozhraní ASP.NET.
  • Představujeme podporu dávek ve webovém rozhraní API a OData webového rozhraní API popisuje, jak implementovat dávkové operace ve webovém rozhraní API pomocí OData.
  • Vzory idempotence na blogu Jonathana Olivera poskytují přehled idempotenci a to, jak souvisí s operacemi správy dat.
  • Definice stavového kódu na webu W3C obsahují úplný seznam stavových kódů HTTP a jejich popisy.
  • Článek Spouštění úloh na pozadí pomocí WebJobs obsahuje informace a příklady týkající se provádění operací na pozadí pomocí WebJobs.
  • Azure Notification Hubs upozorní uživatele , jak pomocí centra oznámení Azure odesílat asynchronní odpovědi do klientských aplikací.
  • Článek API Management popisuje, jak publikovat produkt, který poskytuje kontrolovaný a zabezpečený přístup k webovému rozhraní API.
  • Referenční informace k rozhraní REST API služby Azure API Management popisují použití rozhraní REST API služby API Management k vytváření vlastních aplikací pro správu.
  • Metody směrování Traffic Manageru shrnují způsob použití Azure Traffic Manageru k vyrovnávání zatížení požadavků napříč několika instancemi webu hostujícího webové rozhraní API.
  • Článek Application Insights – začínáme s rozhraním ASP.NET obsahuje podrobné informace o instalaci a konfiguraci Application Insights v projektu webového rozhraní API ASP.NET.