Partilhar via


Cookies HTTP no ASP.NET Web API

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

Plano de fundo sobre cookies HTTP

Esta seção fornece uma breve visão geral de como os cookies são implementados no nível HTTP. Para obter detalhes, consulte RFC 6265.

Um cookie é um dado que um servidor envia na resposta HTTP. O cliente (opcionalmente) armazena o cookie e o retorna em solicitações subsequentes. Isso permite que o cliente e o servidor compartilhem o 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 retornar um cookie ao servidor, o cliente inclui um cabeçalho Cookie em solicitações posteriores.

Cookie: session-id=1234567

Diagrama do processo para retornar um cookie ao servidor, durante o qual o cliente inclui um cabeçalho Cookie em solicitações posteriores.

Uma resposta HTTP pode incluir vários cabeçalhos Set-Cookie.

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

O cliente retorna vários cookies usando um único cabeçalho Cookie.

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

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

  • Domínio: informa ao cliente qual domínio deve receber o cookie. Por exemplo, se o domínio for "example.com", o cliente retornará o cookie para cada subdomínio de example.com. Se não for especificado, o domínio será o servidor de origem.
  • Caminho: restringe o cookie ao caminho especificado dentro do domínio. Se não for especificado, o caminho do URI de solicitação será usado.
  • Expira: define uma data de validade para o cookie. O cliente exclui o cookie quando ele expira.
  • Idade Máxima: define a idade máxima para o cookie. O cliente exclui o cookie quando atinge a idade máxima.

Se e ExpiresMax-Age estiverem definidos, Max-Age terá precedência. Se nenhum for definido, o cliente excluirá o cookie quando a sessão atual terminar. (O significado exato de "sessão" é determinado pelo user-agent.)

No entanto, lembre-se de que os clientes podem ignorar cookies. Por exemplo, um usuário pode desabilitar cookies por motivos de privacidade. Os clientes podem excluir cookies antes de expirarem ou limitar o número de cookies armazenados. Por motivos de privacidade, os clientes geralmente rejeitam cookies de "terceiros", em que o domínio não corresponde ao servidor de origem. Em suma, o servidor não deve depender da obtenção dos cookies que ele define.

Cookies na API Web

Para adicionar um cookie a uma resposta HTTP, crie uma instância CookieHeaderValue que representa o cookie. Em seguida, chame o método de extensão AddCookies , que é definido no System.Net.Http. Classe HttpResponseHeadersExtensions para adicionar o cookie.

Por exemplo, o código a seguir 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;
}

Observe que AddCookies usa uma matriz de instâncias CookieHeaderValue .

Para extrair os cookies de uma solicitação de cliente, chame 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 CookieState . Cada CookieState representa um cookie. Use o método indexador para obter um CookieState por nome, conforme mostrado.

Muitos navegadores limitam quantos cookies eles armazenarão, tanto o número total quanto o número por domínio. Portanto, pode ser útil colocar dados estruturados em um único cookie, em vez de definir vários cookies.

Observação

O RFC 6265 não define a estrutura dos dados de cookie.

Usando a classe CookieHeaderValue , você pode passar uma lista de pares nome-valor para os dados de cookie. Esses pares nome-valor são codificados como dados de formulário codificados em 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 sub-valores de um cookie na mensagem de solicitação:

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 em um manipulador de mensagens

Os exemplos anteriores mostraram como usar cookies de dentro de um controlador de API Web. Outra opção é usar manipuladores de mensagens. Manipuladores de mensagens são invocados anteriormente no pipeline do que controladores. Um manipulador de mensagens pode ler cookies da solicitação antes que a solicitação atinja o controlador ou adicionar cookies à resposta depois que o controlador gerar a resposta.

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

O código a seguir mostra um manipulador de mensagens para criar IDs de sessão. A ID da sessão é armazenada em um cookie. O manipulador verifica a solicitação do cookie de sessão. Se a solicitação não incluir o cookie, o manipulador gerará uma nova ID de sessão. Em ambos os casos, o manipulador armazena a ID da sessão no recipiente de propriedades HttpRequestMessage.Properties . Ele também adiciona o cookie de sessão à resposta HTTP.

Essa implementação não valida que a ID da sessão do cliente foi realmente emitida pelo servidor. Não o use como uma forma de autenticação! O ponto do exemplo é mostrar o gerenciamento de 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;
    }
}

Um controlador pode obter a ID da sessão do recipiente 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)
    };
}