Поддержка кэширования для веб-служб HTTP WCF

Платформа .NET Framework, версия 4 позволяет использовать декларативный механизм кэширования, который уже доступен в ASP.NET в службах WCF HTTP. Это позволяет кэшировать ответы от операций службы WCF Web HTTP. Когда пользователь отправляет инструкцию HTTP GET службе, настроенной для кэширования, ASP.NET отправляет обратно кэшированный ответ, метод службы при этом не вызывается. По истечении срока действия кэш в следующий раз, когда пользователь отправит инструкцию HTTP GET, будет вызван метод службы и ответ будет снова кэширован. Дополнительные сведения о кэшировании ASP.NET см. в разделе Общие сведения о кэшировании АSP.NET

Кэширование базовой службы Web HTTP

Чтобы включить кэширование службы WEB http, необходимо сначала включить совместимость с ASP.NET, применив к службе атрибут AspNetCompatibilityRequirementsAttribute и установив RequirementsMode для Allowed или Required.

В .NET Framework 4 вводится новый атрибут AspNetCacheProfileAttribute, который позволяет указать имя профиля кэша. Этот атрибут применяется к операции службы. В следующем примере атрибут AspNetCompatibilityRequirementsAttribute применяется к службе для включения совместимости с ASP.NET, а операция GetCustomer настраивается для кэширования. Атрибут AspNetCacheProfileAttribute задает профиль кэширования, содержащий используемые параметры кэширования.

[ServiceContract] AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
    public class Service
    { 
        [WebGet(UriTemplate = "{id}")]
        [AspNetCacheProfile("CacheFor60Seconds")]
        public Customer GetCustomer(string id)
        {
             // ...
        }
}

Следует также включить режим совместимости с ASP.NET в файле Web.config, как показано в следующем примере.

<system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />    
</system.serviceModel>
Ee230443.Warning(ru-ru,VS.100).gif Внимание!
Если режим совместимости с ASP.NET не включен и используется AspNetCacheProfileAttribute, то возникнет исключение.

Имя профиля кэша, заданное атрибутом AspNetCacheProfileAttribute, идентифицирует профиль кэша, добавленный в файл конфигурации Web.config. Профиль кэша определяется в элементе <outputCacheSetting>, как показано в следующем примере конфигурации.

<!-- ...  -->
<system.web>
   <caching>
      <outputCacheSettings>
         <outputCacheProfiles>
            <add name="CacheFor60Seconds" duration="60" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
         </outputCacheProfiles>
      </outputCacheSettings>
   </caching>
   <!-- ... -->
</system.web>

Этот же элемент конфигурации доступен приложениям ASP.NET. Дополнительные сведения о профилях кэша ASP.NET см. в разделе OutputCacheProfile. Самыми важными атрибутами для служб Web HTTP в профиле кэша являются cacheDuration и varyByParam. Оба атрибута являются обязательными. cacheDuration задает время в секундах, в течение которого ответ должен храниться в кэше. varyByParam позволяет задать параметр строки запроса, используемый для кэширования ответов. Все запросы, составленные с разными значениями параметра строки запроса, кэшируются отдельно. Например, после составления исходного запроса по адресу http://MyServer/MyHttpService/MyOperation?param=10 все последующие запросы с этим URI будут возвращаться из кэша (пока не истечет срок кэширования). Ответы для аналогичного запроса, имеющего другое значение для параметра строки запроса, кэшируются отдельно. Если такое раздельное кэширование нежелательно, установите параметр varyByParam в значение «none».

Зависимость кэша SQL

Ответы службы Web HTTP также могут быть кэшироваться с зависимостью от кэша SQL. Если служба WCF Web HTTP зависит от данных, которые хранятся в базе данных SQL, можно кэшировать ответ службы и аннулировать кэшированный ответ при изменении данных в таблице базы данных SQL. Это полностью настраивается в файле Web.config. Сначала необходимо определить строку соединения в элементе <connectionStrings>.

<connectionStrings>
    <add name="connectString"
        connectionString="Data Source=MyService;Initial Catalog=MyTestDatabase;Integrated Security=True"
        providerName="System.Data.SqlClient" />
  </connectionStrings>

Затем необходимо включить зависимость кэша SQL в элементе <caching> элемента <system.web>, как показано в следующем примере конфигурации.

<system.web>
   <caching>
      <sqlCacheDependency enabled="true" pollTime="1000" >
         <databases>
            <add name="MyTestDatabase" connectionStringName="connectString" />
         </databases>
      </sqlCacheDependency>
      <!-- ... -->
   </caching>
   <!-- ... -->
</system.web>

В данном случае зависимость кэша SQL включена и задан интервал опроса в 1000 миллисекунд. Каждый раз при истечении времени опроса таблица базы данных проверяется на предмет обновлений. Если найдены изменения, то содержимое кэша удаляется, при этом при следующем вызове операции службы новый ответ будет помещен в кэш. Добавьте базы данных в элементе <sqlCacheDependency> и строки соединения в элементе <databases>, как показано в следующем примере.

<system.web>
   <caching>
      <sqlCacheDependency enabled="true" pollTime="1000" >
         <databases>
            <add name="MyTestDatabase" connectionStringName="connectString" />
         </databases>
      </sqlCacheDependency>
      <!-- ... -->
   </caching>
   <!-- ... -->
</system.web>

Затем необходимо настроить параметры кэша вывода в элементе <caching>, как показано в следующем примере.

<system.web>
<caching>
      <!-- ...  -->
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheFor60Seconds" duration="60" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
<!-- ... -->
</system.web>

В данном случае длительность кэширования ― 60 секунд, параметр varyByParam установлен в значение «none», а в параметре sqlDependency содержится список (через точку с запятой) пар «база данных-таблица», где значения разделяются двоеточием. При изменении данных в MyTable ответ для операции службы удаляется из кэша. При вызове операции формируется новый ответ (путем вызова операции службы), который помещается в кэш и возвращается клиенту.

Ee230443.Important(ru-ru,VS.100).gif Примечание
Чтобы ASP.NET имел доступ к базе данных SQL, необходимо использовать средство регистрации АSP.NET Server SQL. Помимо этого, необходимо разрешить соответствующей учетной записи пользователя доступ к базе данных и таблице. Дополнительные сведения см. в разделе Получение доступа к SQL Server из веб-приложения.

Условное кэширование на основе HTTP GET

В сценариях Web HTTP условный HTTP-запрос GET часто используется службами для реализации интеллектуального HTTP-кэширования, которое описано в Спецификации протокола HTTP. Для этого служба должна задать значение заголовка ETag в ответе HTTP. Также необходимо посмотреть заголовок If-None-Match в запросе HTTP, чтобы проверить, совпадает ли какой-либо элемент ETag с текущим элементом ETag.

Для запросов GET и HEAD метод CheckConditionalRetrieve получает значение ETag и сравнивает его с заголовком If-None-Match запроса. Если заголовок присутствует и совпадение найдено, то возникает исключение WebFaultException с кодом состояния HTTP 304 (нет изменений), после чего заголовок ETag добавляется к ответу с совпадающим элементом ETag.

Одна перегрузка метода CheckConditionalRetrieve получает последнюю измененную дату и проверяет ее относительно заголовка If-Modified-Since запроса. Если заголовок присутствует и источник не был изменен, то возникает исключение WebFaultException с кодом состояния HTTP 304 (нет изменений).

Для запросов PUT, POST и DELETE метод CheckConditionalUpdate получает текущее значение ETag источника. Если текущее значение ETag равно null, то метод проверяет, имеет ли заголовок If-None-Match значение «*». Если текущее значение ETag не является значением по умолчанию, то метод проверяет текущее значение ETag относительно заголовка If-Match запроса. В обоих случаях метод выдает исключение WebFaultException с кодом состояния HTTP 412 (предварительное условие не выполнено), если ожидаемый заголовок не присутствует в запросе либо его значение не проходит проверку условия, и задает заголовку ETag текущее значение ETag.

Методы CheckConditional и SetETag гарантируют, что значение ETag, заданное для заголовка ответа, является допустимым значением ETag в соответствии со спецификацией протокола HTTP. Это предполагает заключение значения ETag в двойные кавычки (если они отсутствуют) и правильное экранирование символов двойных кавычек внутри строки. Нестрогое сравнение ETag не поддерживается.

В следующем примере показано использование этих методов.

[WebGet(UriTemplate = "{id}"), Description("Returns the specified customer from customers collection. Returns NotFound if there is no such customer. Supports conditional GET.")]
public Customer GetCustomer(string id)
{
    lock (writeLock)
    {
        // return NotFound if there is no item with the specified id.
        object itemEtag = customerEtags[id];
        if (itemEtag == null)
        {
            throw new WebFaultException(HttpStatusCode.NotFound);
        }

        // return NotModified if the client did a conditional GET and the customer item has not changed
        // since when the client last retrieved it
                 WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve((long)itemEtag);
       Customer result = this.customers[id] as Customer;
       // set the customer etag before returning the result
                WebOperationContext.Current.OutgoingResponse.SetETag((long)itemEtag);
                return result;
    }
}

Вопросы безопасности

Не должны кэшироваться ответы для запросов, требующих авторизации, так как авторизация не выполняется при получении ответа из кэша. Кэширование таких ответов серьезно снижает безопасность. Обычно запросы, требующие авторизации, содержат данные конкретного пользователя, в силу чего кэширование на стороне сервера просто не имеет смысла. В таких ситуациях практичнее использовать кэширование на стороне клиента или вовсе не использовать кэширование.