Host de serviço personalizado
A amostra CustomServiceHost demonstra como usar um derivativo personalizado da classe ServiceHost para alterar o comportamento de tempo de execução de um serviço. Essa abordagem fornece uma alternativa reutilizável para configurar um grande número de serviços de maneira comum. A amostra também demonstra como usar a classe ServiceHostFactory para usar um ServiceHost personalizado no ambiente de hospedagem dos Serviços de Informações da Internet (IIS) ou do Serviço de Ativação de Processo do Windows (WAS).
Sobre o cenário
Para evitar a divulgação não intencional de metadados de serviço potencialmente confidenciais, a configuração padrão para os serviços WCF (Windows Communication Foundation) desabilita a publicação de metadados. Esse comportamento é seguro por padrão, mas também significa que não é possível usar uma ferramenta de importação de metadados (como o Svcutil.exe) para gerar o código cliente necessário para chamar o serviço, a menos que o comportamento de publicação de metadados do serviço esteja explicitamente habilitado na configuração.
Habilitar a publicação de metadados para um grande número de serviços envolve a adição dos mesmos elementos de configuração a cada serviço individual, o que resulta em uma grande quantidade de informações de configuração que é essencialmente a mesma. Como alternativa para configurar cada serviço individualmente, é possível escrever o código imperativo que permite a publicação de metadados uma vez e reutilizar esse código em vários serviços diferentes. Isso é feito criando uma nova classe que deriva de ServiceHost e substitui o método ApplyConfiguration
() para adicionar imperativamente o comportamento de publicação de metadados.
Importante
Para maior clareza, esta amostra demonstra como criar um ponto de extremidade de publicação de metadados não protegidos. Esses pontos de extremidade estão potencialmente disponíveis para os consumidores anônimos não autenticados, e é preciso tomar cuidado antes da implantação desses pontos de extremidade, a fim de garantir que a divulgação pública dos metadados de um serviço seja apropriada.
Implementando um ServiceHost personalizado
A classe ServiceHost expõe vários métodos virtuais úteis que os herdeiros podem substituir para alterar o comportamento em tempo de execução de um serviço. Por exemplo, o método ApplyConfiguration
() lê informações de configuração de serviço do repositório de configuração e altera o ServiceDescription do host de acordo. A implementação padrão lê a configuração do arquivo de configuração de aplicativo. Implementações personalizadas podem substituir ApplyConfiguration
() para alterar ainda mais o ServiceDescription usando código imperativo ou até mesmo substituir totalmente o repositório de configuração padrão. Por exemplo, para ler a configuração de um ponto de extremidade de serviço de um banco de dados em vez do arquivo de configuração de aplicativo.
Nesta amostra, queremos criar um ServiceHost personalizado que adicione o ServiceMetadataBehavior (que habilita a publicação de metadados), mesmo que esse comportamento não seja adicionado explicitamente no arquivo de configuração de serviço. Para fazer isso, crie uma nova classe que herda de ServiceHost e substitua ApplyConfiguration
().
class SelfDescribingServiceHost : ServiceHost
{
public SelfDescribingServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses) { }
//Overriding ApplyConfiguration() allows us to
//alter the ServiceDescription prior to opening
//the service host.
protected override void ApplyConfiguration()
{
//First, we call base.ApplyConfiguration()
//to read any configuration that was provided for
//the service we're hosting. After this call,
//this.Description describes the service
//as it was configured.
base.ApplyConfiguration();
//(rest of implementation elided for clarity)
}
}
Como não queremos ignorar nenhuma configuração fornecida no arquivo de configuração de aplicativo, a primeira coisa que nossa substituição de ApplyConfiguration
() faz é chamar a implementação base. Depois que esse método for concluído, podemos adicionar imperativamente ServiceMetadataBehavior à descrição usando o código imperativo a seguir.
ServiceMetadataBehavior mexBehavior = this.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (mexBehavior == null)
{
mexBehavior = new ServiceMetadataBehavior();
this.Description.Behaviors.Add(mexBehavior);
}
else
{
//Metadata behavior has already been configured,
//so we do not have any work to do.
return;
}
A última coisa que nossa substituição de ApplyConfiguration
() deve fazer é adicionar o ponto de extremidade de metadados padrão. Por convenção, um ponto de extremidade de metadados é criado para cada URI na coleção BaseAddresses do host de serviço.
//Add a metadata endpoint at each base address
//using the "/mex" addressing convention
foreach (Uri baseAddress in this.BaseAddresses)
{
if (baseAddress.Scheme == Uri.UriSchemeHttp)
{
mexBehavior.HttpGetEnabled = true;
this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex");
}
else if (baseAddress.Scheme == Uri.UriSchemeHttps)
{
mexBehavior.HttpsGetEnabled = true;
this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpsBinding(),
"mex");
}
else if (baseAddress.Scheme == Uri.UriSchemeNetPipe)
{
this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexNamedPipeBinding(),
"mex");
}
else if (baseAddress.Scheme == Uri.UriSchemeNetTcp)
{
this.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexTcpBinding(),
"mex");
}
}
Usando um ServiceHost personalizado no auto-host
Agora que concluímos nossa implementação do ServiceHost personalizado, podemos usá-lo para adicionar o comportamento de publicação de metadados a qualquer serviço hospedando esse serviço dentro de uma instância da nossa SelfDescribingServiceHost
. O código a seguir mostra como usá-lo no cenário de auto-host.
SelfDescribingServiceHost host =
new SelfDescribingServiceHost( typeof( Calculator ) );
host.Open();
Nosso host personalizado ainda lê a configuração do ponto de extremidade de serviço do arquivo de configuração de aplicativo, como se tivéssemos usado a classe ServiceHost padrão para hospedar o serviço. No entanto, como adicionamos a lógica para habilitar a publicação de metadados dentro do host personalizado, não devemos mais habilitar explicitamente o comportamento de publicação de metadados na configuração. Essa abordagem tem uma vantagem distinta ao criar um aplicativo que contém vários serviços e você deseja habilitar a publicação de metadados em cada um deles sem gravar os mesmos elementos de configuração repetidamente.
Usando um ServiceHost personalizado no IIS ou WAS
O uso de um host de serviço personalizado em cenários de auto-host é simples, pois ele é o código do aplicativo responsável por criar e abrir a instância do host de serviço. Entretanto, no ambiente de hospedagem do IIS ou WAS, a infraestrutura do WCF está instanciando dinamicamente o host de serviço em resposta às mensagens de entrada. Os hosts de serviço personalizados também podem ser usados nesse ambiente de hospedagem, mas eles exigem algum código adicional na forma de um ServiceHostFactory. O código a seguir mostra um derivado de ServiceHostFactory que retorna instâncias do nosso SelfDescribingServiceHost
personalizado.
public class SelfDescribingServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
//All the custom factory does is return a new instance
//of our custom host class. The bulk of the custom logic should
//live in the custom host (as opposed to the factory)
//for maximum
//reuse value outside of the IIS/WAS hosting environment.
return new SelfDescribingServiceHost(serviceType,
baseAddresses);
}
}
Como você pode ver, implementar um ServiceHostFactory personalizado é simples. Toda a lógica personalizada reside dentro da implementação do ServiceHost; a fábrica retorna uma instância da classe derivada.
Para usar uma fábrica personalizada com uma implementação de serviço, devemos adicionar alguns metadados adicionais ao arquivo .svc do serviço.
<% @ServiceHost Service="Microsoft.ServiceModel.Samples.CalculatorService"
Factory="Microsoft.ServiceModel.Samples.SelfDescribingServiceHostFactory"
language=c# Debug="true" %>
Aqui, adicionamos um atributo Factory
adicional à diretiva @ServiceHost
e passamos o nome do tipo CLR de nossa fábrica personalizada como o valor do atributo. Quando o IIS ou WAS recebe uma mensagem para esse serviço, a infraestrutura de hospedagem do WCF primeiro cria uma instância do ServiceHostFactory e, em seguida, cria uma instância do próprio host de serviço chamando ServiceHostFactory.CreateServiceHost()
.
Executando o exemplo
Embora esta amostra forneça uma implementação de serviço e cliente totalmente funcional, o objetivo da amostra é ilustrar como alterar o comportamento de tempo de execução de um serviço por meio de um host personalizado, execute as seguintes etapas:
Observar o efeito do host personalizado
Abra o arquivo Web.config do serviço e observe que não há configuração que habilite explicitamente os metadados para o serviço.
Abra o arquivo .svc do serviço e observe que sua diretiva @ServiceHost contém um atributo Factory que especifica o nome de um ServiceHostFactory personalizado.
Configurar, compilar e executar a amostra
Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para compilar a solução, siga as instruções contidas em Compilar as amostras do Windows Communication Foundation.
Depois que a solução for criada, execute Setup.bat para configurar o aplicativo ServiceModelSamples no IIS 7.0. O diretório ServiceModelSamples agora deve aparecer como um aplicativo IIS 7.0.
Para executar a amostra em uma configuração de computador único ou cruzado, siga as instruções em Como executar as amostras do Windows Communication Foundation.
Para remover o aplicativo IIS 7.0, execute Cleanup.bat.