Partilhar via


Cookies HTTP na Web API ASP.NET

Este tópico descreve como enviar e receber cookies HTTP na Web API.

Informações sobre Cookies HTTP

Esta secção oferece uma breve visão geral de como os cookies são implementados ao nível HTTP. Para mais detalhes, consulte o RFC 6265.

Um cookie é um dado que um servidor envia na resposta HTTP. O cliente (opcionalmente) armazena o cookie e devolve-o em pedidos subsequentes. Isto permite que o cliente e o servidor partilhem estado. Para definir um cookie, o servidor inclui um cabeçalho Set-Cookie na resposta. O formato de um cookie é um par nome-valor, com atributos opcionais. Por exemplo:

Set-Cookie: session-id=1234567

Aqui está um exemplo com atributos:

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

Para devolver um cookie ao servidor, o cliente inclui um cabeçalho Cookie em pedidos posteriores.

Cookie: session-id=1234567

Diagrama do processo para devolver um cookie ao servidor, durante o qual o cliente inclui um cabeçalho Cookie em pedidos posteriores.

Uma resposta HTTP pode incluir múltiplos cabeçalhos Set-Cookie.

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

O cliente devolve múltiplos cookies usando um único cabeçalho Cookie.

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

O âmbito e a duração de um cookie são controlados pelos seguintes atributos no cabeçalho Set-Cookie:

  • Domínio: Indica ao cliente qual o domínio que deve receber o cookie. Por exemplo, se o domínio for "example.com", o cliente devolve o cookie a todos os subdomínios de example.com. Se não for especificado, o domínio é o servidor de origem.
  • Caminho: Restringe o cookie ao caminho especificado dentro do domínio. Se não especificado, o caminho do URI do pedido é utilizado.
  • Expira: Define uma data de validade para o cookie. O cliente apaga o cookie quando este expira.
  • Max-Age: Define a idade máxima para o cookie. O cliente apaga o cookie quando atinge a idade máxima.

Se ambos Expires e Max-Age forem definidos, Max-Age tem precedência. Se nenhum dos dois estiver definido, o cliente apaga o cookie quando a sessão atual termina. (O significado exato de "sessão" é determinado pelo user-agent.)

No entanto, esteja ciente de que os clientes podem ignorar cookies. Por exemplo, um utilizador pode desativar cookies por razões de privacidade. Os clientes podem eliminar cookies antes de expirarem ou limitar o número de cookies armazenados. Por razões de privacidade, os clientes frequentemente rejeitam cookies de "terceiros", quando o domínio não corresponde ao servidor de origem. Resumindo, o servidor não deve depender de recuperar os cookies que estabelece.

Cookies na Web API

Para adicionar um cookie a uma resposta HTTP, crie uma instância CookieHeaderValue que represente o cookie. Depois chama o método de extensão AddCookies , que está definido no System.Net.Http. HttpResponseHeadersExtensions , para adicionar o cookie.

Por exemplo, o seguinte código adiciona um cookie dentro de uma ação do controlador:

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

Note que AddCookies recebe um array de instâncias de CookieHeaderValue.

Para extrair os cookies de um pedido de cliente, ligue para o método GetCookies :

string sessionId = "";

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

Um CookieHeaderValue contém uma coleção de instâncias de CookieState. Cada CookieState representa um cookie. Use o método indexador para obter um CookieState pelo nome, como mostrado.

Muitos navegadores limitam o número de cookies que vão armazenar — tanto o número total como o número por domínio. Por isso, pode ser útil colocar dados estruturados num único cookie, em vez de definir múltiplos cookies.

Observação

A RFC 6265 não define a estrutura dos dados de cookies.

Usando a classe CookieHeaderValue , pode passar uma lista de pares nome-valor para os dados de cookies. Estes pares nome-valor são codificados como dados de formulário codificados por URL no cabeçalho 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 });

O código anterior produz o seguinte cabeçalho Set-Cookie:

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

A classe CookieState fornece um método indexador para ler os subvalores de um cookie na mensagem de pedido:

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

Exemplo: Definir e recuperar cookies num manipulador de mensagens

Os exemplos anteriores mostraram como usar cookies a partir de um controlador Web API. Outra opção é usar manipuladores de mensagens. Os manipuladores de mensagens são invocados mais cedo no pipeline do que os controladores. Um manipulador de mensagens pode ler cookies do pedido antes de este chegar ao controlador, ou adicionar cookies à resposta depois de o controlador gerar a resposta.

Diagrama do processo para definir e receber cookies num manipulador de mensagens. Ilustra como os manipuladores de mensagens são invocados mais cedo no pipeline do que os controladores.

O código seguinte mostra um manipulador de mensagens para criar IDs de sessão. O ID da sessão é armazenado num cookie. O manipulador verifica a requisição para o cookie de sessão. Se o pedido não incluir o cookie, o handler gera um novo ID de sessão. Em qualquer dos casos, o handler armazena o ID da sessão no saco de propriedades HttpRequestMessage.Properties . Também adiciona o cookie de sessão à resposta HTTP.

Esta implementação não valida que o ID de sessão do cliente tenha sido realmente emitido pelo servidor. Não o uses como forma de autenticação! O objetivo do exemplo é mostrar a gestão de cookies 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;
    }
}

Um controlador pode obter o ID da sessão do saco de propriedades HttpRequestMessage.Properties .

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

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