Cookie HTTP nell'API Web ASP.NET

Questo argomento descrive come inviare e ricevere cookie HTTP nell'API Web.

Informazioni di base sui cookie HTTP

Questa sezione offre una breve panoramica del modo in cui i cookie vengono implementati a livello HTTP. Per informazioni dettagliate, vedere RFC 6265.

Un cookie è un dato inviato da un server nella risposta HTTP. Il client (facoltativamente) archivia il cookie e lo restituisce nelle richieste successive. Ciò consente al client e al server di condividere lo stato. Per impostare un cookie, il server include un'intestazione Set-Cookie nella risposta. Il formato di un cookie è una coppia nome-valore, con attributi facoltativi. Ad esempio:

Set-Cookie: session-id=1234567

Ecco un esempio con attributi:

Set-Cookie: session-id=1234567; max-age=86400; domain=example.com; path=/;

Per restituire un cookie al server, il client include un'intestazione Cookie nelle richieste successive.

Cookie: session-id=1234567

Diagramma del processo per restituire un cookie al server, durante il quale il client include un'intestazione Cookie nelle richieste successive.

Una risposta HTTP può includere più intestazioni Set-Cookie.

Set-Cookie: session-token=abcdef;
Set-Cookie: session-id=1234567;

Il client restituisce più cookie usando una singola intestazione Cookie.

Cookie: session-id=1234567; session-token=abcdef;

L'ambito e la durata di un cookie sono controllati dagli attributi seguenti nell'intestazione Set-Cookie:

  • Dominio: indica al client quale dominio deve ricevere il cookie. Ad esempio, se il dominio è "example.com", il client restituisce il cookie a ogni sottodominio di example.com. Se non specificato, il dominio è il server di origine.
  • Percorso: limita il cookie al percorso specificato all'interno del dominio. Se non specificato, viene utilizzato il percorso dell'URI della richiesta.
  • Scadenza: imposta una data di scadenza per il cookie. Il client elimina il cookie alla scadenza.
  • Validità massima: imposta la validità massima per il cookie. Il client elimina il cookie quando raggiunge la validità massima.

Se sono impostati sia che ExpiresMax-Age sono impostati, Max-Age ha la precedenza. Se nessuna delle due impostazioni è impostata, il client elimina il cookie al termine della sessione corrente. Il significato esatto di "sessione" è determinato dall'agente utente.

Tuttavia, tenere presente che i client possono ignorare i cookie. Ad esempio, un utente potrebbe disabilitare i cookie per motivi di privacy. I client possono eliminare i cookie prima della scadenza o limitare il numero di cookie archiviati. Per motivi di privacy, i client spesso rifiutano i cookie di "terze parti", in cui il dominio non corrisponde al server di origine. In breve, il server non deve basarsi sul recupero dei cookie impostati.

Cookie nell'API Web

Per aggiungere un cookie a una risposta HTTP, creare un'istanza di CookieHeaderValue che rappresenta il cookie. Chiamare quindi il metodo di estensione AddCookies , definito in System.Net.Http. Classe HttpResponseHeadersExtensions per aggiungere il cookie.

Ad esempio, il codice seguente aggiunge un cookie all'interno di un'azione del controller:

public HttpResponseMessage Get()
{
    var resp = new HttpResponseMessage();

    var cookie = new CookieHeaderValue("session-id", "12345");
    cookie.Expires = DateTimeOffset.Now.AddDays(1);
    cookie.Domain = Request.RequestUri.Host;
    cookie.Path = "/";

    resp.Headers.AddCookies(new CookieHeaderValue[] { cookie });
    return resp;
}

Si noti che AddCookies accetta una matrice di istanze di CookieHeaderValue .

Per estrarre i cookie da una richiesta client, chiamare il metodo GetCookies :

string sessionId = "";

CookieHeaderValue cookie = Request.Headers.GetCookies("session-id").FirstOrDefault();
if (cookie != null)
{
    sessionId = cookie["session-id"].Value;
}

CookieHeaderValue contiene una raccolta di istanze di CookieState. Ogni CookieState rappresenta un cookie. Usare il metodo dell'indicizzatore per ottenere un cookieState per nome, come illustrato.

Molti browser limitano il numero di cookie che archivieranno, sia il numero totale che il numero per dominio. Pertanto, può essere utile inserire dati strutturati in un singolo cookie, invece di impostare più cookie.

Nota

RFC 6265 non definisce la struttura dei dati dei cookie.

Usando la classe CookieHeaderValue , è possibile passare un elenco di coppie nome-valore per i dati dei cookie. Queste coppie nome-valore vengono codificate come dati del modulo con codifica URL nell'intestazione Set-Cookie:

var resp = new HttpResponseMessage();

var nv = new NameValueCollection();
nv["sid"] = "12345";
nv["token"] = "abcdef";
nv["theme"] = "dark blue";
var cookie = new CookieHeaderValue("session", nv); 

resp.Headers.AddCookies(new CookieHeaderValue[] { cookie });

Il codice precedente produce l'intestazione di Set-Cookie seguente:

Set-Cookie: session=sid=12345&token=abcdef&theme=dark+blue;

La classe CookieState fornisce un metodo indicizzatore per leggere i valori secondari da un cookie nel messaggio di richiesta:

string sessionId = "";
string sessionToken = "";
string theme = "";

CookieHeaderValue cookie = Request.Headers.GetCookies("session").FirstOrDefault();
if (cookie != null)
{
    CookieState cookieState = cookie["session"];

    sessionId = cookieState["sid"];
    sessionToken = cookieState["token"];
    theme = cookieState["theme"];
}

Esempio: Impostare e recuperare i cookie in un gestore messaggi

Negli esempi precedenti è stato illustrato come usare i cookie dall'interno di un controller API Web. Un'altra opzione consiste nell'usare i gestori di messaggi. I gestori di messaggi vengono richiamati in precedenza nella pipeline rispetto ai controller. Un gestore di messaggi può leggere i cookie dalla richiesta prima che la richiesta raggiunga il controller o aggiungere cookie alla risposta dopo che il controller genera la risposta.

Diagramma del processo per impostare e ricevere cookie in un gestore di messaggi. Illustra il modo in cui i gestori di messaggi vengono richiamati in precedenza nella pipeline rispetto ai controller.

Il codice seguente mostra un gestore di messaggi per la creazione di ID sessione. L'ID sessione viene archiviato in un cookie. Il gestore controlla la richiesta per il cookie di sessione. Se la richiesta non include il cookie, il gestore genera un nuovo ID sessione. In entrambi i casi, il gestore archivia l'ID sessione nel contenitore delle proprietà HttpRequestMessage.Properties . Aggiunge anche il cookie di sessione alla risposta HTTP.

Questa implementazione non convalida che l'ID sessione del client sia stato effettivamente emesso dal server. Non usarlo come forma di autenticazione. Il punto dell'esempio consiste nel mostrare la gestione dei cookie HTTP.

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;

public class SessionIdHandler : DelegatingHandler
{
    public static string SessionIdToken = "session-id";

    async protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        string sessionId;

        // Try to get the session ID from the request; otherwise create a new ID.
        var cookie = request.Headers.GetCookies(SessionIdToken).FirstOrDefault();
        if (cookie == null)
        {
            sessionId = Guid.NewGuid().ToString();
        }
        else 
        {
            sessionId = cookie[SessionIdToken].Value;
            try
            {
                Guid guid = Guid.Parse(sessionId);
            }
            catch (FormatException)
            {
                // Bad session ID. Create a new one.
                sessionId = Guid.NewGuid().ToString();
            }
        }

        // Store the session ID in the request property bag.
        request.Properties[SessionIdToken] = sessionId;

        // Continue processing the HTTP request.
        HttpResponseMessage response = await base.SendAsync(request, cancellationToken);

        // Set the session ID as a cookie in the response message.
        response.Headers.AddCookies(new CookieHeaderValue[] {
            new CookieHeaderValue(SessionIdToken, sessionId) 
        });

        return response;
    }
}

Un controller può ottenere l'ID sessione dal contenitore delle proprietà HttpRequestMessage.Properties .

public HttpResponseMessage Get()
{
    string sessionId = Request.Properties[SessionIdHandler.SessionIdToken] as string;

    return new HttpResponseMessage()
    {
        Content = new StringContent("Your session ID = " + sessionId)
    };
}