O objetivo 3.3 da certificação Microsoft 70-487, Configure WCF services by using the API, trata dos mesmos aspectos do objetivo anterior. A diferença é que enquanto no objetivo 3.2 vimos como alterar as configurações de um serviço WCF via arquivo de configuração, nesse vamos ver como fazer essas configurações via código, ou API.
Configurando Comportamentos do Serviço
Como configuramos os behaviors no post anterior com ServiceBehaviors e EndpointBehaviors, podemos fazer as mesmas configurações com os atributos ServiceBehaviorAttribute e OperationBehaviorAttribute.
ServiceBehaviorAttribute é do mesmo nível que ServiceContract, então os behaviors são configurados no nível de serviço e se aplicam ao serviço inteiro. OperationBehaviorAttribute se equivale ao OperationContract, ou seja, a cada método do serviço.
ServiceBehaviorAttribute não pode decorar a interface do contrato, somente a implementação (classe). A lista das propriedades pode ser consultada na MSDN.
Uma questão provável de cair no exame é sobre concorrência de ServiceContractAttribute e ServiceBehaviorAttribute. Por exemplo, ambas têm as propriedades Name e Namespace, resultando no seguinte cenário:
[ServiceContract(Namespace="http://www.williamgryan.mobi/Books/70-487", Name="RandomText")]
public interface ITestService{}
[ServiceBehavior(Name="Test", Namespace="http://www.williamgryan.mobi/Books/70-487/Services")]
public class TestService : ITestService
{}
O resultado do envelope do request é o seguinte:
Aqui o namespace (xmlns) é o definido no ServiceContract. Note o que acontece no MetadataExchange, quando você adiciona uma referência ao serviço em um client, e no WSDL:
Isso mostra que o ServiceBehaviorAttribute seta os valores de Name e Namespace no elemento service dentro do WSDL. Quando definidos no ServiceContract, eles ficam no WSDL.
Configurando Bindings
Assim como no arquivo de configuração, há vários bindings disponíveis para se configurar via API. Todos eles funcionam de forma quase idêntica, apenas com pequenas diferenças. Vamos passar pelos 5 mais comuns, incluindo custom binding.
BasicHttpBinding
BasicHttpBinding é o mais simples dos bindings disponíveis. Funciona via HTTP, precisa da especificação do SOAP 1.1 e tem poucos recursos de segurança. Ele possui 3 construtores:
- new BasicHttpBinding(): deixa o WCF lidar com os valores default de configuração;
- new BasicHttpBinding(BasicHttpSecurityMode): recebe um BasicHttpSecurityMode que tem os seguintes valores: None, Transport, Message e TransportCredentialOnly;
- new BasicHttpBinding(string): recebe o nome de um binding no arquivo de configuração.
wsHttpBinding
Funciona via HTTP como o BasicHttpBinding, mas possui 2 principais vantagens: usa o padrão SOAP 1.2 e tem suporte ao padrão WS-*, que contempla features como Reliable Messaging, transações, comunicação duplex, etc.
wsHttpBinding tem 4 construtores:
- new WSHttpBinding(): deixa o WCF lidar com os valores default de configuração;
- new WSHttpBinding(SecurityMode): recebe um BasicHttpSecurityMode que tem os seguintes valores: None, Transport, Message e TransportWithMessageCredential;
- new WSHttpBinding(string): recebe o nome de um binding no arquivo de configuração;
- new WSHttpBinding(SecurityMode, bool): recebe um SecurityMode e um bool que habilita ReliableSession
NetMsmqBinding
Implementação quase idêntica à de um wsHttpBinding. A diferença é que usa o enum MsmqSecurityMode para opções de segurança e não há suporte para Reliable Messaging:
- new NetMsmqBinding(): deixa o WCF lidar com os valores default de configuração;
- new NetMsmqBinding(NetMsmqSecurityMode): recebe um NetMsmqSecurityModeque tem os seguintes valores: Transport, Message, Both e None;
- new NetMsmqBinding(string): recebe o nome de um binding no arquivo de configuração.
NetNamedPipeBinding
NetNamedPipeBinding é usado para comunicação entre serviços na mesma máquina, tendo um grande nível de performance.
Como o NetMsmqBinding Implementação quase idêntica à de um wsHttpBinding. A diferença é que usa o enum MsmqSecurityMode para opções de segurança e não há suporte para Reliable Messaging:
- new NetMsmqBinding(): deixa o WCF lidar com os valores default de configuração;
- new NetMsmqBinding(NetMsmqSecurityMode): recebe um NetMsmqSecurityModeque tem os seguintes valores: Transport, Message, Both e None;
- new NetMsmqBinding(string): recebe o nome de um binding no arquivo de configuração.
Custom Binding
Para implementar um binding customizado, você pode usar um ou mais dos predefinidos e adicionar features ou implementar um exclusivo.
O elemento <customBinding> é utilizado para se definir um binding customizado. Há um processo a se seguir com ordem específica:
- O item mais externo é o TransactionFlowBindingElement. É opcional, necessário somente se você quiser suportar Flowing Transactions;
- ReliableSessionBindingElement, também opcional, indica se Reliable Sessions devem ser suportadas;
- SecurityBindingElement define funcionalidades de segurança como autorização, autenticação, proteção, confidencialidade, etc. Também opcional;
- O próximo item da cadeia é o CompositeDuplexBindingElement, caso comunicações duplex sejam suportadas;
- O elemento OneWayBindingElement pode ser usado se você quiser prover comunicações OneWay para o serviço;
- Dois elementos podem ser implementados para prover Stream Security: SslStreamSecurityBindingElement e WindowsStreamSecurityBindingElement. Implementação opcional;
- O próximo item se trata do encoding da mensagem. Ao contrário dos outros, esse é obrigatório na implementação de um custom binding. Há 3 encodings disponíveis: TextMessageEncodingBindingElement, BinaryMessageEncodingBindingElement, e MtomMessageEncodingBindingElement;
- O último e obrigatório item é o elemento de transporte. Há 8 opções disponíveis: TcpTransportBindingElement, HttpTransportBindingElement, HttpsTransportBindingElement, NamedPipeTransportBindingElement, PeerTransportBindingElement, MsmqTransportBindingElement, MsmqIntegrationBindingElement e ConnectionOrientedTransportBindingElement.
Especificando Contratos
Essa seção é uma revisão de como se define um serviço WCF:
- Defina um contrato usando o atributo ServiceContract;
- Defina as operações com o atributo OperationContract;
- Defina as classes que serão transportadas com os atributos DataContract e DataMember.
Um ponto é que o atributo ServiceContract pode ser definido tanto na classe de implementação quanto na interface, mas não em ambas. Caso isso aconteça, uma InvalidOperationException é lançada ao tentar referenciar esse serviço. O mesmo ocorre com o atributo OperationContract.
O último detalhe é que o construtor padrão do ServiceContract seta 3 propriedades: Name (com o nome da classe), Namespace (com “http://tempuri.org”) e ProtectionLevel para ProtectionLevelNone. É recomendável setar essas propriedades manualmente.
Expondo Metadados
Os atributos usados para se definir um serviço (ServiceContract e OperationContract) não expõem automaticamente os detalhes do serviço (metadados).
Você pode fazer isso definindo endpoints de metadata exchange. Atualmente há 4 bindings disponíveis: mexHttpBinding, mexHttpsBinding, mexNamedPipeBinding e mexTcpBinding (“mex” é uma combinação das palavras “metadata exchange”).
No arquivo de configuração, isso ficaria assim:
<endpoint address="" binding="basicHttpBinding" contract="Samples.WCF.Services.ITestService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
Note que o atributo contract sempre usa a interface IMetadataExchange.
Para adicionar o endpoint de metadata exchange via API, você pode usar a classe MetadataExchangeBindings. Vamos assumir que você está definindo o seguinte ServiceHost:
String uriString = "http://yourhost/Samples.WCF.Services/TestService/";
Uri[] baseAddresses = new Uri[]{new Uri(uriString)};
ServiceHost hoster = new ServiceHost(typeof(TestService), baseAddresses);
A classe MetadataExchangeBindings tem um método Create para cada binding disponível. Você adicionaria os endpoints assim:
- Hoster.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), “mexBindingHttp”);
- Hoster.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpsBinding(), “mexBindingHttps”);
- Hoster.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexTcpBinding(), “mexBindingTcp”);
- Hoster.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexNamedPipeBinding(), “mexBindingPipe”).
ServiceMetadataBehavior.MexContractName é apenas uma constante com o nome da interface IMetadataExchange.
Chegamos ao fim desse objetivo 🙂
Obrigado pela leitura e fique de olho no post do próximo objetivo, Secure a WCF service.
Até mais!