Certificação Microsoft 70-487: Objetivo 4.2 – Implement a Web API

O objetivo 4.2, Implement a Web API, é a continuação lógica do objetivo anterior com alguns tópicos mais avançados como content negotiation, HttpMessageHandler, injeção de dependência, Action Filters, streaming, etc. Vamos lá?

Manipulando Dados Em JSON

Com sua Web API retornando os dados, é interessante (e óbvio) que você construa uma interface para seus usuários trabalharem com os dados.

Ao criar uma aplicação Web API, a engine de views Razor é usada por padrão e uma view Index.cshtml é criada na pasta Views/Home, com algum layout pronto. Fazendo as alterações a seguir no HTML, a tela fica mais ou menos assim:

index.cshtml

O plano é listar todos os Customers na ul customers e os que contêm o Last Name informado pelo usuário na ul results. Tudo isso será feito via AJAX (Asynchronous JavaScript and XML), onde o browser consegue fazer requisições ao serviço Web API sem a necessidade de carregar a página inteira novamente.

O seguinte script pode ser utilizado:

O ponto principal dessa seção é que você não precisa alterar nada no serviço Web API para que ele aceite formatos em JSON ou XML. O passo mais importante no client é apenas indicar explicitamente se ele está enviando JSON ou XML pelo request header Content-Type (o método do jQuery getJSON() fez isso).

Content Negotiation

Content Negotiation (negociação de conteúdo) é o processo onde o client indica o tipo do retorno que ele deseja ao fazer uma chamada a um serviço Web API.
Isso é feito por 4 headers no request:

  • Accept: especifica o tipo do retorno do response. Para JSON o valor é application/json e para XML é application/xml ou text/xml;
  • Accept-Charset: especifica o charset. Valores comuns são UTF-8 e ISO-8859-1;
  • Accept-Encoding: indica quais encodings são aceitos;
  • Accept-Language: especifica a linguagem preferida.

Para entender melhor, vamos olhar o request e o response da chamada ao serviço FindCustomers do exemplo anterior:

Request

GET http://localhost:53366/data/Customer/FindCustomers?lastName=r HTTP/1.1
X-Requested-With: XMLHttpRequest
Accept: application/json, text/javascript, */*; q=0.01
Referer: http://localhost:53366/
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: localhost:53366
DNT: 1
Connection: Keep-Alive

Response

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?QzpcUHJvamVjdHNcTXlTb2x1dGlvblxNeVdlYkFwaVxkYXRhXEN1c3RvbWVyXEZpbmRDdXN0b21lcnM=?=
X-Powered-By: ASP.NET
Date: Mon, 15 Jul 2013 15:52:20 GMT
Content-Length: 290
[{"CustomerId":4,"AccountId":2,"FirstName":"Captain","LastName":"America"},
{"CustomerId":8,"AccountId":3,"FirstName":"Ham","LastName":"Burgler"},
{"CustomerId":11,"AccountId":4,"FirstName":"Betty","LastName":"Rubble"},
{"CustomerId":12,"AccountId":4,"FirstName":"Barney","LastName":"Rubble"}]

Nesse caso, o client fez a requisição solicitando o resultado em JSON (application/json), JavaScript (text/javascript) ou qualquer outra coisa (*/*). O servidor informou que o retorno está em JSON pelo response header Content-Type.

Caso alguma Exception acontecesse, o response estaria com o status code 500 mas o body ainda seria JSON, provavelmente com a Exception serializada.

A execução dessa negociação acontece da seguinte forma: quando o request é recebido pelo servidor, o framework obtém uma instância de IContentNegotiator do HttpConfiguration, que tem a lista dos formatters disponíveis. O método Negotiate de IContentNegotiator é chamado com 3 parâmetros (o tipo do objeto sendo serializado, a lista dos formatters e o request) e retorna o formatter a ser utilizado e o media type para o response. Se nenhum formatter for encontrado, o client recebe um response com o HttpStatusCode NotAcceptable (406).

Data Binding

Um dos aspectos importantes da Web API é o binding dos dados. As actions que você cria recebem parâmetros e é importante entender como o framework “preenche” os valores.

No caso de tipos simples como int, string, bool, DateTime, etc., os valores vêm da URL. No nosso exemplo anterior, o serviço FindCustomers recebe uma string lastName que é passada na URL como query string: /data/Customer/FindCustomers?lastName=r. Para tipo complexos (classes), o framework obtém os dados do body usando um formatter.

Você pode alterar o processo de binding padrão. O atributo FromUri, por exemplo, indica que um tipo complexo deve ser lido da URL ao invés do body:

public HttpResponseMessage Get([FromUri] Person person)

Dessa forma você chamaria esse serviço assim: /api/Persons/?FirstName=John&LastName=Doe. De maneira similar, o atributo FromBody indica que o tipo simples deve ser obtido do body.

HttpMessageHandler

Message handlers fazem parte do pipeline do framework Web API e são responsáveis por interceptar as mensagens que chegam (requests) e as que são retornadas (responses) para os clients.

Você pode implementar um message handler para efetuar alguma lógica ou fazer alguma validação antes que uma action seja chamada. Isso é possível derivando a classe System.Net.Http.DelegatingHandler e implementando o método SendAsync. Por exemplo:

Para interromper o request, basta não chamar o método base e retornar um response de falha.

Seu handler precisa ainda ser registrado no pipeline da Web API. Você pode fazer isso de duas formas.

Na primeira, você adiciona o handler nos MessageHandlers do HttpConfiguration, no método Register da classe WebApiConfig: config.MessageHandlers.Add(new LoggingMessageHandler());
Dessa forma o handler é registrado globalmente, sendo executado para todos os requests.

O outro jeito é adicionar o handler para uma rota específica, fazendo ele ser executado apenas para actions daquela rota:

Implementando Injeção De Dependência

Por motivos fora do escopo do exame, dependências suck. Tome o código a seguir como exemplo:

public string Get()
{
WebClient webClient = new WebClient();
string result = webClient.DownloadString("http://microsoft.com");
return result;
}

Seria bem melhor você ter uma interface IContentRepository com um método GetContent(string uri) e ter uma implementação WebClientContentRepository, que utiliza a classe WebClient. O controller agora ficaria assim:

Com o código desacoplado, você precisa de um mecanismo para preencher a dependência do seu controller. A interface IDependencyResolver pode ser implementada da seguinte forma:

Configurando o DependencyResolver no objeto HttpConfiguration (config.DependencyResolver = new ResolveController();), o framework já consegue resolver a dependência de IContentRepository.

Essa obviamente não é a melhor opção, pois cada nova dependência vai acarretar numa mudança na verificação do tipo do controller no método GetService. Para resolver esse problema, existem vários projetos open source que ajudam nessa implementação, e talvez uma delas caia no exame: Unity.

Ao instalar o Unity pelo NuGet, o arquivo Bootstrapper.cs é adicionado ao projeto. As únicas coisas que você precisa saber é que o método BootStrapper.Initialize deve ser chamado no Global.asax e que você registra uma dependência dessa forma: container.RegisterType();.

Action Filters E Exception Filters

Filters são métodos que podem alterar a action que está sendo executada.
Action Filter é um método que é invocado toda vez que uma action é chamada, enquanto Exception Filter é um método executado quando ocorre uma Exception não tratada.
Para implementar uma Action Filter você implementa a classe ActionFilterAttribute, e para um Exception Filter a classe ExceptionFilterAttribute.

Veja o seguinte exemplo:

Esse filtro pode ser aplicado para forçar a action a retornar XML, sobrescrevendo o comportamento padrão.

Você pode aplicar esse atributo a um controller (que vai aplicar para todas as actions) ou actions específicas. Também é possível configurar esse filtro globalmente pela propriedade HttpConfiguration.Filters.

Streaming Actions

A classe PushStreamContent pode ser usada para uma action retornar um stream, quando você precisa enviar uma quantidade muito grande de dados para um client.

Segue um exemplo de implementação. Esse código usa um timer para enviar alguns dados para os clients conectados a cada segundo. A classe PushStreamContent é inicializada com o método OnStreamAvailable:

Se você executar essa action, o request só vai terminar quando você fechar o browser. Dessa forma, você consegue enviar uma grande quantidade de dados em pacotes para o client.


E chegamos ao final desse objetivo :)
Obrigado pela leitura e fique de olho no post do próximo objetivo, Secure a Web API.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 4.1 – Design a Web API

Olá pessoal!

Hoje vamos começar o primeiro objetivo do capítulo sobre Web API, Design a Web API. Esse objetivo cobre como escolher um método HTTP apropriado, definir recursos HTTP com actions, usar rotas, escolher formatos e planejar quando usar actions assíncronas. Vamos lá?

Escolhendo Métodos HTTP

Resumindo a introdução, Web API é um serviço REST (Representational State Transfer) baseado em HTTP. Ao invés de chamar um método específico, como no WCF, com Web API você faz uma chamada para uma URL em conjunto com um método HTTP.

Segue uma lista com os métodos HTTP existentes e depois entraremos em detalhes sobre os mais importantes:

  • Delete: deleta a entidade;
  • Get: obtém os dados de uma ou mais entidades;
  • Put: atualiza a entidade inteira;
  • Patch: atualiza parcialmente a entidade;
  • Post: cria uma nova entidade;
  • Head: recupera somente os headers;
  • Options: requisita informações sobre as opções disponíveis de comunicação.

HttpGet

Geralmente o método mais usado, o HttpGet serve para obter dados de uma ou mais entidades. Ou seja, para cada entidade em um modelo, geralmente haverá um Get para obter todos os itens e outro Get para obter os detalhes de um item específico:

"/api/Foos" // obtém lista de todos os Foos
"/api/Foos/keyvalue" // obtém um Foo com a key especificada
"/api/Foos?attributename=attributevalue" // obtém um ou mais Foos com os atributos especificados

Caso nenhum valor seja encontrado, é importante lançar uma HttpResponseException com HttpStatusCode.NotFound (404) ao invés de simplesmente lançar uma Exception.

HttpDelete

Talvez o mais simples de todos, o HttpDelete simplesmente deleta uma entidade com o valor especificado, geralmente a chave. É aconselhável usar o prefixo Delete no nome do método do controller Web API.

Idealmente, há 3 possíveis retornos para um request Delete:

  • HttpStatusCode.OK (200), que indica sucesso;
  • HttpStatusCode.Accepted (202), que indica que o request foi processado e aceito mas está ainda pendente;
  • HttpStatusCode.NoResponse (204), valor padrão para métodos com retorno void

HttpPost

Para criar uma nova entidade, o método HttpPost deve ser usado. Assim como em Delete, o prefixo Post deve ser utilizado.

Em caso de sucesso, o retorno estipulado pelo protocolo HTTP 1.1 é HttpStatusCode.Created (201), mas o status code padrão é 200 (OK). É também aconselhável retornar a localização (URL) de detalhes da nova entidade criada. Seguindo esses padrões, a implementação de um HttpPost para criar uma Account seria assim:

[HttpPost]
public HttpResponseMessage PostAccount(Account account)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, account);
string newUri = Url.Link("NamedApi", new { accountId = account.AccountId });
response.Headers.Location = new Uri(newUri);
return response;
}

HttpPut

HttpPut deve ser utilizado para operações upserts, que inserem novos registros ou atualizam registros existentes. Também devem utilizar o prefixo Put.

Para atualizar uma Account, por exemplo, você deve receber um Id para procurar e uma Account para atualizar os valores. A implementação seria assim:

[HttpPut]
public HttpResponseMessage PutAccount(int id, Account account)
{
// perform insert and/or edit here.
}

Definindo Recursos HTTP Com Actions

Um serviço Web API nada mais é do que uma aplicação que usa controllers e actions para mapear URLs com ações HTTP. Esta seção foca no básico de como construir uma aplicação Web API.

Criando O Model

Voltando um pouco ao capítulo anterior, em WCF era necessário decorar suas classes com os atributos DataContract e DataMember para trafegá-las pelos serviços.

Com Web API, isso não é mais necessário. O próprio framework toma conta de enviar sua classe via HTTP, serializada em JSON ou XML. Isso evita o acoplamento entre seu modelo e o serviço que o expõe, além de facilitar os possíveis clients desse serviço pelo fato de JSON e XML serem praticamente universais.

Para os exemplos a seguir, vamos considerar o seguinte model:

Criando O Controller

Os controllers são as classes que definem os serviços da sua Web API. Eles herdam de ApiController e cada método público vira uma action, como se fosse um método no WCF.

Veja a implementação dos controllers abaixo. Seguindo o padrão REST, é criado um serviço para cada tipo no modelo:

Com isso, você já tem sua Web API funcionando. Basta acessar http://localhost:{porta}/api/Account e a lista de Accounts é retornada. Chamando /api/Account?accountId=1, a Account com o Id 1 é retornada.

Um ponto importante é que, chamando essas URLs diretamente no browser, o comportamento é definido pelo próprio browser. O Chrome e o Firefox, por exemplo, mostrariam um XML na tela, enquanto o Internet Explorer faria o download de um JSON. Isso varia do request header Accept que o browser envia para a Web API.

Definindo Rotas

Toda a mágica da Web API de tornar métodos públicos em um controller acessíveis por clients acontece por causa da Routing Table.

Routing Table é um classe que implementa System.Web.Http.Routing.IHttpRoute e tem a tarefa de mepear um request para um controller e uma action específicos. No caso de Web API, a classe se chama WebApiConfig e fica dentro de App_Start:

Se você mudar o routeTemplate para "data/{controller}/{id}", por exemplo, você agora acessa sua api com http://localhost:{porta}/data/Account.

Quando uma requisição é feita para uma Web API, o framework tenta achar o controller e a action segundo o que está definido na Routing Table. Se não encontra, uma HttpStatusCode 404 é retornado. Caso contrário, o request é encaminhado para o método. As seguintes ações são tomadas pelo framework para encontrar o controller e a action:

  • Para achar o controller, a Web API adiciona a palavra Controller na variável do controller (por isso você chama diretamente Account, por exemplo);
  • Para a action, a Web API examina o método HTTP do request e tenta achar uma action com o nome correspondente;
  • Por padrão, isso só funciona com os métodos GET, POST, PUT e DELETE. Outro métodos são suportados, mas outro mecanismo é utilizado;
  • Por último, as variáveis no routeTemplate são mapeadas para os parâmetros da action (por padrão, id).

Você pode setar explicitamente um método HTTP em uma action. Isso é importante quando você quer que o nome do método seja diferente do método HTTP. Para fazer isso, basta decorar o método com o atributo System.Web.Http.ActionMethodSelectorAttribute. Ele possui 4 valores: HttpGet, HttpPost, HttpDelete e HttpPut. O método ficaria assim:

[HttpGet]
public Account GetAccount(int accountId)

Para definir múltiplos métodos HTTP para uma action ou suportar um método HTTP fora os 4 citados, você usa o atributo System.Web.Http.AcceptVerbsAttribute. O construtor aceita um array de strings que correspondem aos métodos que devem ser mapeados. O uso ficaria assim:

[System.Web.Http.AcceptVerbs("GET", "HEAD")]
public IEnumerable GetCustomers(int accountId)

Alterando Actions

Se você quiser criar as rotas com o nome da ation explícito, basta mudar a Routing Table para isso. O código ficaria assim:

Com isso, ao invés de acessar /api/Customer/1, você acessa /api/Customer/GetCustomers/1.

Você pode alterar o nome da action pelo atributo ActionNameAttribute:

[HttpGet]
[ActionName("FindCustomers")]
public IEnumerable SearchCustomers(string lastName)

Assim, você acessa essa action com a URL /api/Customer/FindCustomers?lastName=r.

O último ponto é que você pode querer que um método público no controller não seja uma action acessível pelo serviço. Para isso, decore o método com o atributo NonAction.

Escolhendo Formatos

O request header Accept indica o formato que o client espera como retorno. Acessando sua Web API pelo browser, geralmente o seguinte header é enviado:

Accept: text/html,application/xhtml+xml,application/xml

Por padrão, a Web API trabalha com JSON e XML. Isso é configurado por um objeto chamado media-type formatter.

Para essa seção, basta você saber isso e que você pode adicionar formatters customizados implementando a classe MediaTypeFormatter ou BufferedMediaTypeFormatter.

Actions Assíncronas

Ao fazer um request para um serviço Web API, uma thread é alocada do servidor para fazer o processamento necessário.
Em cenários de grandes processamentos de I/O (entrada/saída), como acesso a banco de dados e chamadas a outros serviços, é importante liberar a thread atual para que ela processe mais requests enquanto essa entrada/saída longa é executada.

O C# 5 nos deu mecanismos para fazer isso de forma muito simples: as palavras-chave async e await. Tome por exemplo a seguinte action de uma Web API:

public string Get()
{
WebClient webClient = new WebClient();
string result = webClient.DownloadString("http://microsoft.com");
return result;
}

Na maior parte do tempo, a thread alocada fica esperando o processamento I/O de fazer o request para o site da microsoft e buscar o HTML. Olhe essa action com async/await:

public async Task<string> Get()
{
WebClient webClient = new WebClient();
string result = await webClient.DownloadStringTaskAsync("http://microsoft.com");
return result;
}

Na chamada a await webClient.DownloadStringTaskAsync, a thread alocada é liberada para processar outros requests enquanto a requisição ao site da Microsoft é feita.

Um último alerta é que chamadas assíncronas devem ser feitas apenas para processamentos I/O. Se o método assíncrono fizer um processamento de CPU, outra thread terá que ser alocada para fazer esse processamento, o que invalida quaisquer benefícios da programação assíncrona (podendo até piorar devido à troca de threads).


Chegamos ao fim desse objetivo :)

Obrigado pela leitura e espere pelo próximo post sobre o objetivo 4.2, Implement a Web API.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.9 – Host and manage services

Olá!
Esse post será sobre o último objetivo do capítulo de WCF, Host and manage services.
Esse objetivo cobre como gerenciar simultaneidade, escolher um Instance Mode, criar hosts, escolher um mecanismo de hosting, criar serviços transacionais e hostear em um Azure Worker Role.

Gerenciando Simultaneidade

É bem provável que seu serviço seja consumido por mais de um consumidor ao mesmo tempo. Com isso, é necessário tentar prever e gerenciar a simultaneidade que seu serviço suportará.

Dentro de ServiceBehavior, você pode definir o ConcurrencyMode: [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single)]

O enum ConcurrencyMode possui 3 valores:

  • Single: em qualquer momento, somente 1 request tem acesso ao serviço WCF por vez. Caso vários requests sejam feitos simultaneamente, todos vão esperar até que o anterior complete seu processamento;
  • Multiple: como o nome sugere, múltiplos requests podem ser feitos simultaneamente em threads separadas;
  • Reentrant: funciona como o Single, mas se você fizer uma chamada a outro serviço, o lock é liberado para que o próximo request seja processado.

Escolhendo um Instance Mode

Tratando ainda sobre simultaneidade, a configuração InstanceContextMode especifica o número de instâncias do serviço para processar os requests. Também tem 3 valores e, junto com ConcurrencyMode, dão 9 possíveis combinações.

Assim como ConcurrencyMode, você pode setar essa propriedade no atributo ServiceBehavior. Os 3 valores são:

  • Single: uma única instância do serviço é usada para todos os requests;
  • PerSession: cria uma ou mais instâncias para cada client. É a configuração padrão;
  • PerCall: cria uma ou mais instâncias para cada request.

Esse é um assunto bem complexo e o comportamento de cada combinação aparentemente está fora do escopo do exame. Se quiser ir a fundo no tema, há um bom artigo no codeproject.

Criando Hosts

Há várias opções de host para um serviço WCF. Vamos passar pelas opções disponíveis (ou prováveis de estar no exame) resumindo os pontos positivos e negativos.

Self Hosting

Serviços WCF podem ser hospedados em qualquer aplicação .NET. Você especifica a configuração do serviço em um arquivo de configuração ou no código em conjunto com instâncias da classe ServiceHost.

Há vários benefícios dessa forma. Como você pode facilmente trocar de hosts, se sua aplicação não estiver performando bem, você pode fazer upgrade para outro host ou habilitar outros bindings. Fazer debug da aplicação também fica facilitado.

A principal desvantagem é que você deve gerenciar todo o host, consertando quaisquer bugs que apareçam. Mover o host para outra máquina ou fazer um upgrade de hardware, se for necessário, também é doloroso. Por isso são geralmente usados para ambiente de desenvolvimento, e não produção.

Managed Windows Services

Não muito diferentes de outras aplicações .NET, Managed Services são parecidos com Self Hosting, mas com mais poder. Se a máquina reiniciar, por exemplo, o serviço pode startar automaticamente, sem a necessidade de um usuário logado. Também não é necessário usar IIS ou WAS para rodar na máquina.

Como desvantagens, você ainda precisa escrever código para criar o host com a classe ServiceHost e existe limitação para scale up e scale down. Services também tendem a ser mais difíceis para debug.

Internet Information Services (IIS)

O IIS, conhecido por hospedar aplicações ASP.NET, também pode ser usado para hospedar um serviço WCF.

As vantagens são todas as features que o IIS tem, como isolamento de pools de aplicações, recycling, desligamento por ociosidade, monitoramento, etc. Se o serviço precisar de alta disponibilidade e ser escalável, IIS é a escolha ideal.

A desvantagem que pode deixá-lo inutilizável para alguns serviços é que ele só suporta bindings que fazem o transporte por HTTP.

Windows Activation Service (WAS)

WAS, apesar de presente na maioria das grandes organizações, não é tão conhecido como o IIS.

Ele provê todos os benefícios do IIS e suporta outros tipos de transporte, como TCP, named pipes e MSMQ.

A desvantagem é que, por ser uma tecnologia nova, há pouco suporte e material sobre ela.

Windows Azure

Hospedar seu serviço na nuvem possui várias vantagens. A principal é que toda a dor de cabeça de manter um servidor com um serviço rodando fica com a Microsoft, e não com você.

Por enquanto, é suficiente saber que há 3 principais opções no Azure: WebRole, AppFabric service bus e WorkerRole.

A desvantagem pode ser que sua aplicação está hospedada por outras pessoas, o que tira o seu controle de ter um servidor on premise. Usando Azure, você ainda terá que fazer a escolhe entre self-host, IIS ou WAS.

Classe ServiceHost

Criar uma instância de ServiceHost é simples. Você chama o construtor passando o tipo do serviço, que deve estar referenciado. Há vários overloads do construtor, mas não são prováveis de estarem no exame. Após isso, você chama o método Open e por fim Close, ambos num try/catch pois várias coisas podem dar errado nesse tempo.

using (ServiceHost HostInstance = new ServiceHost(typeof(TestServiceType)))
{
HostInstance.Open();
//Put in a Wait Condition so Host stays open
HostInstance.Close();
}

Após o canal ser aberto por um client, o comportamento depende das definições do serviço (ConcurrencyMode, InstancingMode, tipos de endpoint, etc.). Um ponto importante para o client é que, caso haja algum erro não tratado, todo o canal é considerado falho e inválido para novas operações. Sempre deve haver a validação se a conexão está válida.

É importante também entender as opções de host. Como serviços WCF podem ser hospedados em qualquer AppDomain, seja uma aplicação Windows Form ou Console, se o AppDomain não estiver ativo, o host também não estará.

Ao usar um mecanismo de hosting como IIS ou WAS, não é necessário criar um ServiceHost. Basta configurar o serviço e gerenciar pela ferramenta.

Há outro ponto que possivelmente esteja no exame. Antes do .NET 4.0, se você quisesse usar o IIS para hospedar seu serviço, você tinha que criar uma aplicação ASP.NET com uma referência ao seu serviço, colocar as configurações no Web.config e criar um arquivo de texto com a extenção .svc (com uma única linha) para mapear o serviço.

Com a versão 4.0, isso não é mais necessário. Você pode usar a classe ServiceHostingEnvironment, que permite que você referencie o arquivo .svc virtualmente, sem a necessidade de criá-lo fisicamente. A configuração via arquivo fica assim:

<serviceHostingEnvironment>
<serviceActivations>
<add relativeAddress="ServiceOrInterfaceName.svc" service="FullyQualifiedServiceName"/>
</serviceActivations>
</serviceHostingEnvironment>

Escolhendo Um Mecanismo De Hosting

Resumindo o que vimos até agora, você tem várias opções para hospedar um serviço WCF:

  • Windows Form application
  • Console application
  • Managed Windows Service
  • ASP.NET application
  • IIS
  • WAS
  • Windows Azure

Não existe regra para fazer essa escolha ou uma opção que atenda todos os cenários. Se seu serviço demandar que o transporte não seja feito por HTTP, por exemplo, o IIS não é uma opção. Ou se você precisar usar um binding MSMQ, você vai precisar de um host que dê suporte a isso.

Falando no geral, procure uma solução que te dê menos trabalho. IIS e WAS são produtos confiáveis, com uma base de usuários grande e fáceis de usar em conjunto com WCF. Escrever um novo host fatalmente irá gerar bugs ao longo do caminho.

Criando Serviços Transacionais

No objetivo 1.3, falamos sobre a classe TransactionScope. Ela também funciona de maneira bem simples com serviços WCF.

Você precisa decorar a operação com o atributo TransactionFlow (em conjunto com o atributo OperationContract), que especifica se a operação suporta transações. O construtor aceita 3 valores:

  • [TransactionFlow(TransactionFlowOption.NotAllowed)]: a operação não pode participar de uma transaction;
  • [TransactionFlow(TransactionFlowOption.Allowed)]: a operação pode participar de uma transaction, se o client iniciar uma;
  • [TransactionFlow(TransactionFlowOption.Mandatory)]: obrigatoriamente a chamada deve fazer parte de uma transaction.

Além disso, você deve setar as propriedades TransactionAutoComplete e TransactionScopeRequired do OperationBehavior como true:

[OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)]
public String GetTestSummary(String examId){}

TransactionAutoComplete diz ao runtime para automaticamente completar a transação se nenhuma exceção for lançada. Caso seja, será feito o rollback. O atributo TransactionScopeRequired diz que o método precisa fazer parte de uma TransactionScope.

Hospedando Serviços Em Um Azure WorkerRole

Hospedar seu serviço no Azure em um WorkerRole significa ter mais controle sobre o servidor e as configurações do seu serviço, permitindo que você não use o IIS como hosting, por exemplo.

Com isso, é de sua responsabilidade configurar endpoints, inicializar e abrir um ServiceHost. Isso pode ser feito no método OnStart.

As configurações de endpoint podem estar no arquivo de configuração da role (.csdef). O código fica assim:

É interessante deixar a variável de host fora do método para que o garbage collector não a remova do escopo quando o método terminar.

O último detalhe desse objetivo, não relacionado a nenhum item, é que você deve saber que existe um DiscoveryEndpoint que faz com que seu serviço seja descoberto por clients.


E acabamos o capítulo 3 sobre serviços WCF 😀 Por ser o maior objetivo da certificação, o assunto ficou bem extenso e chato, às vezes.

Mas agora vem o capítulo 4 com os objetivos sobre Web API, que é o mais comum nas aplicações hoje em dia (e bem mais elegante).

O próximo objetivo será o 4.1, Design a Web API.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.8 – Implement messaging patterns

Olá pessoal!
O objetivo 3.8, Implement messaging patterns, trata de Message Exchange Patterns (ou Padrões de Troca de Mensagem), que são mecanismos que descrevem como o client e o server vão se comunicar.
Vamos cobrir como implementar os tipos one-way, request/reply, streaming e duplex, além de falar sobre alguns mecanismos no Azure. Vamos lá?

Implementando One-Way, Request/Reply, Streaming e Duplex

One-Way

Implementações one-way servem para cenários fire and forget, onde você faz uma chamada e não se importa com a resposta, como escrever alguma coisa em um log.

O WCF suporta implementações one-way de forma simples. Basta setar o parâmetro IsOneWay como true no atributo OperationContract e não ter nenhum tipo de retorno no método:

[OperationContract(IsOneWay = true)]
void LogMessage(String message);

Num cenário comum, um request é feito para o servidor que processa e envia um response. Com OneWay, acontece o seguinte: quando o request é feito, o servidor responde instantaneamente com o StatusCode 202, Accepted. Só depois disso ele vai de fato processar o request, como se fosse uma operação assíncrona.

Apesar de ser uma boa feature, OneWay deve ser usado com cuidado. Operações críticas podem dar errado sem que você saiba, causando problemas. Bons candidatos a OneWay são operações de baixa prioridade e que não retornam valor.

Streaming

Ao implementar streaming com WCF, você tem dois modos para trabalhar: Buffered ou Streaming.

Buffered, o modo default, significa que a mensagem inteira precisa ser enviada para que o recebedor comece a leitura. Em Streaming, o recebedor consegue começar a leitura assim que a informação estiver disponível (cenário ideal para transferência de arquivos grandes, como áudio ou vídeo).

Nesse formato, uma propriedade importante é a maxReceivedMessageSize, que limita o tamanho de mensagens recebidas. Isso é crucial para evitar ataques DDoS.
No nível do contrato, você começa o processo definindo o retorno de um método como Stream. Depois é necessário configurar o binding para que o TransferMode seja Streamed e aí configurar o atributo maxReceivedMessageSize. O arquivo de configuração ficaria assim:

Há 4 valores para o enum TransferMode, com nomes bem sugestivos: Buffered, Streamed, StreamedRequest e StreamedResponse.

Um ponto para se ter em mente é que habilitar streaming para um serviço não vai automaticamente melhorar a performance. É verdade que com streaming não são necessários grandes buffers de memória, mas num contexto comum de transferência de informações a melhora de performance é imperceptível.

Outro aspecto é que nem todos os bindings suportam a propriedade TransferMode. Os únicos bindings padrão que aceitam são BasicHttpBinding, NetTcpBinding e NetNamedPipeBinding.

Por último, há algumas limitações ao se usar streaming. São elas:

  • Operações que usam o transporte Streamed só podem ter um input e um output de parâmetro;
  • Headers SOAP são sempre buffered, mesmo habilitando streaming. Eles não podem ultrapassar o tamanho da propriedade MaxBufferSize;
  • As implementações de segurança Reliable Message e SOAP Message confiam na transmissão buffered. Geralmente você deve usar segurança no nível de transporte (objetivo 3.4).

Request/Reply

É o modo padrão de funcionamento de qualquer web service, onde o client envia um request, o server processa e envia um response. Não há muito a mais o que ser dito 😀 Vamos para o próximo item.

Duplex

Na comunicação duplex, o client se conecta com um serviço provendo um canal de comunicação, onde o server pode mandar mensagens de volta para o client. Para funcionar, tudo isso precisa existir no contexto de uma sessão.

Nesse cenário com WCF, além de definir um contrato de ServiceContract, outra interface é necessária para especificar a propriedade CallbackContract, que serve como callback. Para o exame, não é necessário saber mais que isso existe.

Implementando Azure Service Bus E Azure Queues

Como visto no objetivo anterior, o Azure Service Bus é um componente que conecta serviços WCF (e outras aplicações) pelo Azure.

Usando relay, a comunicação é feita diretamente entre as duas partes. Isso implica que ambas precisam estar online para a comunicação acontecer.
Às vezes, você precisa de uma fila de comunicações ou usar um mecanismo de publish/subscribe. O Azure também oferece essas tipos de comunicações.

Uma Queue (fila) permite que mensagens enviadas por um client sejam armazenadas até que outro client as leia. Um Topic extende o comportamento da queue adicionando subscriptions, onde cada client especifica critérios para filtrar as mensagens que vê. Como Topics não estarão no exame, vamos focar na Queue.

Quando usando uma Queue, você manda uma mensagem para o service bus com um conjunto de propriedades chave/valor e um body binário. A mensagem é armazenada pelo service bus e entregada a um recebedor em um determinado momento, desacoplando as duas (ou mais) aplicações. O recebedor pode remover o item da fila na leitura ou colocar um lock, processar a mensagem e depois removê-la (como o Storage Queue faz).

Questões sobre a implementação não são prováveis de estar no exame, então o conhecimento do mecanismo já é suficiente. Você pode obter mais detalhes aqui.

O conceito importante é o uso da classe QueueClient que permite o envio e recebimento de mensagens da queue. A mensagem é representada pela classe BrokeredMessage.

E chegamos ao final desse objetivo.
O próximo post, sobre o objetivo Host and manage services, será o último do capítulo sobre WCF.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.7 – Create and configure a WCF service on Windows Azure

O objetivo Create and configure a WCF service on Windows Azure (finalmente sobre Azure) fala principalmente sobre o Azure Service Bus, serviço que permite que você exponha seu serviço WCF (ou qualquer outro) hospedado no seu próprio datacenter. Isso evita os problemas de instalação e configuração de deixar tudo on premises e te dá as vantagens que o Azure pode oferecer. Vamos lá?

Criando E Configurando Bindings

Apenas revisando, um serviço WCF basicamente precisa do ABC (address, binding e contract). Para fazer o deploy de um serviço WCF no Azure em uma Web Role ou Web Site você não precisa fazer nenhuma modificação no código ou nas configurações.

Usando a feature de relay do Azure Service Bus, você pode configurar alguns bindings adicionais no seu projeto adicionando o pacote NuGet Windows Azure Service Bus.

Considere o código a seguir, assumindo que ISecretService e SecretService formam o contrato e a implementação de um serviço:

Nesse exemplo, o serviço está exposto em dois endpoints, um interno (com NetTcpBinding) e um externo, usando a feature de relay do Azure Service Bus (com NetTcpRelayBinding). Ao fechar a aplicação (sh.Close()), o endpoint é removido do Azure.

Você pode configurar também o endpoint no Azure pelo arquivo de configuração:

Nos dois exemplos o binding NetTcpRelayBinding é usado, mas há outros disponíveis.
Tanto por código ou arquivo de configuração, você deve setar o nome do serviço e a Default Key.
Outro ponto interessante é que o Azure Service Bus não requer nenhuma configuração no seu servidor on premise. Normalmente a comunicação é feita por TCP, mas se as portas não estiverem disponíveis, o relay pode usar as portas HTTP ou HTTPS.

Criando Um Azure Service Bus

Para criar um Azure Service Bus, basta ir no portal do Azure, selecionar a criação de um Azure Service Bus, configurar um namespace e selecionar uma região.

O portal vai informar a connection string de conexão, que contém o nome do serviço e a Default Key:

Endpoint=sb://wcfonazure.servicebus.windows.net/;SharedSecretIssuer=owner;SharedSecretValue=D1Kmb83CCLrAtz4du9vb3xn99DaM2dH4jQqSec98if4=

Integrando Com O Azure Service Bus Relay

Após criar um novo service bus no Azure e hospedar um serviço WCF, você pode criar um client para se comunicar com seu serviço on premise pelo Azure.

É necessário ter o namespace especificado, o nome do serviço e a Default Key. O código a seguir mostra como o consumo pode ser feito via ChannelFactory:

O método CreateSharedSecretTokenProvider se autentica no service bus pelo owner e a key, mas há outros mecanismos de autenticação como SAML ou SimpleWebToken.

Isso é tudo :)
O próximo post será sobre o objetivo 3.8, Implement messaging patterns.

Até lá!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.6 – Version a WCF service

Olá pessoal!
Continuando a série sobre a certificação Microsoft 70-487, esse post será sobre o objetivo 3.6, Version a WCF service.
Esse objetivo, que é bem curto, cobre os conceitos sobre como versionar diferentes tipos de contratos e informações de bindings.

Várias mudanças podem ocorrer no ciclo de vida de um serviço WCF: no contrato, no endereço (address), nos bindings, na implementação de algum método, etc. Essas mudançar podem ser classificadas em breaking changes ou Nonbreaking changes.

Há 2 tipos de versionamento: strict e lax.

O versionamento strict é quando você tem um ou mais clients que não toleram mudanças no serviço. Nesse caso, todas as mudanças precisam levar isso em conta e você nunca deve modificar algum comportamento; apenas adicionar em outras versões ou namespaces.

No versionamento lax, suportado pelo WCF e até web services .asmx, você pode fazer pequenas alterações sem quebrar o contrato existente. É possível, por exemplo, adicionar um novo membro fazendo com que os dados sejam somente ignorados por clients antigos.

Esse objetivo é bem conceitual e saber quando uma mudança vai quebrar ou não um serviço é suficiente para o exame. Mudanças no contrato são breaking changes; adições no DataContract podem ser ignoradas por clients antigos e não vão quebrar a funcionalidade.

E é isso :)
O próximo objetivo, também curto, será o Create and configure a WCF service on Windows Azure.

Até lá!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.5 – Consume WCF services

Olá!
Hoje vamos falar sobre o objetivo 3.5 da certificação 70-487, Consume WCF services.
Esse objetivo cobre como consumir um serviço WCF de 3 formas: gerando classes proxy com Svcutil.exe, gerando classes proxy adicionando uma referência ao serviço e consumindo via ChannelFactory. Vamos lá?

Gerando Proxies Com Svcutil.exe

É possível consumir serviços WCF gerando uma classe proxy e interagindo com ela como se fosse uma classe comum, deixando o framework fazer toda a comunicação por trás.

O Svcutil.exe, uma ferramenta chamada pela linha de comando, é uma das formas de gerar classes proxy de um serviço WCF. É possível fazer a geração baseado em serviços existentes ou metadata.

A ferramenta também permite que você exporte metadata de um serviço, valide código de um serviço, faça download dos metadatas de serviços existentes e gere código de serialização.

A melhor maneira de estudar para o exame é criar alguns serviços e gerar as proxies chamando o Svcutil.exe com opções diferentes, analisando o que a ferramenta de fato faz. A lista abaixo traz as opções importantes para o exame ao chamar a ferramenta pela linha de comando (atalhos estão entre parênteses):

  • /directory:<directory> (/d): especifica o diretório em que os arquivos gerados serão colocados. Se não for especificado, o diretório atual será usado. Caso o diretório já tenha sido usado, o Svcutil.exe irá sobrescrever os arquivos;
  • /mergeConfig: caso o diretório já tenha sido usado, ao invés de sobrescrever os arquivos, com essa opção o Svcutil.exe irá fazer o merge das configurações;
  • /help (/?): lista as opções disponíveis;
  • /noLogo: tira as informações Logo (copyright e mensagem de banner);
  • /svcutilConfig:<configFile>: coloca as configurações no arquivo especificado. O default é o App.config;
  • /target:<output type> (/t): instrui a ferramenta a criar um output específico. As opções são code, metadata e xmlSerializer;
  • /async (/a): cria todos os métodos das classes proxy com a versão normal e uma Async;
  • /internal (/i): cria todos os métodos das classes proxy como internal, e não public;
  • /serializer (/ser): gera os tipos com um serializer específico. As opções são Auto, DataContractSerializer e XmlSerializer;
  • /out (/o): especifica o nome do arquivo para o código gerado;
  • /language (/l): especifica uma linguagem para o código gerado. As opções são c#, cs, csharp, vb, visualbasic, c++ e cpp.

Gerando Proxies Por Referência

Uma feature do Visual Studio é a possibilidade de gerar classes proxy adicionando uma referência a um serviço.
Basta clicar com o botão direito no seu projeto e ir em Add Service Reference. A seguinte tela é mostrada:

add service

Você pode prover uma URL de um serviço (com metadata habilitado) ou clicar no botão Discover para encontrar services que estão na mesma solution. Após clicar em Go, os serviços são listados e você pode especificar um namespace, o que é recomendado.

Ao clicar em Advanced abaixo, a tela Service Reference Settings é aberta:

service reference settings

No topo você pode especificar o modificador de acesso e se métodos async devem ser gerados, como as opções /internal e /async usando Svcutil.exe.
A opção Always Generate Message Contracts faz o que o nome diz, gera classes para os contratos.

Na seção Data Type, você pode definir o tipo das coleções. Nada te impede de usar generics (List<T>), mas geralmente arrays são utilizados para manter a compatibilidade entre diferentes clients que não sejam .NET.

A última seção, Compatibility, serve se você estiver consumindo um web service antigo .asmx. A tela Add Web Reference é aberta:

add web reference

Criando E Implementando ChannelFactories

A classe ChannelFactory serve para fazer comunicações com endpoints de forma simples. Ela pode atuar em conjunto com o arquivo de configuração da sua aplicação, fazendo com que uma chamada a um serviço tenha 2 linhas de código.

A primeira coisa que você deve saber é que a classe ChannelFactory trabalha com o contrato do serviço, então você deve ter uma referência à interface que especifica o contrato.

Construtores

O construtor padrão não aceita nenhum parâmetro, então informações sobre o endpoint precisam ser especificadas: new ChannelFactory().

O próximo aceita uma string com o nome de configuração do endpoint:

String endpointConfigName = "wsHttp_BindingConfig";
ChannelFactory<ITestService> proxy = new ChannelFactory<ITestService>(endpointConfigName);

Outro aceita um binding, mas funciona da mesma forma:

String endpointConfigName = "wsHttp_BindingConfig";
WSHttpBinding wSBinding = new WSHttpBinding(EndpointConfigName);
ChannelFactory<ITestService> proxy = new ChannelFactory<ITestService>(wSBinding);

O próximo aceita um ServiceEndpoint:

ServiceEndpoint endpoint = new ServiceEndpoint(new ContractDescription("Endpoint"));
ChannelFactory<ITestService> proxy = new ChannelFactory<ITestService>(endpoint);

Há outros construtores que são bem intuitivos. Vamos a um cenário real de uso da classe ChannelFactory, algo bem provável de conter pelo menos 1 questão no exame:

O método CreateChannel inicializa a ChannelFactory, retornando o tipo do contrato especificado no construtor. Depois disso, basta chamar os métodos desejados que estão no contrato. Por fim, chame o método Close. Ele possui uma chamada padrão sem parâmetros e uma recebendo um TimeSpan, onde você indica que se o canal não for fechado nesse tempo uma exceção é lançada.

Por último, é importante destacar que com ChannelFactory você não precisa da classe de definição do serviço, apenas a interface do contrato.

Isso é tudo para esse objetivo :)
Obrigado pela leitura e fique de olho no post do próximo objetivo, Version a WCF service.

Até mais!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.4 – Secure a WCF service

O objetivo 3.4 do exame, Secure a WCF service, cobre as features de segurança que o WCF disponibiliza. Isso inclui 1) como implementar segurança no nível de mensagem, 2) como implementar segurança no nível de transporte e 3) como implementar certificates, que é a combinação dos itens anteriores. Vamos lá?

Implementando Segurança no Nível de Mensagem

Basicamente, implementar segurança no nível da mensagem é “incrementar” as mensagens SOAP com 3 características CIA:

  • Confidencialidade: significa que somente as pessoas que devem ver uma mensagem são as pessoas que de fato vejam a mensagem;
  • Integridade: significa que a mensagem não pode ser alterada;
  • Autenticação: garante a identidade de quem está vendo a mensagem.

Isso significa que a mensagem não é visualizada sem a chave correspondente, mas o texto criptografado pode ser visto na rede.

Na maioria dos bindings, isso é feito por padrão e roda transparentemente, sem intervenção do desenvolvedor. Você pode também especificar via código ou arquivo de configuração.

Por código, basta setar a propriedade Security.Mode do binding desejado, seja por construtor ou setando a propriedade manualmente:

// Set Security Mode to Message in Constructor
WSHttpBinding WsHttpSecurity = new WSHttpBinding(SecurityMode.Message);

// Set the Security property manually
WSHttpBinding wsHttpSecurity2 = new WSHttpBinding();
wsHttpSecurity2.Security.Mode = SecurityMode.Message;

A propriedade Mode para o wsHttpBinding é do tipo SecurityMode. Para o basicHttpBinding o tipo é BasicHttpSecurityMode e para o netNamedPipeBinding é NetNamedPipeSecurityMode.

É possível fazer essa configuração no arquivo de configuração pelo WCF Service Configuration Editor. Para cada binding configurado, há duas abas no editor: Binding e Security. Basta ir na aba Security e setar o Mode para Message:

security mode

Se você for para o arquivo de configuração, verá que nada foi adicionado pois o wsHttpBinding faz essa segurança por padrão. Contudo, é possível declarar também explicitamente dessa forma:

<wsHttpBinding>
<binding name="wsHttpBindingConfigSample" >
<security mode="Message"/>
</binding>
</wsHttpBinding>

Implementando Segurança no Nível de Transporte

Transporte seguro significa que todo o transporte é criptografado, tornando difícil para um interceptor compreender a mensagem. Com isso, o texto da mensagem não precisa ser criptografado; pode ser trafegado em plain text.

Implementar segurança no nível de transporte é virtualmente idêntico ao item anterior. A diferença é que, ao invés de setar o Mode como Message, você deve escolher Transport ou TransportWithMessageCredential

Seja via código ou arquivo de configuração, o funcionamento é exatamente o mesmo.

Implementando Certificates

Para usar certificados, você precisa adicionar uma referência a System.Security.dll. Com o certificado instalado, a configuração no WCF é bem simples.

Assumindo que você tenha um netHttpBinding configurado para o serviço, basta sincronizar os elementos <security> e <message> para que o Mode da propriedade Security seja TransportWithMessageCredential e a propriedade ClientCredentialType da mensagem seja o certificado.

Após essa configuração, basta garantir que certificado está instalado no client, obter uma referência a ele e adicioná-lo na propriedade Credentials.ClientCredentials.Certificate:

var MyFactory = new ChannelFactory("*");
MyFactory.Credentials.ClientCredentials.Certificate = X509.CurrentUser.My.SubjectDistinguishedName.Find("CN=CLIENT").FirstOrDefault();

Isso é tudo para esse objetivo :) O próximo post será sobre o objetivo 3.5, Consume WCF services.

Até lá!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.3 – Configure WCF services by using the API

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:

request envelope

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:

reference

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:

  1. O item mais externo é o TransactionFlowBindingElement. É opcional, necessário somente se você quiser suportar Flowing Transactions;
  2. ReliableSessionBindingElement, também opcional, indica se Reliable Sessions devem ser suportadas;
  3. SecurityBindingElement define funcionalidades de segurança como autorização, autenticação, proteção, confidencialidade, etc. Também opcional;
  4. O próximo item da cadeia é o CompositeDuplexBindingElement, caso comunicações duplex sejam suportadas;
  5. O elemento OneWayBindingElement pode ser usado se você quiser prover comunicações OneWay para o serviço;
  6. Dois elementos podem ser implementados para prover Stream Security: SslStreamSecurityBindingElement e WindowsStreamSecurityBindingElement. Implementação opcional;
  7. 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;
  8. 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!

Leia Mais

Certificação Microsoft 70-487: Objetivo 3.2 – Configure WCF services by using configuration settings

Vários comportamentos importantes de um serviço WCF podem ser configurados por arquivos de configurações. Esses arquivos são muito úteis na construção de uma aplicação, seja web ou WCF. Você pode controlar quase qualquer aspecto de um serviço sem a necessidade de rebuildar e redeployar a aplicação. O objetivo 3.2, Configure WCF services by using configuration settings, mostra que o exame vai cobrar conhecimentos sobre as possíveis configurações e como manipulá-las, no formato de drag and drop. Vamos lá?

Configurando Comportamentos do Serviço

O Visual Studio oferece uma ferramenta chamada WCF Service Configuration Editor, disponível no menu Tools. Ela permite a manipulação de arquivos de configuração visualmente, suportando até a criação de serviços. Segue abaixo o guia básico da criação e configuração de um serviço WCF:

Criando Um Serviço

Você pode escolher a opção Create a New Service, se ainda não tiver um serviço criado:

create a new service

A primeira coisa que o wizard faz é adicionar um elemento e um no arquivo de configuração. Depois, apresenta a página New Service Element Wizard para você selecionar o assembly que contém o serviço:

select assembly

Após especificar o serviço, você precisa especificar os contratos correspondentes:

select contract

Depois, o wizard prossegue para a definição da comunicação do serviço:

select communication mode

Depois, o modo de interoperabilidade (básica ou avançada):

select interoperability

Por último, o wizard pede o endereço do endpoint e apresenta uma página que sumariza todas as informações dadas. O importante aqui é como o arquivo de configuração ficou após esses passos:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="Samples.WCF.Services.TestService">
<endpoint address=http://www.williamgryan.mobi/samples/487
binding="wsDualHttpBinding" bindingConfiguration="" name="DualHttp"
contract="Samples.WCF.Services.ITestService" />
</service>
</services>
</system.serviceModel>
</configuration>

Você pode notar, pelo nome do nodo services, que mais de um serviço pode ser especificado no mesmo projeto WCF. O atributo Name é o tipo (classe) do serviço, e ele precisa ser encontrado no caminho especificado. No elemento endpoint, o ABC é incluído: address, binding e contract.

Expondo Metadados

Endpoints de metadados começam com o prefixo mex, abreviação para metadata exchange. Atualmente há quatro tipos de endpoints: mexHttpBinding, mexHttpsBinding, mexNamedPipeBinding e mexMsmqBinding. Cada um corresponde para o binding do serviço, mas você pode expor todos se necessário.

Vamos alterar o serviço acima para o binding wsHttpBinding e adicionar um endpoint de metadados mexHttpBinding. O XML de configuração ficaria assim:

<service name="Samples.WCF.Services.TestService">
<endpoint address="http://www.williamgryan.mobi/samples/487"
binding="wsHttpBinding" bindingConfiguration="" name="WsHttp"
contract="Samples.WCF.Services.ITestService" />
<endpoint binding="mexHttpBinding" bindingConfiguration=""
name="Mex" contract="IMetadataExchange" />

Com isso, a troca de metados está habilitada, permitindo que os clients vejam o WSDL para obter informações do serviço.

Configurando ServiceBehaviors e EndpointBehaviors

Você especificar comportamentos (behaviors) específicos que habilitam opções avançadas de segurança ou performance, por exemplo. EndpointBehaviors habilitam a configuração dos endpoints, como clientCredentials, o batch de transação, o serializer do DataContract, e muito mais. ServiceBehaviors controlam aspectos do serviço em um nível granular.

Usando o WCF Service Configuration Editor, basta ir para o nodo Advanced, depois Service Behaviors ou Endpoint Behaviors e adicionar um novo behavior. A tela seguintes mostra uma lista dos behaviors disponíveis:

select behaviors

Basta selecionar os behaviors desejados e clicar em Add. O XML ficará parecido com o seguinte:

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="Debug">
<serviceDebug />
</behavior>
</serviceBehaviors>
</behaviors>

<services>
<service behaviorConfiguration="Debug" name="Samples.WCF.Services.
TestService">
<endpoint address=http://www.williamgryan.mobi/samples/487
binding="wsHttpBinding" bindingConfiguration="" name="WsHttp"
contract="Samples.WCF.Services.ITestService" />
<endpoint binding="mexHttpBinding" bindingConfiguration="" name="Mex"
contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>

O link entre o behavior configurado e o serviço está no atributo behaviorConfiguration do service, conforme destacado acima.

Outro item provável de cair no exame é o ProtocolMapping. Esse elemento permite que você relacione bindings aos protocolos de transporte:

<protocolMapping>
<add scheme="http" binding="basicHttpBinding"/>
<add scheme="net.tcp" binding="netTcpBinding"/>
<add scheme="net.pipe" binding="netNamedPipeBinding"/>
<add scheme="net.msmq" binding="netMsmqBinding"/>
</protocolMapping>

Isso pode ser feito tanto nos arquivos app.config de cada serviço quanto no arquivo geral da máquina de host, o machine.config.

Configurando Bindings

Ainda no WCF Service Configuration Editor, é possível configurar bindings. A tela Create a New Binding lista todas as opções possíveis:

select binding

Após criar um wsHttpBinding, é necessário definir um ProtocolMapping para o protocolo wsHttp. É possível fazer isso selecionando o nodo Protocol Mapping, clicando em New, e informando o Scheme (wsHttp), o Binding (wsHttpBinding) e o Binding Configuration, com o nome do binding criado anteriormente. A configuração no XML ficaria assim:

<protocolMapping>
<add scheme="wsHttp" binding="wsHttpBinding"
bindingConfiguration="wsHttpBindingConfigSample" />
</protocolMapping>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBindingConfigSample" />
</wsHttpBinding>
</bindings>

Nesse caso, configuramos o protocolo wsHttp pelo Protocol Mapping para responder nesse binding. Um cenário mais real seria editar um protocolo já configurado por padrão para responder no nosso binding criado.

Vamos fazer isso com o protocolo http. Pelo editor, basta ir em Protocol Mapping, selecionar o Scheme http e Edit. Após configurar para o nosso binding (wsHttpBinding), o arquivo de configuração ficará assim:

<protocolMapping>
<remove scheme="http" />
<add scheme="http" binding="wsHttpBinding"
bindingConfiguration="wsHttpBindingConfigSample" />
</protocolMapping>

Isso é tudo para esse objetivo :)
Para o exame, certifique-se de olhar os exemplos em XML acima e memorizar os atributos possíveis para cada elemento.

O próximo post será sobre o objetivo 3.3, Configure WCF services by using the API.
Até lá!

Leia Mais