Implementering av webb-API

Ett noggrant utformat RESTful-webb-API definierar de resurser, relationer och navigeringsscheman som är tillgängliga för klientprogram. När du implementerar och distribuerar ett webb-API bör du tänka på fysiska kraven i miljön som värd för webb-API:et och på vilket sätt webb-API:et är konstruerat i stället för den logiska strukturen hos data. Den här vägledningen fokuserar på metodtips för att implementera ett webb-API och publicera det för att göra det tillgängligt för klientprogram. Detaljerad information om webb-API-design finns i Webb-API-design.

Bearbetning av begäranden

Tänk på följande när du implementerar koden för att hantera begäranden.

Åtgärderna GET, PUT, DELETE, HEAD och PATCH ska vara idempotenta

Den kod som implementerar dessa begäranden bör inte införa några sidoeffekter. Samma begäran som upprepas över samma resurs resulterar i samma tillstånd. Att till exempel skicka flera DELETE-begäranden till samma URI bör ha samma effekt, även om HTTP-statuskoden i svarsmeddelandena kan vara annorlunda. Den första DELETE-begäran kan returnera statuskod 204 (Inget innehåll), medan en efterföljande DELETE-begäran kan returnera statuskod 404 (Hittades inte).

Kommentar

Artikeln Idempotency Patterns på Jonathan Olivers blogg ger en översikt över idempotens och hur den relaterar till datahanteringsåtgärder.

POST-åtgärder som skapar nya resurser bör inte ha orelaterade sidoeffekter

Om en POST-begäran är avsedd att skapa en ny resurs bör effekterna av begäran begränsas till den nya resursen (och eventuellt eventuella direkt relaterade resurser om det finns någon form av koppling). I ett e-handelssystem kan till exempel en POST-begäran som skapar en ny beställning för en kund också ändra lagernivåer och generera faktureringsinformation, men den bör inte ändra information som inte är direkt relaterad till ordern eller ha andra biverkningar på systemets övergripande tillstånd.

Undvik att implementera trafikintensiva POST-, PUT- och DELETE-åtgärder

Stöd för POST-, PUT- och DELETE-begäranden över resurssamlingar. En POST-begäran kan innehålla information för flera nya resurser och lägga till dem i samma samling, en PUT-begäran kan ersätta hela uppsättningen resurser i en samling och en DELETE-begäran kan ta bort en hel samling.

OData-stödet i ASP.NET webb-API 2 ger möjlighet till gruppbegäranden. Ett klientprogram kan packa upp flera webb-API-begäranden och skicka dem till servern i en enda HTTP-begäran och få ett enda HTTP-svar som innehåller svaren på varje begäran. Mer information finns i Introduktion till batchstöd i Webb-API och Webb-API OData.

Följ HTTP-specifikationen när du skickar ett svar

Ett webb-API måste returnera meddelanden som innehåller rätt HTTP-statuskod för att göra det möjligt för klienten att bestämma hur resultatet ska hanteras, lämpliga HTTP-huvuden så att klienten förstår resultatet och en lämpligt formaterad text så att klienten kan tolka resultatet.

Till exempel ska en POST-åtgärd returnera statuskod 201 (skapad) och svarsmeddelandet ska innehålla URI för den nyligen skapade resursen i Location-huvudet i svarsmeddelandet.

Stöd för innehållsförhandling

Texten i ett svarsmeddelande kan innehålla data i olika format. En HTTP GET-begäran kan till exempel returnera data i JSON- eller XML-format. När klienten skickar en begäran kan den inkludera ett Accept-huvud som anger de dataformat som kan hanteras. Dessa format anges som medietyper. En klient som utfärdar en GET-begäran som hämtar en avbildning kan till exempel ange ett Accept-huvud som visar de medietyper som klienten kan hantera, till exempel image/jpeg, image/gif, image/png. När webb-API:et returnerar resultatet bör det formatera informationen med hjälp av någon av de här medietyperna och ange formatet i svarets Content-Type-huvud.

Om klienten inte anger ett Accept-huvud, använder du ett rimligt standardformat för svarstexten. Exempelvis används som standard JSON för textbaserade data för webb-API-ramverket för ASP.NET.

HATEOAS-metoden gör att en klient kan navigera och identifiera resurser från en första startpunkt. Detta uppnås med hjälp av länkar som innehåller URI:er. När en klient skickar en HTTP GET-begäran för att hämta en resurs ska svaret innehålla URI:er som gör att ett klientprogram snabbt kan hitta direkt relaterade resurser. I till exempel ett webb-API som stöder en e-handelslösning kan en kund ha skapat många order. När ett klientprogram hämtar information för en kund bör svaret innehålla länkar som gör att klientprogrammet kan skicka HTTP GET-begäranden som kan hämta dessa order. Dessutom bör HATEOAS-länkar beskriva de andra åtgärderna (POST, PUT, DELETE och så vidare) som varje länkad resurs stöder tillsammans med motsvarande URI för varje begäran. Den här metoden beskrivs mer detaljerat i API-design.

Det finns inga standarder som styr implementeringen av HATEOAS men i följande exempel visas en möjlig metod. I det här exemplet returnerar en HTTP GET-begäran som hittar informationen för en kund ett svar som innehåller HATEOAS-länkar som refererar till order för kunden:

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"]}
]}

I det här exemplet representeras kunddata av klassen Customer som visas i följande kodavsnitt. HATEOAS-länkarna finns i samlingsegenskapen 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; }
}

HTTP GET-åtgärden hämtar kunddata från lagringen och skapar ett Customer-objekt och fyller sedan i samlingen Links. Resultatet formateras som ett JSON-svarsmeddelande. Varje länk innehåller följande fält:

  • Relationen (Rel) mellan objektet som returneras och objektet som beskrivs av länken. I det här fallet self indikerar att länken är en referens tillbaka till själva objektet (liknar en this pekare på många objektorienterade språk) och orders är namnet på en samling som innehåller relaterad orderinformation.
  • Hyperlänken (Href) för objektet som beskrivs av länken i form av en URI.
  • Typ av HTTP-begäran (Action) som kan skickas till denna URI.
  • Formatet för alla data (Types) som ska finnas i HTTP-begäran eller som kan returneras i svaret, beroende på vilken typ av begäran som är aktuell.

HATEOAS-länkarna som visas i exemplet på HTTP-svar anger att ett klientprogram kan utföra följande åtgärder:

  • En HTTP GET-begäran till URI:n https://adventure-works.com/customers/2 för att hämta information om kunden (igen). Data kan returneras som XML eller JSON.
  • En HTTP PUT-begäran till URI:n https://adventure-works.com/customers/2 för att ändra kundens information. Den nya informationen måste anges i meddelandet med begäran i formatet x-www-form-urlencoded.
  • En HTTP DELETE-begäran till URI:n https://adventure-works.com/customers/2 för att ta bort kunden. Begäran förväntar sig inte någon ytterligare information eller returnerar data i bara svarets meddelandetext.
  • En HTTP GET-begäran till URI:n https://adventure-works.com/customers/2/orders för att hitta alla order för kunden. Data kan returneras som XML eller JSON.
  • En HTTP POST-begäran till URI https://adventure-works.com/customers/2/orders :n om att skapa en ny beställning för den här kunden. Informationen måste anges i meddelandet med begäran i formatet x-www-form-urlencoded.

Hantering av undantag

Tänk på följande om en åtgärd utlöser ett undantag utan felhantering.

Fånga undantag och returnera ett meningsfullt svar till klienter

Den kod som implementerar en HTTP-åtgärd bör ge omfattande undantagshantering i stället för att låta undantag utan felhantering spridas till ramverket. Om ett undantag gör det omöjligt att slutföra åtgärden kan undantaget skickas tillbaka i svarsmeddelandet, men det bör innehålla en beskrivning av felet som orsakade undantaget. Undantaget bör också innehålla lämplig HTTP-statuskod i stället för bara returnera statuskod 500 för alla situationer. Om till exempel en användarbegäran orsakar en databas som bryter mot ett villkor (t.ex. försök att ta bort en kund som har utestående order) ska du returnera statuskod 409 (konflikt) och en meddelandetext som anger orsaken till konflikten. Om några andra villkor gör att begäran inte kan uppnås kan du returnera statuskod 400 (felaktig begäran). Du hittar en fullständig lista över HTTP-statuskoder på sidan Statuskoddefinitioner på W3C-webbplatsen.

Kodexemplet fångar olika villkor och returnerar ett lämpligt svar.

[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();
    }
}

Dricks

Inkludera inte information som kan vara användbar för en angripare som försöker ta sig in i ditt API.

Många webbservrar fångar felvillkor själva innan de når webb-API. Om du till exempel konfigurerar autentisering för en webbplats och användaren inte anger rätt autentiseringsinformation bör webbservern svara med statuskod 401 (obehörig). När en klient har autentiserats kan din kod utföra egna kontroller för att verifiera att klienten kan få åtkomst till den begärda resursen. Om auktoriseringen misslyckas bör du returnera statuskod 403 (förbjudet).

Hantera undantag konsekvent och logga information om fel

Överväg att implementera en global strategi för felhantering i hela webb-API:et för att hantera undantag på ett konsekvent sätt. Du bör också ta med felloggning som samlar in fullständig information om varje undantag. Den här felloggen kan innehålla detaljerad information så länge den inte görs tillgänglig på webben för klienter.

Skilja mellan fel på klientsidan och serversidan

HTTP-protokollet skiljer mellan fel som uppstår på grund av klientprogrammet (HTTP 4xx-statuskoder) och fel som orsakas av problem på servern (HTTP 5xx-statuskoder). Se till att du följer denna konvention i felsvarsmeddelanden.

Optimera dataåtkomst på klientsidan

I en distribuerad miljö, till exempel en som inbegriper en webbserver och klientprogram, är nätverket en av de primära källorna. Det kan fungera som en betydande flaskhals, särskilt om ett klientprogram ofta skickar begäranden eller tar emot data. Därför bör du försöka minska mängden trafik som flödar i nätverket. Tänk på följande när du implementerar kod för att hämta och lagra data:

Stöd för cachelagring på klientsidan

HTTP 1.1-protokollet har stöd för cachelagring i klienter och mellanliggande servrar, genom vilka en begäran dirigeras med hjälp av Cache-Control-huvudet. När ett klientprogram skickar en HTTP GET-begäran till webb-API:et kan svaret inkludera ett Cache-Control-huvud som anger om data i svarstexten på ett säkert sätt kan cachelagras av klienten eller en mellanliggande server genom vilken begäran har dirigerats, och hur lång tid den är aktuell innan den upphör att gälla.

I följande exempel visas en HTTP GET-begäran och motsvarande svar som innehåller ett Cache-Control-huvud:

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}

I det här exemplet anger Cache-Control-huvudet att data som returneras ska upphöra att gälla efter 600 sekunder och bara är lämplig för en enda klient och inte får lagras i en delad cache som används av andra klienter (de är private (privata)). Cache-Control-huvudet kan ange att data är public (offentliga) i stället för private (privata) och då kan data lagras i en delad cache. Huvudet kan även ange no-store (lagra inte) och då får data inte cachelagras av klienten. I följande kodexempel visas hur du skapar ett Cache-Control-huvud i ett svarsmeddelande:

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;
    }
    ...
}

Den här koden använder en anpassad IHttpActionResult klass med namnet OkResultWithCaching. Den här klassen gör att kontrollenheten kan ange cachehuvudets innehåll:

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;
    }
}

Kommentar

HTTP-protokollet definierar också no-cache-direktivet för Cache-Control-huvudet. Något förvirrande är att det här direktivet inte betyder ”cachelagra inte”, utan i stället ”validera den cachelagrade informationen på nytt på servern innan den returneras”. Data kan fortfarande cachelagras men de kontrolleras varje gång de används för att se till att de fortfarande är aktuella.

Cachehanteringen är klientprogrammets eller den mellanliggande serverns ansvar men om den implementeras på rätt sätt kan det spara bandbredd och ge bättre prestanda genom att ta bort behovet av att hämta data som redan har hämtats nyligen.

Värdet för max-age (maxålder) i Cache-Control-huvudet är bara vägledande och inte en garanti för att motsvarande data inte ändras under den angivna tiden. Webb-API:et bör ange ett lämpligt max-age-värde, beroende på hur flyktiga data förväntas vara. När perioden har löpt ut bör klienten ta bort objektet från cachen.

Kommentar

De flesta moderna webbläsare stöder klientcachelagring genom att lägga till lämpliga Cache-Control-huvuden i begäranden och undersöka resultatens huvuden, enligt beskrivningen. Men vissa äldre webbläsare cachelagrar inte de värden som returneras från en URL som innehåller en frågesträng. Det är normalt inte ett problem för anpassade klientprogram som implementerar en egen cachehanteringsstrategi baserat på det protokoll som beskrivs här.

Vissa äldre proxyservrar har samma beteende och kanske inte cachelagrar begäranden som baseras på URL:er med frågesträngar. Det kan vara ett problem för anpassade klientprogram som ansluter till en webbserver med en sådan proxy.

Ange ETags för att optimera frågebearbetning

När ett klientprogram hämtar ett objekt kan svarsmeddelandet även inkludera en ETag (entitetstagg). En ETag är en ogenomskinlig sträng som anger versionen av en resurs. varje gång en resurs ändrar ETag ändras också. Denna ETag ska cachelagras av klientprogrammet som en del av data. I följande kodexempel visas hur du lägger till en ETag som en del av svaret på en HTTP GET-begäran. I den här koden används metoden GetHashCode för ett objekt för att generera ett numeriskt värde som identifierar objektet (du kan åsidosätta metoden om det behövs och skapa en egen hash med en algoritm, till exempel 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;
    }
    ...
}

Svarsmeddelandet som publiceras av webb-API:et ser ut så här:

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}

Dricks

Av säkerhetsskäl ska du inte tillåta att känsliga data eller data som returneras via en autentiserad (HTTPS) anslutning cachelagras.

Ett klientprogram kan utfärda en efterföljande GET-begäran för att hämta samma resurs när som helst, och om resursen har ändrats (den har en annan ETag) ska den cachelagrade versionen tas bort och den nya versionen läggas till i cachen. Om en resurs är stor och kräver mycket bandbredd för att skicka tillbaka till klienten kan upprepade begäranden för att hämta samma data bli ineffektiva. För att bekämpa detta definierar HTTP-protokollet följande process för att optimera GET-begäranden som du ska stödja i ett webb-API:

  • Klienten skapar en GET-begäran som innehåller ETag för den aktuella cachelagrade versionen av resursen som refereras i ett If-None-Match-HTTP-huvud:

    GET https://adventure-works.com/orders/2 HTTP/1.1
    If-None-Match: "2147483648"
    
  • GET-åtgärden i webb-API:et hämtar aktuell ETag för begärda data (order 2 i exemplet ovan) och jämför dem med värdet i If-None-Match-huvudet.

  • Om aktuell ETag för begärda data matchar ETag som anges i begäran har resursen inte ändrats och webb-API:et bör returnera ett HTTP-svar med tom meddelandetext och statuskod 304 (har inte ändrats).

  • Om aktuell ETag för begärda data inte matchar ETag som anges i begäran så har data ändrats och webb-API:et bör returnera ett HTTP-svar med nya data i meddelandetexten och statuskod 200 (OK).

  • Om begärda data inte längre finns bör webb-API:et returnera ett HTTP-svar med statuskod 404 (hittades inte).

  • Klienten använder statuskoden för att underhålla cachen. Om data inte har ändrats (statuskod 304) kan objektet fortsätta att cachelagras och klientprogrammet kan fortsätta att använda den här versionen av objektet. Om data har ändrats (statuskod 200) ska det cachelagrade objektet tas bort och det nya infogas. Om data inte längre är tillgängliga (statuskod 404) ska objektet tas bort från cachen.

Kommentar

Om svarshuvudet innehåller Cache-Control-huvudet ”no-store” ska objektet alltid tas bort från cachen oavsett HTTP-statuskod.

Följande kod visar metoden FindOrderByID utökad för att stödja rubriken If-None-Match. Obs! Om If-None-Match-huvudet utelämnas hämtas alltid den angivna ordern:

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();
        }
    }
...
}

I det här exemplet infogas en ytterligare anpassad IHttpActionResult-klass med namnet EmptyResultWithCaching. Den här klassen fungerar helt enkelt som en omslutning runt ett HttpResponseMessage-objekt som inte innehåller svarstext:

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;
    }
}

Dricks

I det här exemplet genereras ETag för data genom hashning av data som hämtats från den underliggande datakällan. Om en ETag kan beräknas på något annat sätt kan processen optimeras ytterligare och data behöver bara hämtas från datakällan om de har ändrats. Den här metoden är särskilt användbar om data är stora eller användning av datakällan kan medföra betydande fördröjningar (till exempel om datakällan är en fjärrdatabas).

Använda ETags för att stödja optimistisk samtidighet

För att möjliggöra uppdateringar via tidigare cachelagrade data stöder HTTP-protokollet en strategi med optimistisk samtidighet. Om klientprogrammet efter hämtning och cachelagring skickar en PUT- eller DELETE-begäran om att ändra eller ta bort resursen ska den innehålla ett If-Match-huvud som refererar till ETag. Webb-API:et kan sedan använda informationen för att avgöra om resursen redan har ändrats av en annan användare sedan den hämtades och skicka ett lämpligt svar tillbaka till klientprogrammet på följande sätt:

  • Klienten skapar en PUT-begäran som innehåller ny information för resursen och ETag för den aktuella cachelagrade versionen av resursen som refereras i ett If-Match HTTP-huvud. I följande exempel visas en PUT-begäran som uppdaterar en order:

    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
    
  • PUT-åtgärden i webb-API:et hämtar aktuell ETag för begärda data (order 1 i exemplet ovan) och jämför dem med värdet i If-Match-huvudet.

  • Om aktuell ETag för begärda data matchar ETag som anges i begäran har resursen inte ändrats och webb-API:et bör utföra uppdateringen, och returnerar ett meddelande med HTTP-statuskod: 204 (inget innehåll) om den lyckas. Svaret kan innehålla Cache-Control- och ETag-huvuden för den uppdaterade versionen av resursen. Svaret bör alltid innehålla Location-huvudet som refererar till URI:n för den uppdaterade resursen.

  • Om aktuell ETag för begärda data inte matchar ETag som anges i begäran har data ändrats av en annan användare sedan det hämtades och webb-API:et bör returnera ett HTTP-svar med tom meddelandetext och statuskod 412 (förhandsvillkoret misslyckades).

  • Om resursen som ska uppdateras inte längre finns bör webb-API:et returnera ett HTTP-svar med statuskod 404 (hittades inte).

  • Klienten använder statuskoden och svarshuvuden för att underhålla cachen. Om data har uppdaterats (statuskod 204) kan objektet bli kvar i cachen (förutsatt att Cache-Control-huvudet inte anger ”no-store”) men ETag ska uppdateras. Om data har ändrats av en annan användare (statuskod 412) eller inte hittades (statuskod 404) bör det cachelagrade objektet tas bort.

I nästa kodexempel visas en implementering av PUT-åtgärden för kontrollenheten Orders (order):

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();
        }
    }
    ...
}

Dricks

Användning av If-Match-huvudet är helt valfritt. Om det utelämnas försöker webb-API:et alltid uppdatera den angivna ordern, och kanske blint skriver över en uppdatering som gjorts av en annan användare. Om du vill undvika problem på grund av förlorade uppdateringar ska du alltid ange ett If-Match-huvud.

Hantering av stora begäranden och svar

Det kan finnas tillfällen då ett klientprogram behöver utfärda begäranden som skickar eller tar emot data som kan vara flera megabyte (eller större) i storlek. Att vänta medan den här mängden data överförs kan orsaka att klientprogrammet inte svarar. Tänk på följande när du måste hantera begäranden som innehåller stora mängder data:

Optimera begäranden och svar som rör stora objekt

Vissa resurser kan vara stora objekt eller innehålla stora fält, till exempel grafikbilder eller andra typer av binära data. Ett webb-API ska ha stöd för strömning för att möjliggöra optimerad upp- och nedladdning av dessa resurser.

Med HTTP-protokollet får du en överföringsmekanism med chunked-kodning för att strömma stora dataobjekt tillbaka till en klient. När klienten skickar en HTTP GET-begäran för ett stort objekt kan webb-API:et skicka tillbaka svaret bit för bit i chunks (segment) via en HTTP-anslutning. Längden på data i svaret kanske inte är känd från början (det kan genereras), så servern som är värd för webb-API:et bör skicka ett svarsmeddelande Transfer-Encoding: Chunked med varje segment som anger rubriken i stället för ett innehållslängdshuvud. Klientprogrammet kan i sin tur ta emot varje segment för att bygga upp det fullständiga svaret. Dataöverföringen slutförs när servern skickar tillbaka ett sista segment med storleken noll.

En enstaka begäran kan möjligen resultera i ett enormt objekt som förbrukar omfattande resurser. Om webb-API:et under strömningsprocessen fastställer att mängden data i en begäran har överskridit vissa godkända gränser kan den avbryta åtgärden och returnera ett svarsmeddelande med statuskoden 413 (Begärandeentiteten är för stor).

Du kan minska storleken på stora objekt som överförs via nätverket med hjälp av HTTP-komprimering. Den här metoden hjälper till att minska nätverkstrafiken och tillhörande nätverksfördröjningar, men på bekostnad av att det krävs ytterligare bearbetning på klienten och servern som är värd för webb-API:et. Ett klientprogram som förväntar sig att ta emot komprimerade data kan till exempel innehålla ett Accept-Encoding: gzip begärandehuvud (andra algoritmer för datakomprimering kan också anges). Om servern stöder komprimering bör den svara med innehållet i gzip-format i meddelandetexten och svarshuvudet Content-Encoding: gzip .

Du kan kombinera kodad komprimering med strömning. Komprimera data först innan du strömmar dem och ange kodning för gzip-innehåll och segmentvis överföring (chunked) i meddelandehuvudena. Tänk också på att vissa webbservrar (till exempel Internet Information Server) kan konfigureras att automatiskt komprimera HTTP-svar oavsett om data komprimeras i webb-API:et.

Implementera partiella svar för klienter som inte stöder asynkrona åtgärder

Som ett alternativ till asynkron strömning kan ett klientprogram uttryckligen begära data för stora objekt i segment, vilket kallas partiella svar. Klientprogrammet skickar en HTTP HEAD-begäran för att hämta information om objektet. Om webb-API:et stöder partiella svar bör det svara på HEAD-begäran med ett svarsmeddelande som innehåller ett Accept-Ranges huvud och ett Content-Length huvud som anger objektets totala storlek, men meddelandets brödtext bör vara tom. Klientprogrammet kan använda denna information för att konstruera ett antal GET-begäranden som anger ett antal byte som ska ta emot. Webb-API:et bör returnera ett svarsmeddelande med HTTP-status 206 (partiellt innehåll), ett innehållslängdshuvud som anger den faktiska mängden data som ingår i brödtexten i svarsmeddelandet och ett Content-Range-huvud som anger vilken del (till exempel byte 4000 till 8000) av objektet som dessa data representerar.

HTTP HEAD-begäranden och partiella svar beskrivs mer detaljerat i API-design.

Undvika att skicka onödiga meddelanden med 100-Continue-status (fortsätt) i klientprogram

Ett klientprogram som håller på att skicka en stor mängd data till en server kan först bestämma om servern faktiskt är beredd att acceptera begäran. Innan klientprogrammet skickar data kan det skicka en HTTP-begäran med ett förväntat Expect: 100-Continue-huvud och ett Content-Length-huvud som anger storleken på data men tom meddelandetext. Om servern är villig att hantera begäran bör den svara med ett meddelande som anger HTTP-status 100 (fortsätt). Klientprogrammet kan sedan gå vidare och skicka den fullständiga begäran, med data i meddelandetexten.

Om du är värd för en tjänst med hjälp av IIS identifierar och hanterar drivrutinen HTTP.sys automatiskt Expect: 100-Continue-huvuden innan begäranden skickas till webbprogrammet. Det innebär att du troligen inte ser dessa huvuden i din programkod och kan du utgå ifrån att IIS redan har filtrerat alla meddelanden som anses vara olämpliga eller för stora.

Om du skapar klientprogram med hjälp av .NET Framework skickar alla POST- och PUT-meddelanden först meddelanden med Expect: 100-Continue-huvuden som standard. Precis som på serversidan hanteras processen transparent av .NET Framework. Men den här processen resulterar i att varje POST- och PUT-begäran orsakar två turer till servern, även för små begäranden. Om programmet inte skickar begäranden med stora mängder data kan du inaktivera funktionen med hjälp av klassen ServicePointManager för att skapa ServicePoint-objekt i klientprogrammet. Ett ServicePoint objekt hanterar anslutningar som klienten skickar till en server baserat på det schema och de värdefragment för URI:er som identifierar resurser på servern. Du kan sedan ange ”false” för egenskapen Expect100Continue för ServicePoint-objektet. Alla efterföljande POST- och PUT-begäranden som görs av klienten via en URI som matchar schemat och värdefragmenten för ServicePoint-objektet kommer att skickas utan Expect: 100-Continue-huvuden. I följande kod visas hur du konfigurerar ett ServicePoint-objekt som konfigurerar alla begäranden som skickas till URI:er med schemat http och värden www.contoso.com.

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

Du kan också ange den statiska Expect100Continue egenskapen för ServicePointManager klassen för att ange standardvärdet för den här egenskapen för alla servicePoint-objekt som skapats senare.

Stöd för sidbrytning för begäranden som kan returnera stora mängder objekt

Om en samling innehåller ett stort antal resurser kan att utfärda en GET-begäran till motsvarande URI medföra betydande bearbetning på servern som är värd för webb-API:et som påverkar prestanda och generera mycket nätverkstrafik, vilket resulterar i ökade fördröjningar.

För att hantera dessa fall ska webb-API:et ha stöd frågesträngar som gör att klientprogrammet kan förfina begäranden eller hämta data i mer hanterbara, diskreta block (eller sidor). Följande kod visar GetAllOrders metoden i kontrollanten Orders . Den här metoden hämtar information om order. Om den här metoden är obegränsad kan den möjligen returnera en stor mängd data. Parametrarna limit och offset är tänkta att minska mängden data till en mindre deluppsättning, i det här fallet som standard bara de första 10 orderna:

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;
    }
    ...
}

Ett klientprogram kan skicka en begäran om att hämta 30 order med början vid förskjutningen 50 med hjälp av URI:n https://www.adventure-works.com/api/orders?limit=30&offset=50.

Dricks

Undvik att aktivera klientprogram för att ange frågesträngar som resulterar i en URI som är över 2 000 tecken lång. Många webbklienter och servrar kan inte hantera URI:er som är så här långa.

Upprätthålla svarstider, skalbarhet och tillgänglighet

Samma webb-API kan användas av många klientprogram som körs var som helst i världen. Det är viktigt att se till att webb-API:et implementeras för att upprätthålla svarstiderna vid hård belastning, upprätthålla skalbarheten för att stödja en mycket varierande arbetsbelastning och garantera tillgängligheten för klienter som utför verksamhetskritiska åtgärder. Tänk på följande när du bestämmer hur du ska uppfylla dessa krav:

Ge synkront stöd för tidskrävande begäranden

En begäran som kan ta lång tid att bearbeta ska utföras utan att blockera den klient som skickade begäran. Webb-API:et kan utföra vissa inledande kontroller för att verifiera begäran, initiera en separat åtgärd för att utföra arbetet och sedan returnera ett svarsmeddelande med HTTP-kod 202 (accepterad). Uppgiften kan köras asynkront som en del av webb-API-bearbetningen eller så kan den avlastas till en bakgrundsaktivitet.

Webb-API:et ska också ge en mekanism för att returnera resultatet av bearbetningen till klientprogrammet. Du kan åstadkomma detta genom att ge en avsökningsmekanism för klientprogram för att regelbundet fråga om bearbetningen har slutförts och hämta resultatet eller göra så att webb-API:et kan skicka ett meddelande när åtgärden har slutförts.

Du kan implementera en enkel avsökningsmekanism genom att ge en URI för avsökning som fungerar som en virtuell resurs genom att använda följande metod:

  1. Klientprogrammet skickar den första begäran till webb-API:et.
  2. Webb-API:et lagrar information om begäran i en tabell som finns i Azure Table Storage eller Microsoft Azure Cache och genererar en unik nyckel för den här posten, eventuellt i form av ett GUID. Ett meddelande som innehåller information om begäran och den unika nyckeln kan också skickas via Azure Service Bus .
  3. Webb-API:et initierar bearbetningen som en separat uppgift eller med ett bibliotek som Hangfire. Webb-API:et registrerar status för uppgiften i tabellen som Körs.
    • Om du använder Azure Service Bus utförs meddelandebearbetningen separat från API:et, eventuellt med hjälp av Azure Functions eller AKS.
  4. Webb-API:et returnerar ett svarsmeddelande med HTTP-statuskod 202 (accepterad) och en URI som innehåller den unika nyckel som genereras – ungefär som /polling/{guid}.
  5. När aktiviteten har slutförts lagrar webb-API:et resultatet i tabellen och anger statusen för aktiviteten till Slutförd. Obs! Om uppgiften misslyckas kan webb-API:et också lagra information om felet och ange Misslyckades som status.
  6. När uppgiften körs kan klienten fortsätta utföra sin egen bearbetning. Den kan regelbundet skicka en begäran till den URI som den tog emot tidigare.
  7. Webb-API:et vid URI:n frågar efter status för motsvarande uppgift i tabellen och returnerar ett svarsmeddelande med HTTP-statuskod 200 (OK) som innehåller det här tillståndet (Körs, Slutfört eller Misslyckades). Om uppgiften har slutförts eller misslyckats kan svarsmeddelandet även inkludera resultatet av bearbetningen eller all tillgänglig information om orsaken till felet.
    • Om den långvariga processen har mer mellanliggande tillstånd är det bättre att använda ett bibliotek som stöder saga-mönstret, till exempel NServiceBus eller MassTransit.

Alternativ för att implementera aviseringar är till exempel:

  • Använda en meddelandehubb för att skicka asynkrona svar till klientprogram. Mer information finns i Skicka meddelanden till specifika användare med hjälp av Azure Notification Hubs.
  • Använda av Comet-modellen för att behålla en beständig nätverksanslutning mellan klienten och servern som är värd för webb-API:et och använda anslutningen för att skicka meddelanden från servern tillbaka till klienten. I MSDN-artikeln Building a Simple Comet Application in the Microsoft .NET Framework (Skapa ett enkelt Comet-program i Microsoft .NET Framework) beskrivs ett exempel på lösning.
  • Använda SignalR för att skicka data i realtid från webbservern till klienten via en beständig nätverksanslutning. SignalR finns för ASP.NET-webbprogram som NuGet-paket. Mer information finns på ASP.NET SignalR-webbplatsen.

Se till att varje begäran tillståndslös

Varje begäran bör ses som atomisk. Det ska inte finnas några beroenden mellan en begäran som görs av ett klientprogram och alla efterföljande begäranden som skickas av samma klient. Den här metoden hjälper skalbarheten. Instanser av webbtjänsten kan distribueras på flera servrar. Klientbegäranden kan dirigeras på vilken som helst av dessa instanser och resultatet bör alltid vara densamma. Även tillgängligheten förbättras av en liknande anledning. Om en webbserver misslyckas kan begäranden dirigeras till en annan instans (med hjälp av Azure Traffic Manager) när servern startas om utan negativa effekter på klientprogram.

Spåra klienter och implementera begränsning för att minska risken för DoS-attacker

Om en viss klient gör ett stort antal begäranden inom en viss tidsperiod kan den monopolisera tjänsten och påverka andra klienters prestanda. Om du vill undvika det här problemet kan ett webb-API övervaka anrop från klientprogram genom spårning av IP-adresser för alla inkommande begäranden eller genom att logga varje autentiserad åtkomst. Du kan använda den här informationen för att begränsa åtkomst till resurser. Om en klient överskrider en definierad gräns kan webb-API:et returnera ett svarsmeddelande med status 503 (tjänsten är inte tillgänglig) och inkludera ett Retry-After-huvud som anger när klienten kan skicka nästa begäran utan att den nekas. Den här strategin kan bidra till att minska risken för en DoS-attack (Denial Of Service) från en uppsättning klienter som stoppar systemet.

Hantera beständiga HTTP-anslutningar försiktigt

HTTP-protokollet stöder beständiga HTTP-anslutningar när de är tillgängliga. HTTP 1.0-specifikationen lade till huvudet Anslut ion:Keep-Alive som gör det möjligt för ett klientprogram att ange för servern att det kan använda samma anslutning för att skicka efterföljande begäranden i stället för att öppna nya. Anslutningen stängs automatiskt om klienten inte återanvänder anslutningen inom en tid som anges av värden. Det här beteendet är standard i HTTP 1.1 som används av Azure-tjänster, så du behöver inte inkludera Keep-Alive-huvuden i meddelanden.

Att hålla en anslutning öppen kan bidra till att förbättra svarstiden genom att minska fördröjningar och överbelastning på nätverket men det kan vara skadligt för skalbarheten om onödiga anslutningar hålls öppna under längre tid än vad som krävs, vilket begränsar möjligheten för andra samtidiga klienter att ansluta. Det kan även påverka batterilivslängden om klientprogrammet körs på en mobil enhet. Om programmet bara gör begäranden till servern ibland kan en öppen anslutning tömma batteriet snabbare. För att se till att en anslutning inte görs beständig med HTTP 1.1 kan klienten inkludera ett Connection:Close-huvud med meddelanden om att åsidosätta standardbeteendet. Och om en server hanterar ett mycket stort antal klienter kan den inkludera ett Connection:Close-huvud i svarsmeddelanden som bör stänga anslutningen och spara serverresurser.

Kommentar

Beständiga HTTP-anslutningar är en helt valfri funktion för att minska nätverksresurser som är associerade med att flera gånger upprätta en kommunikationskanal. Varken webb-API:et eller klientprogrammet bör vara beroende av att en beständig HTTP-anslutning är tillgänglig. Använd inte beständiga HTTP-anslutningar för att implementera meddelanden i kometformat. i stället bör du använda socketar (eller webbsocketer om det är tillgängligt) på TCP-lagret. Observera slutligen att Keep-Alive-huvuden har begränsad användbarhet om ett klientprogram kommunicerar med en server via en prox. Det är bara anslutningen med klienten och proxyn som är beständiga.

Publicera och hantera ett webb-API

Om du vill göra ett webb-API tillgängligt för klientprogram måste webb-API:et distribueras till en värdmiljö. Den här miljön är normalt en webbserver men det kan finnas någon annan typ av värdprocess. Tänk på följande när du publicerar ett webb-API:

  • Alla begäranden måste autentiseras och auktoriseras, och en lämplig nivå av åtkomstkontroll måste tillämpas.
  • Ett kommersiellt webb-API kan omfattas av garantier av olika kvalitet när det gäller svarstider. Det är viktigt att se till att värdmiljön är skalbar om belastningen kan variera avsevärt över tid.
  • Det kan vara nödvändigt att mäta begäranden i intäktsskapande syfte.
  • Det kan vara nödvändigt att reglera trafikflödet till webb-API:et och implementera begränsningar för vissa klienter som har förbrukat sina kvoter.
  • På grund av regelkrav kan det bli aktuellt med loggning och granskning av alla begäranden och svar.
  • För att säkerställa tillgängligheten kan det vara nödvändigt att övervaka hälsotillståndet för servern som är värd för webb-API:et och starta om den vid behov.

Det är användbart att kunna frikoppla dessa problem från de tekniska problem som rör implementeringen av webb-API:et. Därför bör du överväga att skapa en fasad, som körs som en separat process och som skickar begäranden till webb-API:et. Fasaden kan ange hanteringsåtgärderna och vidarebefordra verifierade begäranden till webb-API:et. Att använda en fasad kan också innebära många funktionella fördelar, till exempel:

  • Fungera som en integrationspunkt för flera webb-API:er.
  • Omvandla meddelanden och översätta kommunikationsprotokoll för klienter som skapats med olika tekniker.
  • Cachelagring av begäranden och svar för att minska belastningen på servern som är värd för webb-API:et.

Testa ett webb-API

Ett webb-API bör testas lika noggrant som andra typer av programvara. Du bör överväga att skapa enhetstester för att verifiera funktionen.

Webb-API:ets karaktär medför egna ytterligare krav för att verifiera att det fungerar korrekt. Du bör ta särskild hänsyn till följande aspekter:

  • Testa alla vägar för att kontrollera att de anropar rätt åtgärder. Var särskilt uppmärksam på om HTTP-statuskod 405 (metoden tillåts inte) returneras oväntat, eftersom det kan tyda på ett matchningsfel mellan en väg och HTTP-metoderna (GET, POST, PUT, DELETE) som kan skickas till den vägen.

    Skicka HTTP-begäranden till vägar som inte stöder dem, till exempel skicka en POST-begäran till en specifik resurs (POST-begäranden bör endast skickas till resurssamlingar). I dessa fall ska det enda giltiga svaret vara statuskod 405 (tillåts inte).

  • Kontrollera att alla vägar är ordentligt skyddade och omfattas av lämpliga kontroller för autentisering och auktorisering.

    Kommentar

    Vissa säkerhetsaspekter, till exempel användarautentisering, är sannolikt värdmiljöns ansvar och inte webb-API:ets. Men det är ändå nödvändigt att inkludera säkerhetstester som en del av distributionsprocessen.

  • Testa undantagshanteringen som utförs av varje åtgärd och kontrollera att ett lämpligt och beskrivande HTTP-svar skickas tillbaka till klientprogrammet.

  • Kontrollera att meddelanden för begäran och svar har rätt format. Om till exempel en HTTP POST-begäran innehåller data för en ny resurs i formatet x-www-form-urlencoded ska du bekräfta att motsvarande åtgärd gör följande på rätt sätt: parsar data, skapar resurserna och returnerar ett svar som innehåller information om den nya resursen, inklusive rätt Location-header.

  • Kontrollera alla länkar och URI:er i svarsmeddelanden. Ett HTTP POST-meddelande bör till exempel returnera URI:n för den nyligen skapade resursen. Alla HATEOAS-länkar ska vara giltiga.

  • Se till att varje åtgärd returnerar rätt statuskoder för olika kombinationer av indata. Till exempel:

    • Om en fråga lyckas ska den returnera statuskod 200 (OK)
    • Om en resurs inte hittas ska åtgärden returnera HTTP-statuskod 404 (hittades inte).
    • Om klienten skickar en begäran som tar bort en resurs ska statuskoden vara 204 (inget innehåll).
    • Om klienten skickar en begäran som skapar en ny resurs ska statuskoden vara 201 (Skapad).

Se upp för oväntade svarsstatuskoder i 5xx-intervallet. Dessa meddelanden rapporteras normalt av värdservern för att ange att det inte gick att utföra en giltig begäran.

  • Testa de olika kombinationerna av begäranhuvuden som ett klientprogram kan ange och kontrollera att ditt webb-API returnerar den förväntade informationen i svarsmeddelanden.

  • Testa frågesträngar. Om en åtgärd kan ta valfria parametrar (t.ex sidbrytningsbegäranden) testar du de olika kombinationerna och ordningen på parametrar.

  • Kontrollera att asynkrona åtgärder slutförs. Om webb-API:et stöder strömning för begäranden som returnerar stora binära objekt (som video eller ljud) kontrollerar du att klientbegäranden inte blockeras när data strömmas. Om webb-API:et implementerar avsökning för tidskrävande datamodifieringsåtgärder kontrollerar du att åtgärderna rapporterar sin status korrekt när de fortsätter.

Du bör också skapa och köra prestandatester för att kontrollera att webb-API:et fungerar tillfredsställande under tvång. Du kan skapa ett projekt för webbprestanda och belastning med hjälp av Visual Studio Ultimate.

Använda Azure API Management

I Azure kan du överväga att använda Azure API Management för att publicera och hantera ett webb-API. Med den här funktionen kan skapa du en tjänst som fungerar som en fasad för ett eller flera webb-API:er. Tjänsten är i sig en skalbar webbtjänst som du kan skapa och konfigurera med hjälp av Azure-portalen. Du kan använda den här tjänsten till att publicera och hantera ett webb-API på följande sätt:

  1. Distribuera webb-API:et till en webbplats, Azure-molntjänst eller virtuell Azure-dator.

  2. Anslut API Management-tjänsten till webb-API:et. Begäranden som skickas till URL:en för hanterings-API:et mappas till URI:er i webb-API:et. Samma API Management-tjänst kan dirigera begäranden till fler än ett webb-API. På så sätt kan du samla flera webb-API:er i en enda hanteringstjänst. På liknande sätt kan samma webb-API refereras från fler än en API Management-tjänst om du behöver att begränsa eller partitionera funktionerna som är tillgängliga för olika program.

    Kommentar

    URI:erna i HATEOAS-länkarna som genereras som en del av svaret för HTTP GET-begäranden bör referera till URL:en för API-hanteringstjänsten och inte webbservern som är värd för webb-API:et.

  3. För varje webb-API anger du de HTTP-åtgärder som webb-API:et visar tillsammans med parametrar som en åtgärd kan ta som indata. Du kan också konfigurera om API Management-tjänsten ska cachelagra svaret från webb-API:et för att optimera upprepade begäranden för samma data. Registrera informationen för HTTP-svaren som varje åtgärd kan generera. Den här informationen används för att skapa dokumentation för utvecklare, så det är viktigt att den är korrekt och fullständig.

    Du kan antingen definiera åtgärder manuellt med hjälp av de guider som tillhandahålls av Azure-portalen, eller så kan du importera dem från en fil som innehåller definitionerna i WADL- eller Swagger-format.

  4. Konfigurera säkerhetsinställningarna för kommunikation mellan API Management-tjänsten och webbservern som är värd för webb-API:et. API Management-tjänsten stöder för närvarande grundläggande autentisering och ömsesidig autentisering med certifikat och OAuth 2.0-användarautentisering.

  5. Skapa en produkt. En produkt är enheten som publiceras. Du lägger till webb-API:er som du har anslutit till hanteringstjänsten till produkten. När produkten publiceras blir webb-API:erna tillgängliga för utvecklare.

    Kommentar

    Innan du publicerar ett projekt kan du också definiera användargrupper som kan komma åt produkten och lägga till användare i dessa grupper. Det ger dig kontroll över utvecklare och program som kan använda webb-API:et. Om ett webb-API ska godkännas måste en utvecklare skicka en begäran till produktadministratören innan utvecklaren får åtkomst. Administratören kan bevilja eller neka utvecklaren åtkomst. Befintliga utvecklare kan också blockeras om omständigheterna ändras.

  6. Konfigurera principer för varje webb-API. Principer styr aspekter, till exempel om anrop mellan domäner ska tillåtas, hur klienter autentiseras, om konvertering ska ske transparent mellan XML- och JSON-dataformat, om anrop ska begränsas från ett visst IP-adressintervall, användningskvoter samt om anropshastigheten ska begränsas. Principer kan tillämpas globalt i hela produkten, för ett enda webb-API i en produkt eller för enskilda åtgärder i ett webb-API.

Mer information finns i API Management-dokumentationen.

Dricks

Azure tillhandahåller Azure Traffic Manager, där du kan implementera redundans och belastningsutjämning och minska svarstiden i flera instanser av en webbplats som finns på olika geografiska platser. Du kan använda Azure Traffic Manager tillsammans med API Management-tjänsten. API Management-tjänsten kan dirigera begäranden till instanser av en webbplats via Azure Traffic Manager. Mer information finns i Traffic Manager-routningsmetoder.

Om du använder anpassade DNS-namn för dina webbplatser i den här strukturen bör du konfigurera lämplig CNAME-post för varje webbplats så att den pekar på DNS-namnet på Azure Traffic Manager-webbplatsen.

Stöd för utvecklare på klientsidan

Utvecklare som skapar klientprogram kräver vanligtvis information om hur de kommer åt webb-API:et och dokumentation om parametrar, datatyper, returtyper och returkoder som beskriver olika begäranden och svar mellan webbtjänsten och klientprogrammet.

Dokumentera REST-åtgärder för ett webb-API

Azure API Management-tjänsten innehåller en utvecklarportal som beskriver REST-åtgärderna som visas av ett webb-API. När en produkt har publicerats visas den i den här portalen. Utvecklare kan använda portalen för att registrera sig för åtkomst. Administratören kan sedan godkänna eller neka begäran. Om utvecklaren godkänns tilldelas en prenumerationsnyckel som används för att autentisera anrop från klientprogrammen som utvecklaren skapar. Den här nyckeln måste anges med varje webb-API-anrop, annars avvisas anropet.

Portalen innehåller också:

  • Produktens dokumentation, med de åtgärder som det visar, de parametrar som krävs och de olika svar som kan returneras. Obs! Den här informationen genereras från detaljerna i steg 3 i listan, i avsnittet Publicera ett webb-API med hjälp av Microsoft Azure API Management-tjänsten.
  • Kodavsnitt som visar hur du anropar åtgärder på flera språk, inklusive JavaScript, C#, Java, Ruby, Python och PHP.
  • En utvecklarkonsol som gör att utvecklare kan skicka en HTTP-begäran för att testa varje åtgärd i produkten och visa resultatet.
  • En sida där utvecklaren kan rapportera problem som hittas.

Med Azure-portalen kan du anpassa utvecklarportalen så att den ändrar format och layout så att den matchar organisationens varumärkesanpassning.

Implementera klient-SDK

Att skapa ett klientprogram som anropar REST-begäranden för att komma åt ett webb-API kräver att en betydande mängd kod skrivs för att skapa varje begäran och formatera den på rätt sätt, skicka begäran till den server där webbtjänsten finns och parsa svaret för att ta reda på om begäran har lyckats eller misslyckats och extrahera returnerade data. Du kan isolera klientprogrammet mot dessa problem genom att ange en SDK som omsluter REST-gränssnittet och skiljer av dessa detaljer på låg nivå inuti en mer funktionell uppsättning metoder. Ett klientprogram använder dessa metoder, som transparent konverterar anrop till REST-begäranden och sedan konverterar svar tillbaka till metodreturvärden. Det här är en vanlig metod som implementeras av många tjänster, bland annat Azure SDK.

Att skapa en klient-SDK är en betydande uppgift eftersom den måste implementeras konsekvent och testas noggrant. Men en stor del av den här processen kan göras mekanisk och många leverantörer tillhandahåller verktyg som kan automatisera många av uppgifterna.

Övervaka ett webb-API

Beroende på hur du har publicerat och distribuerat ditt webb-API kan du övervaka webb-API:et direkt, eller så kan du samla in information om användning och hälsotillstånd genom att analysera den trafik som passerar genom API Management-tjänsten.

Övervaka ett webb-API direkt

Om du har implementerat ditt webb-API med hjälp av webb-API-mallen för ASP.NET (som ett webb-API-projekt eller som en webbroll i en Azure-molntjänst) och Visual Studio 2013 kan du samla in data om tillgänglighet, prestanda och användning med hjälp av ASP.NET Application Insights. Application Insights är ett paket som transparent spårar och registrerar information om begäranden och svar när webb-API:et har distribuerats till molnet. När paketet har installerats och konfigurerats behöver du inte ändra kod i ditt webb-API för att kunna använda det. När du distribuerar webb-API:et till en Azure-webbplats undersöks all trafik och följande statistik samlas in:

  • Serversvarstid.
  • Antal serverbegäranden och information om varje begäran.
  • Långsammaste begäranden när det gäller genomsnittlig svarstid.
  • Information om misslyckade begäranden.
  • Antalet sessioner som startats av olika webbläsare och användaragenter.
  • Sidorna med flest visningar (främst användbart för webbprogram i stället för webb-API:er).
  • De olika användarrollerna som får åtkomst till webb-API:et.

Du kan visa dessa data i realtid i Azure-portalen. Du kan också skapa webbtester som övervakar hälsotillståndet för webb-API:et. Ett webbtest skickar en regelbunden begäran till en angiven URI i webb-API:et och samlar in svaret. Du kan ange definitionen för ett lyckat svar (t.ex. HTTP-statuskod 200), och om begäran inte returnerar det svaret kan du ordna en avisering som skickas till en administratör. Om det behövs kan administratören starta om servern som är värd för webb-API:et om det har misslyckats.

Mer information finns i Application Insights – Kom igång med ASP.NET.

Övervaka ett webb-API via API Management-tjänsten

Om du har publicerat webb-API:et med hjälp av API Management-tjänsten innehåller sidan API Management på Azure-portalen en instrumentpanel som gör att du kan visa tjänstens övergripande prestanda. På Analytics-sidan kan du få detaljerad information om hur produkten används. Sidan har följande flikar:

  • Användning. Den här fliken innehåller information om antalet gjorda API-anrop och den bandbredd som används för att hantera dessa anrop över tid. Du kan filtrera användningsinformationen efter produkt, API och åtgärd.
  • Hälsotillstånd. På den här fliken kan du visa resultatet av API-begäranden (returnerade HTTP-statuskoder), cachelagringsprincipens verkan, API-svarstiden och tjänstens svarstid. Återigen kan du filtrera hälsotillståndsdata efter produkt, API och åtgärd.
  • Aktivitet. Den här fliken innehåller en textsammanfattning av antal lyckade anrop, misslyckade anrop, blockerade anrop, genomsnittlig svarstid och svarstider för varje produkt, webb-API och åtgärd. På sidan visas även antalet anrop gjorda av varje utvecklare.
  • Snabbtitt. På den här fliken visas en sammanfattning av prestandadata, inklusive vilka utvecklare som har gjort flest API-anrop och produkter, webb-API:er och åtgärder som har tagit emot dessa anrop.

Du kan använda den här informationen för att avgöra om ett visst webb-API eller en viss åtgärd orsakar en flaskhals och om det är nödvändigt skala värdmiljön och lägga till fler servrar. Du kan också fastställa om ett eller flera program använder en oproportionerlig mängd resurser och tillämpa rätt principer för att ange kvoter och begränsa anropshastigheter.

Kommentar

Du kan ändra informationen för en publicerad produkt och ändringarna börjar gälla direkt. Du kan till exempel lägga till eller ta bort en åtgärd från ett webb-API utan att behöva publicera om den produkt som innehåller webb-API:et.

Nästa steg