Host de serviço personalizado
O exemplo CustomServiceHost demonstra como usar uma derivada personalizada da ServiceHost classe para alterar o comportamento em 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 uma maneira comum. O exemplo também demonstra como usar a ServiceHostFactory classe para usar um ServiceHost personalizado no ambiente de hospedagem do IIS (Serviços de Informações da Internet) ou do Serviço de Ativação de Processos 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 serviços do Windows Communication Foundation (WCF) desabilita a publicação de metadados. Esse comportamento é seguro por padrão, mas também significa que você não pode usar uma ferramenta de importação de metadados (como Svcutil.exe) para gerar o código do 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 são essencialmente as mesmas. Como alternativa à configuração de cada serviço individualmente, é possível escrever o código imperativo que permite a publicação de metadados uma vez e, em seguida, reutilizar esse código em vários serviços diferentes. Isso é feito criando uma nova classe que deriva e substitui o ApplyConfiguration
método () para adicionar imperativamente o comportamento de publicação de ServiceHost metadados.
Importante
Para maior clareza, este exemplo demonstra como criar um ponto de extremidade de publicação de metadados não seguro. Esses pontos de extremidade estão potencialmente disponíveis para consumidores anônimos não autenticados e deve-se tomar cuidado antes de implantar esses pontos finais para garantir que a divulgação pública dos metadados de um serviço seja apropriada.
Implementando um ServiceHost personalizado
A ServiceHost classe 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 ApplyConfiguration
método () lê as informações de configuração do serviço do repositório de configuração e altera as do host ServiceDescription de acordo. A implementação padrão lê a configuração do arquivo de configuração do aplicativo. As implementações personalizadas podem substituir ApplyConfiguration
() para alterar ainda mais o ServiceDescription código imperativo de uso ou até mesmo substituir totalmente o armazenamento de configuração padrão. Por exemplo, para ler a configuração de ponto de extremidade de um serviço a partir de um banco de dados em vez do arquivo de configuração do aplicativo.
Neste exemplo, queremos criar um ServiceHost personalizado que adicione o ServiceMetadataBehavior (que permite a publicação de metadados) mesmo que esse comportamento não seja explicitamente adicionado no arquivo de configuração do serviço. Para fazer isso, crie uma nova classe que herda e ServiceHost 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 que tenha sido fornecida no arquivo de configuração do aplicativo, a primeira coisa que nossa substituição de ApplyConfiguration
() faz é chamar a implementação base. Uma vez que este método é concluído, podemos adicionar imperativamente o ServiceMetadataBehavior à descrição usando o seguinte código imperativo.
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 ApplyConfiguration
substituição () 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 autohost
Agora que concluímos nossa implementação ServiceHost personalizada, podemos usá-la para adicionar comportamento de publicação de metadados a qualquer serviço, hospedando esse serviço dentro de uma instância do nosso SelfDescribingServiceHost
. O código a seguir mostra como usá-lo no cenário de host automático.
SelfDescribingServiceHost host =
new SelfDescribingServiceHost( typeof( Calculator ) );
host.Open();
Nosso host personalizado ainda lê a configuração do ponto de extremidade do serviço a partir do arquivo de configuração do aplicativo, como se tivéssemos usado a classe padrão ServiceHost para hospedar o serviço. No entanto, como adicionamos a lógica para habilitar a publicação de metadados dentro de nosso host personalizado, não precisamos mais habilitar explicitamente o comportamento de publicação de metadados na configuração. Essa abordagem tem uma vantagem distinta quando você está criando um aplicativo que contém vários serviços e deseja habilitar a publicação de metadados em cada um deles sem escrever os mesmos elementos de configuração repetidamente.
Usando um ServiceHost personalizado no IIS ou WAS
Usar um host de serviço personalizado em cenários de autohost é simples, porque é o código do seu aplicativo o responsável final pela criação e abertura da instância do host de serviço. No ambiente de hospedagem IIS ou WAS, no entanto, a infraestrutura WCF está instanciando dinamicamente o host do serviço em resposta a mensagens de entrada. Hosts de serviço personalizados também podem ser usados neste ambiente de hospedagem, mas eles exigem algum código adicional na forma de um ServiceHostFactory. O código a seguir mostra uma derivada de ServiceHostFactory que retorna instâncias de nosso costume SelfDescribingServiceHost
.
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 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 adicional Factory
à @ServiceHost
diretiva e passamos o nome do tipo CLR de nossa fábrica personalizada como o valor do atributo. Quando o IIS ou o WAS recebe uma mensagem para esse serviço, a infraestrutura de hospedagem do WCF primeiro cria uma instância do ServiceHostFactory e, em seguida, instancia o próprio host de serviço chamando ServiceHostFactory.CreateServiceHost()
.
Executar o exemplo
Embora este exemplo forneça um cliente totalmente funcional e uma implementação de serviço, o objetivo do exemplo é ilustrar como alterar o comportamento em tempo de execução de um serviço por meio de um host personalizado., execute as seguintes etapas:
Observe o efeito do host personalizado
Abra o arquivo Web.config do serviço e observe que não há nenhuma configuração que habilite explicitamente os metadados para o serviço.
Abra o arquivo .svc do serviço e observe que sua @ServiceHost diretiva contém um atributo Factory que especifica o nome de um ServiceHostFactory personalizado.
Configurar, compilar e executar o exemplo
Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.
Para criar a solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.
Depois que a solução tiver sido criada, execute Setup.bat para configurar o aplicativo ServiceModelSamples no IIS 7.0. O diretório ServiceModelSamples agora deve aparecer como um aplicativo do IIS 7.0.
Para executar o exemplo em uma configuração de máquina única ou cruzada, siga as instruções em Executando os exemplos do Windows Communication Foundation.
Para remover o aplicativo IIS 7.0, execute Cleanup.bat.