Поделиться через


Файлы cookie HTTP в веб-API ASP.NET

В этом разделе описывается отправка и получение файлов cookie HTTP в веб-API.

Фоновые сведения о файлах cookie HTTP

В этом разделе представлен краткий обзор реализации файлов cookie на уровне HTTP. Дополнительные сведения см. в документе RFC 6265.

Файл cookie — это фрагмент данных, который сервер отправляет в HTTP-ответе. Клиент (необязательно) сохраняет файл cookie и возвращает его при последующих запросах. Это позволяет клиенту и серверу совместно использовать состояние. Чтобы задать файл cookie, сервер включает в ответ заголовок Set-Cookie. Формат файла cookie — это пара "имя-значение" с необязательными атрибутами. Пример:

Set-Cookie: session-id=1234567

Ниже приведен пример с атрибутами:

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

Чтобы вернуть файл cookie на сервер, клиент включает заголовок Cookie в последующих запросах.

Cookie: session-id=1234567

Схема процесса возврата файла cookie на сервер, в ходе которого клиент включает заголовок cookie в последующих запросах.

HTTP-ответ может содержать несколько заголовков Set-Cookie.

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

Клиент возвращает несколько файлов cookie, используя один заголовок cookie.

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

Область и длительность файла cookie управляются следующими атрибутами в заголовке Set-Cookie:

  • Домен: сообщает клиенту, какой домен должен получить файл cookie. Например, если домен является "example.com", клиент возвращает файл cookie каждому поддомену example.com. Если он не указан, домен является сервером-источником.
  • Путь. Ограничивает файл cookie указанным путем в домене. Если он не указан, используется путь к универсальному коду ресурса (URI) запроса.
  • Истекает: задает дату окончания срока действия файла cookie. Клиент удаляет файл cookie по истечении срока его действия.
  • Max-Age: задает максимальный срок действия файла cookie. Клиент удаляет файл cookie по достижении максимального возраста.

Если заданы оба Expires параметра и Max-Age , Max-Age имеет приоритет. Если ни то, ни иное не задано, клиент удаляет файл cookie по завершении текущего сеанса. (Точное значение "сеанс" определяется агентом пользователя.)

Однако имейте в виду, что клиенты могут игнорировать файлы cookie. Например, пользователь может отключить файлы cookie по соображениям конфиденциальности. Клиенты могут удалять файлы cookie до истечения срока их действия или ограничивать количество сохраненных файлов cookie. По соображениям конфиденциальности клиенты часто отклоняют сторонние файлы cookie, где домен не соответствует исходному серверу. Короче говоря, сервер не должен полагаться на получение файлов cookie, которые он устанавливает.

Файлы cookie в веб-API

Чтобы добавить файл cookie в HTTP-ответ, создайте экземпляр CookieHeaderValue , представляющий файл cookie. Затем вызовите метод расширения AddCookies , который определен в System.Net.Http. Класс HttpResponseHeadersExtensions , чтобы добавить файл cookie.

Например, следующий код добавляет файл cookie в действие контроллера:

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

Обратите внимание, что AddCookies принимает массив экземпляров CookieHeaderValue .

Чтобы извлечь файлы cookie из клиентского запроса, вызовите метод GetCookies :

string sessionId = "";

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

Значение CookieHeaderValue содержит коллекцию экземпляров CookieState. Каждый файл CookieState представляет один файл cookie. Используйте метод индексатора, чтобы получить CookieState по имени, как показано ниже.

Многие браузеры ограничивают количество файлов cookie, которые они будут хранить — как общее число, так и число для каждого домена. Поэтому может быть полезно поместить структурированные данные в один файл cookie вместо настройки нескольких файлов cookie.

Примечание

RFC 6265 не определяет структуру данных cookie.

С помощью класса CookieHeaderValue можно передать список пар "имя-значение" для данных файла cookie. Эти пары "имя-значение" кодируются как данные формы в кодировке URL-адреса в заголовке 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 });

Предыдущий код создает следующий заголовок Set-Cookie:

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

Класс CookieState предоставляет метод индексатора для чтения вложенных значений из файла cookie в сообщении запроса:

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

Пример. Установка и извлечение файлов cookie в обработчике сообщений

В предыдущих примерах показано, как использовать файлы cookie в контроллере веб-API. Другой вариант — использовать обработчики сообщений. Обработчики сообщений вызываются в конвейере раньше, чем контроллеры. Обработчик сообщений может считывать файлы cookie из запроса до того, как запрос достигнет контроллера, или добавлять файлы cookie в ответ после того, как контроллер создаст ответ.

Схема процесса установки и получения файлов cookie в обработчике сообщений. Показывает, как обработчики сообщений вызываются в конвейере раньше, чем контроллеры.

В следующем коде показан обработчик сообщений для создания идентификаторов сеансов. Идентификатор сеанса хранится в файле cookie. Обработчик проверяет запрос файла cookie сеанса. Если запрос не содержит файл cookie, обработчик создает новый идентификатор сеанса. В любом случае обработчик сохраняет идентификатор сеанса в контейнере свойств HttpRequestMessage.Properties . Он также добавляет файл cookie сеанса в HTTP-ответ.

Эта реализация не проверяет, что идентификатор сеанса от клиента был выдан сервером. Не используйте его в качестве формы проверки подлинности! В этом примере необходимо показать управление файлами 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;
    }
}

Контроллер может получить идентификатор сеанса из контейнера свойств HttpRequestMessage.Properties .

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

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