Suporte de cache para serviços HTTP da Web WCF
O .NET Framework 4.6.1 permite que você use o mecanismo de cache declarativo já disponível no ASP.NET em seus serviços HTTP da Web WCF. Esse mecanismo permite que você armazene em cache respostas de suas operações de serviço HTTP da Web WCF. Quando um usuário envia um HTTP GET
para seu serviço que está configurado para cache, ASP.NET envia de volta a resposta em cache e o método de serviço não é chamado. Quando o cache expirar, na próxima vez que um usuário enviar um HTTP GET
, seu método de serviço será chamado e a resposta será novamente armazenada em cache. Para obter mais informações sobre cache ASP.NET, consulte Visão geral do cache ASP.NET.
Cache de serviço HTTP da Web básico
Para habilitar o cache do serviço HTTP da WEB, você deve primeiro habilitar a compatibilidade ASP.NET aplicando a AspNetCompatibilityRequirementsAttribute configuração RequirementsMode ao serviço a Allowed ou Required.
O .NET Framework 4 introduz um novo atributo chamado AspNetCacheProfileAttribute que permite especificar um nome de perfil de cache. Este atributo é aplicado a uma operação de serviço. O exemplo a seguir aplica o AspNetCompatibilityRequirementsAttribute a um serviço para habilitar a compatibilidade de ASP.NET e configura a GetCustomer
operação para cache. O AspNetCacheProfileAttribute atributo especifica um perfil de cache que contém as configurações de cache a serem usadas.
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)]
public class Service
{
[WebGet(UriTemplate = "{id}")]
[AspNetCacheProfile("CacheFor60Seconds")]
public Customer GetCustomer(string id)
{
// ...
}
}
Ative também ASP.NET modo de compatibilidade no arquivo Web.config, conforme mostrado no exemplo a seguir.
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Aviso
Se ASP.NET modo de compatibilidade não estiver ativado e o for usado, AspNetCacheProfileAttribute uma exceção será lançada.
O nome do AspNetCacheProfileAttribute perfil de cache especificado pelo identifica um perfil de cache que é adicionado ao seu arquivo de configuração Web.config. O perfil de cache é definido com em um <outputCacheSetting>
elemento conforme mostrado no exemplo de configuração a seguir.
<!-- ... -->
<system.web>
<caching>
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheFor60Seconds" duration="60" varyByParam="none" sqlDependency="MyTestDatabase:MyTable"/>
</outputCacheProfiles>
</outputCacheSettings>
</caching>
<!-- ... -->
</system.web>
Este é o mesmo elemento de configuração que está disponível para ASP.NET aplicativos. Para obter mais informações sobre ASP.NET perfis de cache, consulte OutputCacheProfile. Para serviços HTTP da Web, os atributos mais importantes no perfil de cache são: cacheDuration
e varyByParam
. Ambos os atributos são necessários. cacheDuration
Define a quantidade de tempo que uma resposta deve ser armazenada em cache em segundos. varyByParam
Permite especificar um parâmetro de cadeia de caracteres de consulta que é usado para armazenar respostas em cache. Todas as solicitações feitas com diferentes valores de parâmetros de cadeia de caracteres de consulta são armazenadas em cache separadamente. Por exemplo, uma vez que uma solicitação inicial é feita para http://MyServer/MyHttpService/MyOperation?param=10
, todas as solicitações subsequentes feitas com o mesmo URI seriam retornadas a resposta em cache (desde que a duração do cache não tenha decorrido). As respostas para uma solicitação semelhante que é a mesma, mas tem um valor diferente para o parâmetro de cadeia de caracteres de consulta de parâmetro, são armazenadas em cache separadamente. Se você não quiser esse comportamento de cache separado, defina varyByParam
como "nenhum".
Dependência de cache SQL
As respostas do serviço HTTP da Web também podem ser armazenadas em cache com uma dependência de cache SQL. Se o serviço WCF Web HTTP depender de dados armazenados em um banco de dados SQL, convém armazenar em cache a resposta do serviço e invalidar a resposta em cache quando os dados na tabela do banco de dados SQL forem alterados. Esse comportamento é configurado completamente dentro do arquivo Web.config. Primeiro, defina uma cadeia de conexão no <connectionStrings>
elemento .
<connectionStrings>
<add name="connectString"
connectionString="..."
providerName="System.Data.SqlClient" />
</connectionStrings>
Em seguida, habilite a dependência de cache SQL dentro de um <caching>
elemento dentro do <system.web>
elemento como mostrado no exemplo de configuração a seguir.
<system.web>
<caching>
<sqlCacheDependency enabled="true" pollTime="1000">
<databases>
<add name="MyTestDatabase" connectionStringName="connectString" />
</databases>
</sqlCacheDependency>
<!-- ... -->
</caching>
<!-- ... -->
</system.web>
Aqui, a dependência de cache SQL está habilitada e um tempo de sondagem de 1000 milissegundos é definido. Cada vez que o tempo de sondagem decorre, a tabela do banco de dados é verificada quanto a atualizações. Se forem detetadas alterações, o conteúdo do cache será removido e, da próxima vez que a operação de serviço for invocada, uma nova resposta será armazenada em cache. Dentro do <sqlCacheDependency>
elemento , adicione os bancos de dados e faça referência às cadeias de conexão dentro do <databases>
elemento , conforme mostrado no exemplo a seguir.
<system.web>
<caching>
<sqlCacheDependency enabled="true" pollTime="1000">
<databases>
<add name="MyTestDatabase" connectionStringName="connectString" />
</databases>
</sqlCacheDependency>
<!-- ... -->
</caching>
<!-- ... -->
</system.web>
Em seguida, você deve definir as configurações de cache de saída dentro do <caching>
elemento , conforme mostrado no exemplo a seguir.
<system.web>
<caching>
<!-- ... -->
<outputCacheSettings>
<outputCacheProfiles>
<add name="CacheFor60Seconds" duration="60" varyByParam="none" sqlDependency="MyTestDatabase:MyTable" />
</outputCacheProfiles>
</outputCacheSettings>
</caching>
<!-- ... -->
</system.web>
Aqui, a duração do cache é definida como 60 segundos, varyByParam
é definida como nenhum e sqlDependency
é definida como uma lista delimitada por ponto-e-vírgula de pares de nome/tabela de banco de dados separados por dois pontos. Quando os dados são alterados, a resposta armazenada em MyTable
cache para a operação de serviço é removida e, quando a operação é invocada, uma nova resposta é gerada (chamando a operação de serviço), armazenada em cache e devolvida ao cliente.
Importante
Para ASP.NET acessar um banco de dados SQL, você deve usar a ASP.NET Ferramenta de Registro do SQL Server. Além disso, você deve permitir o acesso apropriado da conta de usuário ao banco de dados e à tabela. Para obter mais informações, consulte Acessando o SQL Server a partir de um aplicativo Web.
Cache baseado em HTTP GET condicional
Em cenários HTTP da Web, um HTTP GET condicional é frequentemente usado pelos serviços para implementar cache HTTP inteligente, conforme descrito na Especificação HTTP. Para fazer isso, o serviço deve definir o valor do cabeçalho ETag na resposta HTTP. Ele também deve verificar o cabeçalho If-None-Match na solicitação HTTP para ver se algum dos ETag especificados corresponde ao ETag atual.
Para solicitações GET e HEAD, CheckConditionalRetrieve pega um valor ETag e o verifica em relação ao cabeçalho If-None-Match da solicitação. Se o cabeçalho estiver presente e houver uma correspondência, um WebFaultException com um código de status HTTP 304 (Não modificado) será lançado e um cabeçalho ETag será adicionado à resposta com o ETag correspondente.
Uma sobrecarga do CheckConditionalRetrieve método leva uma data da última modificação e a verifica em relação ao cabeçalho If-Modified-Since da solicitação. Se o cabeçalho estiver presente e o recurso não tiver sido modificado desde então, um WebFaultException com um código de status HTTP 304 (Não modificado) será lançado.
Para solicitações PUT, POST e DELETE, CheckConditionalUpdate usa o valor ETag atual de um recurso. Se o valor ETag atual for nulo, o método verificará se o cabeçalho If-None-Match tem um valor de "*". Se o valor ETag atual não for um valor padrão, o método verificará o valor ETag atual em relação ao cabeçalho If-Match da solicitação. Em ambos os casos, o método lança um WebFaultException com um código de status HTTP 412 (Precondition Failed) se o cabeçalho esperado não estiver presente na solicitação ou seu valor não satisfizer a verificação condicional e definir o cabeçalho ETag da resposta para o valor ETag atual.
Tanto os CheckConditional
métodos quanto o SetETag método garantem que o valor ETag definido no cabeçalho de resposta seja um ETag válido de acordo com a especificação HTTP. Isso inclui cercar o valor ETag em aspas duplas se elas ainda não estiverem presentes e escapar adequadamente de quaisquer caracteres internos de aspas duplas. Não há suporte para comparação de ETag fraca.
O exemplo a seguir mostra como usar esses métodos.
[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;
}
}
Considerações de Segurança
As solicitações que exigem autorização não devem ter suas respostas armazenadas em cache, porque a autorização não é executada quando a resposta é servida a partir do cache. Armazenar essas respostas em cache introduziria uma grave vulnerabilidade de segurança. Normalmente, as solicitações que exigem autorização fornecem dados específicos do usuário e, portanto, o cache do lado do servidor nem é benéfico. Nessas situações, o cache do lado do cliente ou simplesmente não armazenar em cache é mais apropriado.