Condividi tramite


Negoziazione del contenuto in API Web ASP.NET

Questo articolo descrive come API Web ASP.NET implementa la negoziazione del contenuto per ASP.NET 4.x.

La specifica HTTP (RFC 2616) definisce la negoziazione del contenuto come "il processo di selezione della migliore rappresentazione per una determinata risposta quando sono disponibili più rappresentazioni". Il meccanismo principale per la negoziazione del contenuto in HTTP sono le intestazioni di richiesta seguenti:

  • Accettare: Quali tipi di supporti sono accettabili per la risposta, ad esempio "application/json", "application/xml" o un tipo di supporto personalizzato, ad esempio "application/vnd.example+xml"
  • Accept-Charset: Quali set di caratteri sono accettabili, ad esempio UTF-8 o ISO 8859-1.
  • Accept-Encoding: Quali codifiche di contenuto sono accettabili, ad esempio gzip.
  • Accept-Language: Il linguaggio naturale preferito, ad esempio "en-us".

Il server può anche esaminare altre parti della richiesta HTTP. Ad esempio, se la richiesta contiene un'intestazione X-Requested-With, che indica una richiesta AJAX, il server potrebbe impostare come impostazione predefinita JSON se non è presente alcuna intestazione Accept.

In questo articolo si esaminerà in che modo l'API Web usa le intestazioni Accept e Accept-Charset. Attualmente non è disponibile alcun supporto predefinito per Accept-Encoding o Accept-Language.

Serializzazione

Se un controller API Web restituisce una risorsa come tipo CLR, la pipeline serializza il valore restituito e lo scrive nel corpo della risposta HTTP.

Si consideri ad esempio l'azione controller seguente:

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

Un client potrebbe inviare questa richiesta HTTP:

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

In risposta, il server potrebbe inviare:

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}

In questo esempio il client ha richiesto JSON, Javascript o "anything" (*/*). Il server ha risposto con una rappresentazione JSON dell'oggetto Product . Si noti che l'intestazione Content-Type nella risposta è impostata su "application/json".

Un controller può anche restituire un oggetto HttpResponseMessage . Per specificare un oggetto CLR per il corpo della risposta, chiamare il metodo di estensione 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);
}

Questa opzione offre un maggiore controllo sui dettagli della risposta. È possibile impostare il codice di stato, aggiungere intestazioni HTTP e così via.

L'oggetto che serializza la risorsa viene chiamato formattatore multimediale. I formattatori multimediali derivano dalla classe MediaTypeFormatter . L'API Web fornisce formattatori multimediali per XML e JSON ed è possibile creare formattatori personalizzati per supportare altri tipi di supporti. Per informazioni sulla scrittura di un formattatore personalizzato, vedere Formattatori multimediali.

Funzionamento della negoziazione del contenuto

Prima di tutto, la pipeline ottiene il servizio IContentNegotiator dall'oggetto HttpConfiguration . Ottiene anche l'elenco dei formattatori multimediali dall'insieme HttpConfiguration.Formatters .

Successivamente, la pipeline chiama IContentNegotiator.Negotiate, passando:

  • Tipo di oggetto da serializzare
  • Raccolta di formattatori multimediali
  • Richiesta HTTP

Il metodo Negotiate restituisce due informazioni:

  • Formattatore da usare
  • Tipo di supporto per la risposta

Se non viene trovato alcun formattatore, il metodo Negotiate restituisce Null e il client riceve l'errore HTTP 406 (Non accettabile).

Il codice seguente illustra come un controller può richiamare direttamente la negoziazione del contenuto:

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

Questo codice equivale a ciò che la pipeline esegue automaticamente.

Negoziatore contenuto predefinito

La classe DefaultContentNegotiator fornisce l'implementazione predefinita di IContentNegotiator. Usa diversi criteri per selezionare un formattatore.

Innanzitutto, il formattatore deve essere in grado di serializzare il tipo. Questa verifica viene eseguita chiamando MediaTypeFormatter.CanWriteType.

Successivamente, il negoziatore di contenuto esamina ogni formattatore e valuta il livello di corrispondenza con la richiesta HTTP. Per valutare la corrispondenza, il negoziatore di contenuto esamina due elementi nel formattatore:

  • L'insieme SupportedMediaTypes , che contiene un elenco di tipi di supporti supportati. Il negoziatore di contenuto tenta di corrispondere a questo elenco rispetto all'intestazione Accept della richiesta. Si noti che l'intestazione Accept può includere intervalli. Ad esempio, "text/plain" è una corrispondenza per text/* o */*.
  • Insieme MediaTypeMappings , che contiene un elenco di oggetti MediaTypeMapping . La classe MediaTypeMapping offre un modo generico per trovare le corrispondenze con le richieste HTTP con i tipi di supporto. Ad esempio, potrebbe eseguire il mapping di un'intestazione HTTP personalizzata a un tipo di supporto specifico.

Se sono presenti più corrispondenze, la corrispondenza con il fattore di qualità più alto prevale. Ad esempio:

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

In questo esempio application/json ha un fattore di qualità implicito pari a 1.0, quindi è preferibile rispetto a application/xml.

Se non vengono trovate corrispondenze, il negoziatore di contenuto tenta di trovare una corrispondenza nel tipo di supporto del corpo della richiesta, se presente. Ad esempio, se la richiesta contiene dati JSON, il negoziatore di contenuto cerca un formattatore JSON.

Se non sono ancora presenti corrispondenze, il negoziatore di contenuto seleziona semplicemente il primo formattatore in grado di serializzare il tipo.

Selezione di una codifica di caratteri

Dopo aver selezionato un formattatore, il negoziatore di contenuto sceglie la codifica dei caratteri migliore esaminando la proprietà SupportedEncodings nel formattatore e associandola all'intestazione Accept-Charset nella richiesta (se presente).