Vyjednávání obsahu ve webovém rozhraní API ASP.NET

Tento článek popisuje, jak ASP.NET webové rozhraní API implementuje vyjednávání obsahu pro ASP.NET 4.x.

Specifikace HTTP (RFC 2616) definuje vyjednávání obsahu jako "proces výběru nejlepší reprezentace pro danou odpověď, pokud je k dispozici více reprezentací". Primárním mechanismem pro vyjednávání obsahu v protokolu HTTP jsou tyto hlavičky požadavků:

  • Přijmout: Jaké typy médií jsou pro odpověď přijatelné, například application/json, application/xml nebo vlastní typ média, například application/vnd.example+xml.
  • Accept-Charset: Které znakové sady jsou přijatelné, například UTF-8 nebo ISO 8859-1.
  • Přijmout kódování: Která kódování obsahu jsou přijatelná, například gzip.
  • Accept-Language: Upřednostňovaný přirozený jazyk, například "en-us".

Server se také může podívat na jiné části požadavku HTTP. Pokud například požadavek obsahuje hlavičku X-Requested-With označující požadavek AJAX, server může jako výchozí použít JSON, pokud neexistuje hlavička Accept.

V tomto článku se podíváme na to, jak webové rozhraní API používá hlavičky Accept a Accept-Charset. (V tuto chvíli není k dispozici integrovaná podpora pro Accept-Encoding nebo Accept-Language.)

Serializace

Pokud kontroler webového rozhraní API vrátí prostředek jako typ CLR, kanál serializuje návratovou hodnotu a zapíše ji do těla odpovědi HTTP.

Zvažte například následující akci kontroleru:

public Product GetProduct(int id)
{
    var item = _products.FirstOrDefault(p => p.ID == id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return item; 
}

Klient může odeslat tento požadavek HTTP:

GET http://localhost.:21069/api/products/1 HTTP/1.1
Host: localhost.:21069
Accept: application/json, text/javascript, */*; q=0.01

V reakci na to server může odeslat:

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

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

V tomto příkladu si klient vyžádal JSON, JavaScript nebo "cokoli" (*/*). Server odpověděl reprezentací objektu Product JSON. Všimněte si, že hlavička Content-Type v odpovědi je nastavená na application/json.

Kontroler může také vrátit objekt HttpResponseMessage . Pokud chcete zadat objekt CLR pro tělo odpovědi, zavolejte metodu rozšíření CreateResponse :

public HttpResponseMessage GetProduct(int id)
{
    var item = _products.FirstOrDefault(p => p.ID == id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return Request.CreateResponse(HttpStatusCode.OK, item);
}

Tato možnost poskytuje větší kontrolu nad podrobnostmi odpovědi. Můžete nastavit stavový kód, přidat hlavičky HTTP atd.

Objekt, který serializuje prostředek, se nazývá media formatter. Formátovací nástroje médií jsou odvozeny z Třídy MediaTypeFormatter . Webové rozhraní API poskytuje formátovací moduly médií pro XML a JSON a můžete vytvořit vlastní formátovací moduly pro podporu jiných typů médií. Informace o psaní vlastního formátovače najdete v tématu Formátovací moduly médií.

Jak funguje vyjednávání obsahu

Nejprve kanál získá službu IContentNegotiator z objektu HttpConfiguration . Získá také seznam multimediálních formátovačů z kolekce HttpConfiguration.Formatters .

Kanál pak zavolá IContentNegotiator.Negotiate a předá:

  • Typ objektu, který se má serializovat
  • Kolekce formátovačů médií
  • Požadavek HTTP

Metoda Negotiate vrátí dvě informace:

  • Který formátovací nástroj se má použít
  • Typ média pro odpověď

Pokud není nalezen žádný formátovací kód, vrátí metoda Negotiatehodnotu null a klient obdrží chybu HTTP 406 (Není přijatelné).

Následující kód ukazuje, jak může kontroler přímo vyvolat vyjednávání obsahu:

public HttpResponseMessage GetProduct(int id)
{
    var product = new Product() 
        { Id = id, Name = "Gizmo", Category = "Widgets", Price = 1.99M };

    IContentNegotiator negotiator = this.Configuration.Services.GetContentNegotiator();

    ContentNegotiationResult result = negotiator.Negotiate(
        typeof(Product), this.Request, this.Configuration.Formatters);
    if (result == null)
    {
        var response = new HttpResponseMessage(HttpStatusCode.NotAcceptable);
        throw new HttpResponseException(response));
    }

    return new HttpResponseMessage()
    {
        Content = new ObjectContent<Product>(
            product,		        // What we are serializing 
            result.Formatter,           // The media formatter
            result.MediaType.MediaType  // The MIME type
        )
    };
}

Tento kód je ekvivalentní tomu, co kanál dělá automaticky.

Výchozí vyjednavač obsahu

DefaultContentNegotiator Třída poskytuje výchozí implementaci IContentNegotiator. K výběru formátovače se používá několik kritérií.

Za prvé, formátovací modul musí být schopen serializovat typ. To je ověřeno voláním MediaTypeFormatter.CanWriteType.

V dalším kroku se vyjednavač obsahu podívá na jednotlivé formáty a vyhodnotí, jak dobře odpovídá požadavku HTTP. Při vyhodnocování shody se vyjednavač obsahu podívá na dvě věci na formátovacím nástroji:

  • Kolekce SupportedMediaTypes , která obsahuje seznam podporovaných typů médií. Vyjednavač obsahu se pokusí porovnat tento seznam s hlavičkou request Accept. Všimněte si, že hlavička Accept může obsahovat rozsahy. Například text/plain odpovídá textu/* nebo */*.
  • Kolekce MediaTypeMappings, která obsahuje seznam objektů MediaTypeMapping. MediaTypeMapping Třída poskytuje obecný způsob, jak spárovat požadavky HTTP s typy médií. Může například namapovat vlastní hlavičku HTTP na konkrétní typ média.

Pokud je zápasů více, vyhrává zápas s nejvyšším faktorem kvality. Příklad:

Accept: application/json, application/xml; q=0.9, */*; q=0.1

V tomto příkladu má application/json implicitní faktor kvality 1.0, takže se upřednostňuje před application/xml.

Pokud nejsou nalezeny žádné shody, pokusí se vyjednavač obsahu porovnat typ média v textu požadavku, pokud existuje. Pokud například požadavek obsahuje data JSON, vyjednavač obsahu hledá formátovací modul JSON.

Pokud stále neexistují žádné shody, vyjednavač obsahu jednoduše vybere první formátovací modul, který může serializovat typ.

Výběr kódování znaků

Po výběru formatteru zvolí vyjednavač obsahu nejlepší kódování znaků tak, že se podívá na vlastnost SupportedEncodings (Podporované encodings ) na formátovacím nástroji a porovnává ji s hlavičkou Accept-Charset v požadavku (pokud je k dispozici).