Dela via


RESTful-webb-API-design

De flesta moderna webbprogram tillgängliggör API:er som klienter kan använda för att interagera med programmet. Ett väl utformat webb-API bör syfta till att stödja:

  • Plattformsoberoende. Alla klienter ska kunna anropa API:n, oavsett hur API:n implementeras internt. Detta kräver användning av standardprotokoll och att det finns en mekanism där klienten och webbtjänsten kan komma överens om formatet för data som ska utbytas.

  • Tjänsteutveckling. Webb-API:n bör kunna utvecklas och lägga till funktioner fristående från klientprogram. I takt med att API:n utvecklas ska befintliga klientprogram fortsätta att fungera utan ändringar. Alla funktioner bör kunna identifieras så att klientprogram kan använda den fullt ut.

I den här vägledningen beskrivs problem som du bör tänka på när du skapar ett webb-API.

Vad är REST?

År 2000 föreslog Roy Fielding att REST (Representational State Transfer) skulle användas som en arkitekturmetod för att skapa webbtjänster. REST är ett arkitekturformat för att skapa distribuerade system baserade på hypermedia. REST är oberoende av eventuella underliggande protokoll och är inte nödvändigtvis kopplat till HTTP. De vanligaste REST API-implementeringarna använder dock HTTP som programprotokoll, och den här guiden fokuserar på att utforma REST-API:er för HTTP.

En viktig fördel med REST jämfört med HTTP är att den använder öppna standarder och inte binder implementeringen av API:et eller klientprogrammen till någon specifik implementering. Till exempel kan en REST-webbtjänst skrivas i ASP.NET och klientprogram kan använda alla språk eller verktyg som kan generera HTTP-begäranden och parsa HTTP-svar.

Här följer några av de viktigaste principerna i RESTful-API:er med HTTP:

  • REST API:er är designade runt resurser, vilket är alla slags objekt, data eller tjänster som kan användas av klienten.

  • En resurs har en identifierare, vilket är en URI som unikt identifierar den här resursen. URI för en viss kund kan exempelvis vara:

    https://adventure-works.com/orders/1
    
  • Klienterna interagerar med en tjänst genom att utbyta återgivningar av resurserna. Många webb-API:er använder JSON som utbytesformat. En GET-begäran till den URI som anges ovan kan till exempel returnera följande svarstext:

    {"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}
    
  • REST API:er använder ett enhetligt gränssnitt, vilket hjälper till att frikoppla klienten och tjänsteimplementeringarna. För REST-API:er som bygger på HTTP omfattar det enhetliga gränssnittet att använda http-standardverb för att utföra åtgärder på resurser. De vanligaste åtgärderna är GET, POST, PUT, PATCH och DELETE.

  • REST API:er använder en modell för tillståndslös begäran. HTTP-begäranden bör vara oberoende och kan inträffa i valfri ordning, så det är inte möjligt att behålla tillfällig tillståndsinformation mellan begäranden. Den enda plats där informationen lagras är i själva resurserna och varje begäran måste vara en atomisk åtgärd. Den här begränsningen ger webbtjänsterna möjlighet att vara mycket skalbara, eftersom det inte finns behov av att behålla alla tillhörigheter mellan klienter och specifika servrar. En server kan hantera alla begäranden från alla klienter. Det finns dock andra faktorer som kan begränsa skalbarheten. Många webbtjänster skriver till exempel till ett serverdelsdatalager, vilket kan vara svårt att skala ut. Mer information om strategier för att skala ut ett datalager finns i Horisontell, lodrät och funktionell datapartitionering.

  • REST API:er styrs av hypermedialänkar som ingår i återgivningen. Nedan visas exempelvis en JSON-återgivning av en order. Det innehåller länkar för att hämta eller uppdatera den kund som är associerad med ordern.

    {
      "orderID":3,
      "productID":2,
      "quantity":4,
      "orderValue":16.60,
      "links": [
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" },
        {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" }
      ]
    }
    

År 2008 föreslog Leonard Richardson följande förfallomodell för webb-API:er:

  • Nivå 0: Definiera en URI så blir alla åtgärder POST-begäranden till denna URI.
  • Nivå 1: Skapa separata URI:er för enskilda resurser.
  • Nivå 2: Använd HTTP-metoder för att definiera åtgärder för resurser.
  • Nivå 3: Använd hypermedia (HATEOAS, beskrivs nedan).

Nivå 3 motsvarar en verkligt RESTful-API enligt Fieldings definition. I praktiken klassificeras många publicerade webb-API:er någonstans runt nivå 2.

Organisera API-designen kring resurser

Fokusera på affärsobjekt som webb-API:n gör tillgänglig. I ett e-handelssystem kanske de primära objekten till exempel vara kunder och order. Det går att skapa en order genom att skicka en HTTP POST-begäran som innehåller orderinformationen. HTTP-svaret anger om ordern har gjorts eller ej. När så är möjligt ska resurs-URI:er baseras på substantiv (resursen) och inte verb (åtgärderna på resursen).

https://adventure-works.com/orders // Good

https://adventure-works.com/create-order // Avoid

En resurs behöver inte baseras på ett enda fysiskt dataobjekt. Till exempel kan en orderresurs implementeras internt som flera tabeller i en relationsdatabas, men visas för klienten som en enda enhet. Undvik att skapa API:er som bara speglar den interna strukturen hos en databas. Syftet med REST är att modellera entiteter och de åtgärder som ett program kan utföra på dessa enheter. En klient ska inte göras tillgänglig för den interna implementeringen.

Entiteter grupperas ofta tillsammans i samlingar (order, kunder). En samling är en resurs som är separat från objektet i samlingen och ska ha sin egen URI. Följande URI kan till exempel representera insamling av order:

https://adventure-works.com/orders

Om en HTTP GET-begäran skickas till insamlings-URI:n hämtas en lista över objekt i samlingen. Varje objekt i samlingen har också en egen unik URI. En HTTP GET-begäran till URI:n för objektet returnerar information om det här objektet.

Anta en konsekvent namngivningskonvention i URI:er. I allmänhet är det till hjälp att använda pluralsubstantiv för URI:er som refererar till samlingar. Det är en bra idé att ordna URI:er för samlingar och objekt i en hierarki. Till exempel är /customers sökvägen till kundsamlingarna och /customers/5 är sökvägen till kunden med ID lika med 5. Den här metoden hjälper till att hålla ditt webb-API intuitivt. Dessutom kan många webb-API-ramverk vidarebefordra begäranden baserat på parametriserade URI-sökvägar, så att du kan definiera en väg för sökvägen /customers/{id}.

Fundera även på relationerna mellan olika typer av resurser och hur du kan tillgängliggöra dessa associationer. Till exempel kan /customers/5/orders representera alla order för kund 5. Du kan också gå i den andra riktningen och återge associationen från en order tillbaka till en kund med en URI som /orders/99/customer. Om den här modellen utökas för långt kan den dock bli svårhanterlig att implementera. En bättre lösning är att tillhandahålla navigerbara länkar till associerade resurser i texten till HTTP-svarsmeddelandet. Den här mekanismen beskrivs mer detaljerat i avsnittet Använd HATEOAS för att aktivera navigering till relaterade resurser.

För mer komplexa datorer kan det ligga nära till hands att ange URI:er som gör att en klient kan navigera genom flera nivåer av relationer, t.ex. /customers/1/orders/99/products. Den här nivån av komplexitet kan dock vara svår att underhålla och är oflexibel om relationerna mellan resurserna förändras i framtiden. Se i stället till att hålla URI:erna relativt enkla. När ett program har en referens till en resurs, bör det vara möjligt att använda den här referensen för att hitta objekt som är relaterade till resursen. Föregående fråga kan ersättas med URI:n /customers/1/orders för att hitta alla order för kund 1, och sedan /orders/99/products för att hitta produkterna i den här ordningen.

Dricks

Undvik att kräva resurs-URI:er som är mer komplexa än samling/objekt/samling.

En annan faktor är att alla webbegäranden inför en belastning på webbservern. Ju fler begäranden, desto större blir belastningen. Försök därför att undvika trafikintensiva webb-API:er som tillgängliggör ett stort antal små resurser. Ett sådant API kan kräva att ett klientprogram skickar flera begäranden för att hitta alla data som krävs. Du kanske i stället vill avnormalisera data och kombinera relaterad information i större resurser som kan hämtas med en enskild begäran. Du måste dock balansera den här metoden mot arbetet med att hämta data som klienten inte behöver. Att hämta stora objekt ökar svarstiden för en begäran och innebära ökade kostnader för bandbredd. Läs mer om dessa prestanda-antimönster i Trafikintensiv I/O och Överflödig hämtning.

Undvika att introducera beroenden mellan webb-API:n och underliggande datakällor. Om dina data exempelvis lagras i en relationsdatabas behöver webb-API:n inte tillgängliggöra varje tabell som en samling resurser. Det här är förmodligen i själva verket en dålig design. Se i stället webb-API:n som en abstraktion av databasen. Om det behövs lägger du till ett lager för mappning mellan databasen och webb-API. På så sätt kan klientprogrammen isoleras från förändringar av det underliggande databassystemet.

Slutligen kanske det inte går att mappa varje åtgärd som implementeras av ett webb-API till en viss resurs. Du kan hantera sådana icke-resurs-scenarier via HTTP-begäranden som anropar en funktion och returnerar resultatet som ett HTTP-svarsmeddelande. Ett webb-API som implementerar enkla beräkningsåtgärder som att lägga till och subtrahera kan till exempel ange URI:er som tillgängliggör dessa åtgärder som pseudoresurser och använder frågesträngen för att ange de parametrar som krävs. En GET-begäran till URI /add?operand1=99&operand2=1 returnerar till exempel ett svarsmeddelande med brödtexten som innehåller värdet 100. Dessa typer av URI:er ska dock endast användas sparsamt.

Definiera API-åtgärder när det gäller HTTP-metoder

HTTP-protokollet definierar ett antal metoder som tilldelar en begäran semantisk betydelse. Vanliga HTTP-metoderna som används av de flesta RESTful-webb-API:er är:

  • GET hämtar en återgivning av resursen från angiven URI. Innehållet i svarsmeddelandet innehåller information om den begärda resursen.
  • POST skapar en ny resurs på angiven URI. Innehållet i begärandemeddelandet innehåller information om den nya resursen. Observera att POST kan också användas till att utlösa åtgärder som inte konkret skapar resurser.
  • PUT skapar eller ersätter resursen på angiven URI. Innehållet i begärandemeddelandet anger den resurs som ska skapas eller uppdateras.
  • PATCH utför en deluppdatering av en resurs. Begärandetexten anger vilken uppsättning ändringar som ska tillämpas på resursen.
  • DELETE tar bort resursen på angiven URI.

Effekten av en specifik begäran bör beror på om resursen är en samling eller ett enskilt objekt. I följande tabell sammanfattas de vanliga konventioner som antagits av de flesta RESTful-implementeringar med hjälp av e-handelsexemplet. Alla dessa begäranden kan inte implementeras – det beror på det specifika scenariot.

Resurs POST GET PUT DELETE
/kunder Skapa en ny kund Hämta alla kunder Massuppdatering av kunder Ta bort alla kunder
/kunder/1 Fel Hämta informationen för kund 1 Uppdatera informationen om kund 1, om sådan finns Ta bort kund 1
/customers/1/orders Skapa en ny order för kund 1 Hämta alla order för kund 1 Massuppdatering av order för kund 1 Ta bort alla order för kund 1

Skillnaderna mellan POST, PUT och PATCH kan vara förvirrande.

  • En POST-begäran skapar en resurs. Servern tilldelar en URI för den nya resursen och returnerar denna URI till klienten. I REST-modellen använder du ofta POST-begäranden för samlingar. Den nya resursen har lagts till i samlingen. En POST-begäran kan också användas för att skicka data för bearbetning till en befintlig resurs utan att någon ny resurs skapas.

  • En PUT-begäran skapar en resurs eller uppdaterar en befintlig resurs. Klienten anger URI för resursen. Begärandetexten innehåller en fullständig återgivning av resursen. Om det finns redan en resurs med denna URI ersätts den. Annars skapas en ny resurs om servern har stöd för detta. PUT-begäranden används oftast för resurser som är enskilda objekt, till exempel en viss kund, i stället för samlingar. En server kan ha stöd för uppdateringar men inte att skapa via PUT. Om du vill stödja skapande via PUT beror på om klienten meningsfullt kan tilldelas en URI till en resurs innan den finns. Om inte, använder du sedan POST för att skapa resurser och PUT eller PATCH för att uppdatera.

  • En PATCH-begäran utför en deluppdatering av en befintlig resurs. Klienten anger URI för resursen. Begärandetexten anger att en uppsättning med ändringar ska gälla för resursen. Detta kan vara effektivare än att använda PUT, eftersom klienten enbart skickar ändringarna, inte hela representation av resursen. Tekniskt sett kan PATCH också skapa en ny resurs (genom att ange en uppsättning uppdateringar till en ”null”-resurs), om servern stöder detta.

PUT-begäranden måste vara idempotenta. Om en klient skickar samma PUT-begäran flera gånger, bör resultaten alltid vara desamma (samma resurs ändras med samma värden). POST- och PATCH-begäranden är inte garanterat idempotenta.

Överensstämmer med HTTP-semantik

I det här avsnittet beskrivs några vanliga överväganden vid utformning av ett API som stämmer överens med HTTP-specifikationen. Det täcker dock inte in varje enskild detalj eller möjligt scenario. Kontrollera HTTP-specifikationerna om det uppstår tveksamheter.

Mediatyper

Som tidigare nämns utbyter klienter och servrar återgivningar av resurser. Till exempel innehåller en POST-begäran en återgivning av den resurs som ska skapas i begärandetexten. I en GET-begäran innehåller svarstexten en återgivning av den hämtade resursen.

I HTTP-protokollet, anges format genom användning av medietyper, även kallat MIME-typer. För icke-binära data stöder de flesta webb-API:er JSON (medietyp = application/json) och eventuellt XML (medietyp = application/xml).

Content-Type-rubriken i en begäran eller ett svar anger formatet för återgivning. Här är ett exempel på en POST-begäran som innehåller JSON-data:

POST https://adventure-works.com/orders HTTP/1.1
Content-Type: application/json; charset=utf-8
Content-Length: 57

{"Id":1,"Name":"Gizmo","Category":"Widgets","Price":1.99}

Om servern inte har stöd för medietypen bör den returnera HTTP-statuskod 415 (medietypen stöds ej).

En klientbegäran kan innehålla ett Accept-huvud som innehåller en lista över medietyper som klienten ska ta emot från servern i svarsmeddelandet. Till exempel:

GET https://adventure-works.com/orders/2 HTTP/1.1
Accept: application/json

Om servern inte kan matcha någon av de medietyper som anges bör den returnera HTTP-statuskod 406 (inte acceptabel).

GET-metoder

En lyckad GET-metod returnerar vanligtvis HTTP-statuskod 200 (OK). Om inte det går att hitta resursen ska metoden returnera 404 (hittades ej).

Om begäran har uppfyllts men inget svarstext ingår i HTTP-svaret bör den returnera HTTP-statuskod 204 (inget innehåll). Till exempel kan en sökåtgärd som inte ger några matchningar implementeras med det här beteendet.

POST-metoder

Om en POST-metod skapar en ny resurs returnerar den HTTP-statuskod 201 (skapad). URI för den nya resursen ingår i svarets platsrubrik. Svarstexten innehåller en återgivning av resursen.

Om metoden utför viss bearbetning, men inte skapar en ny resurs, returnerar metoden HTTP-statuskod 200 och inkluderar resultatet av åtgärden i svarstexten. Om det inte finns några resultat som ska returneras kan metoden alternativt returnera HTTP-statuskod 204 (inget innehåll) utan någon svarstext.

Om klienten placerar ogiltiga data i begäran ska servern returnera HTTP-statuskod: 400 (felaktig begäran). Svarstexten kan innehålla ytterligare information om felet eller en länk till en URI som innehåller mer information.

PUT-metoder

Om en PUT-metod skapar en ny resurs, returnerar den HTTP-statuskod 201 (skapad), precis som med en POST-metod. Om metoden uppdaterar en befintlig resurs returnerar den antingen 200 (OK) eller 204 (inget innehåll). I vissa fall kanske det inte går att uppdatera en befintlig resurs. I så fall kan du överväga att returnera HTTP-statuskod 409 (konflikt).

Överväg att implementera HTTP PUT-åtgärder som kan batchsända uppdateringar till flera resurser i en samling. PUT-begäran bör ange URI för insamling och begärandetexten ska ange information om de resurser som ska ändras. Den här metoden kan bidra till att minska trafikintensiteten och förbättra prestanda.

PATCH-metoder

Med PATCH-begäran skickar klienten en uppsättning uppdateringar till en befintlig resurs i form av ett korrigeringsdokument. Servern bearbetar korrigeringsdokumentet för att utföra uppdateringen. Korrigeringsdokumentet beskriver inte hela resursen, utan endast en uppsättning ändringar som ska tillämpas. Specifikationen för PATCH-metoden (RFC 5789) definierar inte ett visst format för korrigeringsdokument. Formatet måste härledas från medietypen i begäran.

JSON är förmodligen vanligaste dataformatet för webb-API:er. Det finns två huvudsakliga JSON-baserade korrigeringsformat, JSON-korrigering och JSON-sammanslagningskorrigering.

JSON-sammanslagningskorrigeringen är lite enklare. Korrigeringsdokumentet har samma struktur som den ursprungliga JSON-resursen, men innehåller bara deluppsättningen av fält som ska ändras eller läggas till. Dessutom kan ett fält kan tas bort genom att null anges för fältvärdet i korrigeringsdokumentet. (Det innebär att en sammanslagningskorrigering inte lämpar sig om den ursprungliga resursen kan ha explicita null-värden.)

Anta exempelvis att den ursprungliga resursen har följande JSON-återgivning:

{
    "name":"gizmo",
    "category":"widgets",
    "color":"blue",
    "price":10
}

Här är en möjligt JSON-sammanslagningskorrigering för den här resursen:

{
    "price":12,
    "color":null,
    "size":"small"
}

Detta instruerar servern att uppdatera price, ta bort coloroch lägga till size, medan name och category inte ändras. Mer information om JSON-sammanslagningskorrigeringen finns i RFC 7396. Medietypen för JSON-sammanslagningskorrigeringen är application/merge-patch+json.

Sammanslagningskorrigering lämpar sig inte om den ursprungliga resursen kan innehålla explicita null-värden på grund av den särskilda innebörden av null i korrigeringsdokumentet. Korrigeringsdokumentet anger inte heller i vilken ordning servern ska tillämpa uppdateringarna. Detta kan ha betydelse eller inte, beroende på data och domänen. JSON-korrigeringen, som definieras i RFC 6902, är mer flexibel. Den anger ändringarna som en sekvens med åtgärder att tillämpa. Åtgärderna inbegriper att lägga till, ta bort, ersätta, kopiera och testa (om du vill verifiera värdena). Medietypen för JSON-korrigeringen är application/json-patch+json.

Här följer några vanliga feltillstånd som kan uppstå vid bearbetning av en PATCH-begäran, tillsammans med den lämpliga HTTP-statuskoden.

Feltillstånd HTTP-statuskod
Korrigeringsdokumentets format kan inte användas. 415 (medietypen stöds inte)
Felaktigt korrigeringsdokument. 400 (felaktig begäran)
Korrigeringsdokumentet är giltigt, men ändringarna kan inte tillämpas på resursen i det aktuella tillståndet. 409 (konflikt)

DELETE-metoder

Om borttagningen lyckas bör webbservern svara med HTTP-statuskod 204 (inget innehåll), vilket anger att processen har hanterats korrekt, men att svarstexten inte innehåller någon ytterligare information. Om resursen inte finns kan webbservern returnera HTTP 404 (kunde inte hittas).

Asynkrona åtgärder

Ibland kan en POST-, PUT-, PATCH- eller DELETE-åtgärd kräva bearbetning som tar ett tag att slutföra. Om du väntar på att slutföras innan du skickar ett svar till klienten kan det orsaka oacceptabel svarstid. Överväg i så fall att göra åtgärden asynkron. Returnera HTTP-statuskod 202 (accepterad) om du vill ange att begäran togs emot för bearbetning, men har inte slutförts.

Du bör tillgängliggöra en slutpunkt som returnerar statusen för en asynkron begäran så att klienten kan övervaka statusen genom att avsöka statusslutpunkten. Inkludera URI för statusslutpunkten i platsrubriken på 202-svaret. Till exempel:

HTTP/1.1 202 Accepted
Location: /api/status/12345

Om klienten skickar en GET-begäran till den här slutpunkten ska svaret innehålla den aktuella statusen för begäran. Den kan även innehålla en uppskattad tid för slutförande eller en länk för att avbryta åtgärden.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
    "link": { "rel":"cancel", "method":"delete", "href":"/api/status/12345" }
}

Om den asynkrona åtgärden skapar en ny resurs, ska statusslutpunkten returnera statuskod 303 (se annan) när åtgärden har slutförts. Använd en platsrubrik som anger URI för den nya resursen i 303-svaret:

HTTP/1.1 303 See Other
Location: /api/orders/12345

Mer information om hur du implementerar den här metoden finns i Tillhandahålla asynkront stöd för långvariga begäranden och mönstret Asynkron begäran-svar.

Tomma uppsättningar i meddelandetexter

Varje gång brödtexten för ett lyckat svar är tom ska statuskoden vara 204 (inget innehåll). För tomma uppsättningar, till exempel ett svar på en filtrerad begäran utan objekt, bör statuskoden fortfarande vara 204 (inget innehåll), inte 200 (OK).

Filtrera och sidbryta data

Att exponera en samling resurser via en enda URI kan leda till att programmen hämtar stora mängder data när bara en del av informationen krävs. Anta exempelvis att ett klientprogram måste hitta alla order med en kostnad över ett specifikt värde. Den kan hämta alla order från URI:n /order och sedan filtrera dessa order på klientsidan. Det står klart att den här processen är mycket ineffektiv. Det slösar nätverksbandbredd och bearbetningskraft på den server som är värd för webb-API:n.

I stället kan API:n placera ett filter i frågesträngen för URI:n, exempelvis /orders?minCost=n. Webb-API:et ansvarar sedan för att parsa och hantera parametern minCost i frågesträngen och returnera de filtrerade resultaten på serversidan.

GET-begäranden via samlingsresurser kan potentiellt returnera ett stort antal objekt. Du bör utforma en webb-API för att begränsa den mängd data som returneras av varje enskild begäran. Överväg att ge stöd för frågesträngar som anger det maximala antal objekt som ska hämtas och en starta en förskjutning i samlingen. Till exempel:

/orders?limit=25&offset=50

Överväg också att införa en övre gräns för antalet objekt som returneras för att förhindra DOS-attacker. För att underlätta för klientprogrammen kan GET-begäranden som returnerar sidindelade data också innehålla någon form av metadata som anger det totala antalet tillgängliga resurser i samlingen.

Du kan använda en liknande strategi för att sortera data som har hämtats genom att tillhandahålla en sorteringsparameter som tar ett fältnamn som värde, exempelvis /orders?sort=ProductID. Den här metoden kan dock ha en negativ inverkan på cachelagringen, eftersom frågesträngsparametrarna utgör en del av resursidentifieraren som används av många olika cacheimplementeringar som nyckeln till cachelagrade data.

Du kan utöka den här metoden om du vill begränsa de fält som returneras för varje objekt om varje objekt innehåller stora mängder data. Du kan till exempel använda en frågesträngsparameter som accepterar en kommaavgränsad lista med fält, exempelvis /orders?fields=ProductID,Quantity.

Ge alla valfria parametrar i frågesträngarna meningsfulla standardvärden. Ställ exempelvis in parameter limit till 10 och parameter offset till 0 om du implementerar sidbrytning, ange sorteringsparametern till nyckeln för resursen om du implementerar ordning och ange parametern fields i alla fält i resursen om du har stöd för projektioner.

Stöd för partiellt svar för stora binära resurser

En resurs kan innehålla stora binära fält, till exempel filer eller bilder. För att lösa problem orsakade av otillförlitliga och återkommande anslutningar och för att förbättra svarstiderna bör du överväga att aktivera dessa resurser som ska hämtas i segment. Om du vill göra detta ska webb-API:n ha stöd för rubriken Accept-Ranges för GET-begäranden för stora resurser. Den här rubriken anger att åtgärden GET kan användas med partiella begäranden. Klientprogrammet kan skicka GET-begäranden som returnerar en delmängd av en resurs som anges som ett byteintervall.

Överväg också att implementera HTTP HEAD-begäranden för dessa resurser. En HEAD-begäran liknar en GET-begäran, förutom att den returnerar endast HTTP-rubriker som beskriver resursen med en tom meddelandetext. Ett klientprogram kan utfärda en HEAD-begäran för att avgöra om den ska hämta en resurs med hjälp av partiella GET-begäranden. Till exempel:

HEAD https://adventure-works.com/products/10?fields=productImage HTTP/1.1

Här är ett exempel på ett svarsmeddelande:

HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 4580

Content-Length-rubriken anger den totala storleken på resursen och Accept-Ranges-rubriken anger att motsvarande GET-åtgärd fungerar med ofullständiga resultat. Klientprogrammet kan använda den här informationen för att hämta avbildningen i mindre segment. Den första begäran hämtar de första 2 500 byten med rubriken Range:

GET https://adventure-works.com/products/10?fields=productImage HTTP/1.1
Range: bytes=0-2499

Svarsmeddelandet anger att detta är ett partiellt svar genom att returnera HTTP-statuskod 206. Content-Length-rubriken som anger det faktiska antal byte som returneras i meddelandetexten (inte storleken på resursen) och Content-Range-rubriken anger vilken del av resursen detta är (byte 0 2 499 av 4 580):

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes
Content-Type: image/jpeg
Content-Length: 2500
Content-Range: bytes 0-2499/4580

[...]

En efterföljande begäran från klientprogrammet kan hämta resten av resursen.

En av de primära anledningarna till att använda REST är att det ska vara möjligt att navigera i hela uppsättningen av resurser utan att det krävs förkunskaper om URI-schemat. Varje HTTP GET-begäran ska returnera den information som behövs för att hitta de resurser som är direkt relaterade till det begärda objektet via hyperlänkar som inkluderas i svaret, och bör också anges med information som beskriver de åtgärder som är tillgängliga på var och en av dessa resurser. Denna princip kallas HATEOAS, alltså Hypertext as the Engine of Application State (hypertext som motorn för programtillståndet). Systemet är i praktiken en begränsad tillståndsdator och svaret på varje begäran innehåller den information som krävs för att gå från ett tillstånd till ett annat. Ingen annan information bör vara nödvändig.

Kommentar

För närvarande finns det inga allmänna standarder som definierar hur HATEOAS-principen ska modelleras. Exemplen som visas i det här avsnittet illustrerar en möjlig, patentskyddad lösning.

För att exempelvis hantera relationen mellan en order och en kund kan återgivningen av en order innehålla länkar som identifierar de tillgängliga åtgärderna för kunden i orderna. Här är en möjlig återgivning:

{
  "orderID":3,
  "productID":2,
  "quantity":4,
  "orderValue":16.60,
  "links":[
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"customer",
      "href":"https://adventure-works.com/customers/3",
      "action":"DELETE",
      "types":[]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"GET",
      "types":["text/xml","application/json"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"PUT",
      "types":["application/x-www-form-urlencoded"]
    },
    {
      "rel":"self",
      "href":"https://adventure-works.com/orders/3",
      "action":"DELETE",
      "types":[]
    }]
}

I detta exempel har matrisen links en uppsättning av länkar. Varje länk representerar en åtgärd för en relaterad entitet. Data för varje länk innehåller relationen (”kund”), URI:n (https://adventure-works.com/customers/3), HTTP-metoden och de MIME-typer som stöds. Det här är den information som ett klientprogram behöver för att kunna anropa åtgärden.

Matrisen links innehåller också självrefererande information om själva resursen som har hämtats. Dessa har relationen self.

Uppsättningen med länkar som returneras kan förändras beroende på tillståndet för resursen. Det är detta som avses med att hypertexten är ”motorn i programtillståndet”.

Versionshantering av ett RESTful-webb-API

Det är mycket osannolikt att ett webb-API förblir statiskt. När affärsbehov förändras kan nya samlingar av resurser läggas till och relationerna mellan resurserna kan förändras, och strukturen för data i resurserna kan ändras. Att uppdatera en webb-API för att hantera nya eller olika krav är relativt enkelt, men du måste tänka på de effekter som sådana ändringar har på klientprogram som förbrukar webb-API:n. Problemet är att även om utvecklaren som utformar och implementerar ett webb-API har fullständig kontroll över det API:et, har utvecklaren inte samma kontroll över klientprogram som kan skapas av tredjepartsorganisationer som arbetar via fjärranslutning. Den primära tvingande åtgärden är att aktivera befintliga klientprogram så att de fortsätter att fungera oförändrade samtidigt som det blir möjligt för nya klientprogram att dra nytta av nya funktioner och resurser.

Versionshantering aktiverar en webb-API för att ange de funktioner och resurser som den gör tillgänglig, och ett klientprogram kan skicka begäranden som dirigeras till en viss version av en funktion eller resurs. I följande avsnitt beskrivs flera olika metoder, som var och en har sina fördelar och kompromisser.

Ingen versionshantering

Detta är den enklaste metoden och kan vara godtagbar för vissa interna API:er. Betydande ändringar kan representeras som nya resurser eller nya länkar. Att lägga till innehåll i befintliga resurser kanske inte medför någon icke-bakåtkompatibel ändring eftersom klientprogram som inte förväntar sig att se det här innehållet ignorerar det.

En begäran till URI https://adventure-works.com/customers/3 :n ska till exempel returnera information om en enskild kund som innehåller id, nameoch address fält som förväntas av klientprogrammet:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Kommentar

De exempelsvar som visas i det här avsnittet innehåller inte HATEOAS-länkar för enkelhetens skull.

Om DateCreated-fältet har lagts till i schemat för kundresursen skulle svaret se ut så här:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":"1 Microsoft Way Redmond WA 98053"}

Befintliga klientprogram kan fortsätta att fungera korrekt om de har förmåga att ignorera okända fält när nya klientprogram kan utformas för att hantera det här nya fältet. Men om fler radikala dataändringar i schemat för resurser sker (till exempel om fält tas bort eller byter namn) eller relationerna mellan resurserna förändras kan dessa utgöra avgörande förändringar som gör att befintliga klientprogram inte fungerar korrekt. I dessa situationer bör du överväga någon av följande metoder.

URI-versionshantering

Varje gång du ändrar webb-API:n eller ändra schemat för resurser, lägger du till ett versionsnummer till URI:n för varje resurs. Tidigare URI:er bör fortsätta att fungera som tidigare och returnera resurser som motsvarar deras ursprungliga schema.

Om du utökar föregående exempel kan den här versionen av resursen address exponeras via en URI som innehåller ett versionsnummer, till https://adventure-works.com/v2/customers/3exempel streetAddresscitystatezipCode:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Den här mekanismen för versionshantering är mycket enkel men beror på den server som vidarebefordrar begäran till lämplig slutpunkt. Dock kan det bli svårhanterligt eftersom webb-API:n utvecklas via flera itereringar och servern måste stödja ett antal olika versioner. Dessutom hämtar klientprogrammen i samtliga fall samma data (kund 3), så URI:n bör egentligen inte skilja sig beroende på vilken version som finns. Det här schemat komplicerar även implementeringen av HATEOAS eftersom alla länkar behöver inkludera versionsnumret i sina URI:er.

Versionshantering av frågesträngar

I stället för att tillhandahålla flera URI:er kan du ange resursversionen med hjälp av en parameter i frågesträngen som läggs till i HTTP-begäran, till exempel https://adventure-works.com/customers/3?version=2. Versionsparametern ska som standard ha ett beskrivande värde, till exempel 1 om det utelämnas av äldre klientprogram.

Den här metoden har den semantiska fördelen att samma resurs alltid hämtas från samma URI, men det beror på koden som hanterar begäran om att parsa frågesträngen och skicka tillbaka lämpligt HTTP-svar. Den här metoden drabbas även av samma komplikationer för att implementera HATEOAS som URI-mekanismen för versionshantering.

Kommentar

Vissa äldre webbläsare och webbproxyservrar cachelagrar inte svar för begäranden som innehåller en frågesträng i URI:n. Detta kan försämra prestanda för webbprogram som använder ett webb-API och som körs inifrån en sådan webbläsare.

Rubrikversionshantering

I stället för att lägga till versionsnumret som en frågesträngsparameter kan du implementera en anpassad rubrik som anger versionen av resursen. Den här metoden kräver att klientprogrammet lägger till lämpligt sidhuvud till alla begäranden, även om den kod som hanterar klientbegäran kan använda ett standardvärde (version 1) om versionshuvudet utelämnas. I följande exempel används en anpassad rubrik med namnet Custom-Header. Värdet för den här rubriken anger versionen av webb-API:n.

Version 1:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=1
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Version 2:

GET https://adventure-works.com/customers/3 HTTP/1.1
Custom-Header: api-version=2
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{"id":3,"name":"Contoso LLC","dateCreated":"2014-09-04T12:11:38.0376089Z","address":{"streetAddress":"1 Microsoft Way","city":"Redmond","state":"WA","zipCode":98053}}

Precis som med de föregående två metoderna kräver implementering av HATEOAS att lämplig anpassad rubrik inkluderas i alla länkar.

Versionshantering av medietyper

När ett klientprogram skickar en HTTP GET-begäran till en webbserver bör den ange formatet på det innehåll som den kan hantera med hjälp av en Accept-rubrik som beskrivits tidigare i den här vägledningen. Ofta är syftet med rubriken Accept att tillåta klientprogrammet att ange om texten i svaret ska vara i XML-format, JSON-format eller något annat vanligt format som klienten kan parsa. Det är dock möjligt att definiera anpassade medietyper som innehåller information som aktiverar klientprogrammet för att ange vilken version av en resurs den förväntar.

I följande exempel visas en begäran som anger en Accept-rubrik med värdet application/vnd.adventure-works.v1+json. Elementet vnd.adventure works.v1 anger till webbservern att den ska returnera version 1 av resursen, medan elementet json anger att formatet för svarstexten ska vara JSON:

GET https://adventure-works.com/customers/3 HTTP/1.1
Accept: application/vnd.adventure-works.v1+json

Koden som hanterar begäran ansvarar för bearbetning av rubriken Accept och respekterar så långt möjligt (klientprogrammet kan ange flera olika format i rubriken Accept, i så fall kan webbservern välja det lämpligaste formatet för svarstexten). Webbservern bekräftar dataformatet för svarstexten med rubriken ”Content-Type”:

HTTP/1.1 200 OK
Content-Type: application/vnd.adventure-works.v1+json; charset=utf-8

{"id":3,"name":"Contoso LLC","address":"1 Microsoft Way Redmond WA 98053"}

Om Accept-rubriken inte anger några kända medietyper, kan webbservern generera ett svarsmeddelande av typen HTTP 406 (inte godtagbart) eller returnera ett meddelande med en typ av standardmedia.

Den här metoden är utan tvekan den renaste av de mekanismer för versionshantering som används, och lämpar sig naturligt för HATEOAS, vilket kan innefatta MIME-typen av närliggande data i resurslänkar.

Kommentar

När du väljer en strategi för versionshantering bör du också överväga konsekvenserna för prestandan, i synnerhet cachelagring på webbservern. Schemana för URI-versionshantering och versionshantering av frågesträngar är cache-egna i den mån samma kombination av URI/frågesträng refererar till samma data varje gång.

Rubrikversionshanteringens och medietypversionshanteringens mekanismer kräver vanligen ytterligare logik för att granska värdena i den anpassade sidrubriken eller Accept-rubriken. I en omfattande miljö kan många klienter som använder olika versioner av ett webb-API resultera i en mängd duplicerade data i ett cacheminne för serversidan. Det här problemet kan bli märkbart om ett klientprogram kommunicerar med en server via en proxyserver som implementerar cachelagring, och som enbart vidarebefordrar en begäran till webbservern om den inte för närvarande har en kopia av den begärda informationen i cacheminnet.

Öppet API-initiativ

Öppet API-initiativ skapades av ett branschkonsortium för att standardisera REST API-beskrivningar mellan leverantörer. Som en del av det här initiativet har Swagger 2.0-specifikationen bytt namn till OpenAPI Specification (OAS) och förts in i Öppet API-initiativ.

Du kanske vill använda OpenAPI för dina webb-API:er. Några saker att tänka på:

  • OpenAPI-specifikationen har en uppsättning strikta riktlinjer för hur en REST API ska vara utformad. Detta innebär fördelar för samverkan, men kräver större noggrannhet när du utformar API:n så att det passar specifikationen.

  • OpenAPI främjar en metod som prioriterar kontraktet snarare än en metod som prioriterar implementeringen. Kontraktet först betyder att du utformar API-kontraktet (gränssnittet) först och sedan skriver kod som implementerar kontraktet.

  • Verktyg som Swagger kan generera klientbibliotek eller dokumentation från API-kontrakt. Se till exempel ASP.NET webb-API-hjälpsidor med Swagger.

Nästa steg

  • Riktlinjer för Microsoft Azure REST API. Detaljerade rekommendationer för att utforma REST-API:er i Azure.

  • Checklista för webb-API. En användbar lista över saker att tänka på när du utformar och implementerar ett webb-API.

  • Öppet API-initiativ. Dokumentation och implementeringsinformation om öppet API.