WCF Web API – Formulários

Sabemos que temos vários métodos suportados pelo protocolo HTTP, e entre eles, os mais comuns que vemos e utilizamos é o GET e o POST. Na primeira opção, o que enviamos ao servidor durante a requisição é o cabeçalho contendo tudo o que é necessário para efetuá-la, sem qualquer informação extra no corpo da mensagem. Ao contrário do que ocorre com o verbo POST, que além das informações que vão no cabeçalho, podemos também enviar qualquer conteúdo no corpo da mensagem.

Obviamente que o entendimento deste conteúdo por parte do serviço está condicionado ao header chamado Content-Type. Entre os vários tipos de conteúdo, temos os formatos Xml e Json. Mas ainda existe um outro formato, que também é muito comum em aplicações Web, que é conhecido como application/x-www-form-urlencoded. Este é o formato padrão que é utilizado pelo navegador, quando ele efetua um POST de um formulário HTML, codificando os campos existentes no mesmo, em uma coleção de pares de chave e valor.

Abaixo temos uma página HTML de exemplo, que contém um formulário simples, com dois campos do tipo Text e um botão que submete o formulário para o serviço que vamos criar mais tarde. Note que os controles estão envolvidos por um elemento chamado form, que contém o endereço (action) para onde o formulário será postado.

<form action=”http://localhost:1989/paginas/Enviar&#8221; method=”post”>
  <input type=”text=” id=”Nome” name=”Nome” />
  <input type=”text=” id=”Cpf” name=”Cpf” />
  <input type=”submit” name=”Enviar” value=”Enviar” />
</form>

Ao preencher os campos e postar o formulário, podemos capturar a requisição e analisar o que está sendo enviado ao serviço mencionado. Podemos notar a estrutura da requisição abaixo, e que alguns headers menos relevantes para a situação, foram omitidos por questões de espaço. A nossa análise começa pelo header, onde temos o Content-Type definido como application/x-www-form-urlencoded, que como falamos acima, corresponde ao valor padrão para formulários HTML. No corpo da mensagem temos os campos do formulário separados pelo caracter &. Já os espaços são substituídos pelo caracter +. E, para finalizar, o nome do controle é definido como chave, enquanto o conteúdo do controle é definido como valor na coleção.

POST http://localhost:1989/paginas/Enviar HTTP/1.1
Accept-Language: en-US,pt-BR;q=0.5
Content-Type: application/x-www-form-urlencoded
Connection: Keep-Alive
Content-Length: 50
Host: localhost:1989

Nome=Israel+Aece&Cpf=111.222.333-44&Enviar=Enviar

O WCF Web API possibilita a leitura deste tipo de requisição, e para isso, temos um media type específico, chamado de FormUrlEncodedMediaTypeFormatter. Em um artigo anterior eu comentei sobre os media types, mais deixei este media type de fora justamente para abordá-lo aqui. Assim como os media types Xml e Json, este também é adicionado por padrão à execução, mapeando o tipo application/x-www-form-urlencoded como sendo o formato que ele interpreta.

Quando você requisita alguma informação para um serviço construído sobre essa nova API, a escolha do media type é realizada no interior da classe ObjectContent, através de um método protegido chamado SelectReadFormatter. Esse método percorre os formatadores que temos adicionados, verificando se o tipo de conteúdo da requisição é suportado por ele, e sendo, ele é imediatamente retornado para que o próprio ObjectContent possa ler o conteúdo utilizando-o. Com o formatador em mãos, a classe ObjectContent invoca o método ReadFromStream, exposto pela classe MediaTypeFormatter, repassando a ele o stream contendo o corpo da requisição, que fará o parser da mesma, e devolvendo para o serviço o objeto já deserializado. Abaixo temos um método simples, chamado Enviar, que recebe a requisição do formulário HTML que fizemos acima:

[WebInvoke]
public HttpResponseMessage Enviar(HttpRequestMessage request)
{
    var conteudo = request.Content.ReadAsString();

    return new HttpResponseMessage() { Content = new StringContent(conteudo) };
}

O grande problema do código acima, é que estamos lendo o corpo da mensagem como uma string, ficando difícil manipular e extrair informações do conteúdo enviado pelo cliente. Há algumas alternativas para conseguirmos extrair informações de uma forma mais amigável. A primeira delas é utilizando o método estático chamado ParseQueryString, da classe HttpUtility. Esse método recebe uma string e faz o parser dela, retornando o resultado (chaves e valores), em uma coleção especializada do tipo NameValueCollection. É importante lembrar que esta coleção é uma espécie de dicionário, suportando apenas strings como chave e como valor, e sua principal característica é não permitir chaves duplicadas, assim como qualquer dicionário, mas ela consegue agrupar valores que tenham a mesma chave, dentro de em um mesmo item. O código abaixo ilustra essa nova versão:

[WebInvoke]
public HttpResponseMessage Enviar(HttpRequestMessage request)
{
    var conteudo = HttpUtility.ParseQueryString(request.Content.ReadAsString());
    var resultado = new StringBuilder();

    foreach (var item in conteudo.AllKeys)
        resultado.AppendLine(string.Format(“{0}: {1}”, item, conteudo[item]));

    return new HttpResponseMessage()
    {
        Content = new StringContent(resultado.ToString())
    };
}

A outra opção que temos, é utilizando classes que correspondem ao formato Json. Há um método estático chamado ParseFormUrlEncoded, fornecido pela classe FormUrlEncodedExtensions, que dado uma string representando o corpo codificado no formato que estamos vendo neste artigo, ele retorna um objeto do tipo JsonObject, que nada mais é do que uma espécie de dicionário, onde a chave é do tipo string e o valor do tipo JsonValue. Estes objetos também fazem parte desta nova API, quais já discuti neste outro artigo com maiores detalhes.

[WebInvoke]
public HttpResponseMessage Enviar(HttpRequestMessage request)
{
    var conteudoCodificado = request.Content.ReadAsString();
    var conteudo = FormUrlEncodedExtensions.ParseFormUrlEncoded(conteudoCodificado);

    HttpResponseMessage response = new HttpResponseMessage();
    response.Content = new ObjectContent<JsonValue>(conteudo);
    response.Content.Headers.ContentType = new MediaTypeHeaderValue(“application/json”);

    return response;
}

Depois de extrair a string contendo o corpo da requisição, submetemos a mesma para o método ParseFormUrlEncoded, que retornará o objeto JsonObject com todo o conteúdo já no formato Json. Depois disso, definimos o objeto construído como corpo da mensagem de resposta e, finalmente, definimos o tipo da resposta como sendo do tipo application/json. Abaixo temos a resposta sendo devolvida para o cliente, retornando conforme o esperado:

HTTP/1.1 200 OK
Content-Length: 63
Content-Type: application/json

{“Nome”:”Israel Aece”,”Cpf”:”111.222.333-44″,”Enviar”:”Enviar”}

Como vimos acima, o formato em que o formulário é encaminhado para o serviço, pode ser convertido implicitamente para Json, dando assim, um maior poder na manipulação da requisição e na geração dos resultados. Isso quer dizer que podemos também receber um conteúdo em formato application/x-www-form-urlencoded defindo no parâmetro do método, o tipo JsonObject ou invés de lidar diretamente com a classe HttpRequestMessage. Abaixo temos o método Enviar refletindo essa alteração:

[WebInvoke]
public HttpResponseMessage Enviar(JsonValue conteudo)
{
    HttpResponseMessage response = new HttpResponseMessage();
    response.Content = new ObjectContent<JsonValue>(conteudo);
    response.Content.Headers.ContentType = new MediaTypeHeaderValue(“application/json”);

    return response;
}

Finalmente, podemos utilizar o método AsDinamyc da classe JsonValue, que converte o nosso objeto em um tipo dinâmico, que permite burlar a verificação estática dos membros, fazendo isso somente em tempo de execução. Abaixo temos esse exemplo, e logo na sequência, o resultado gerado por este método e que foi capturado. Note que o código fica muito mais simples e expressivo.

[WebInvoke]
public HttpResponseMessage Enviar(JsonValue conteudo)
{
    dynamic valores = conteudo.AsDynamic();

    return new HttpResponseMessage()
    {
        Content = 
            new StringContent(string.Format(“Nome: {0} – C.P.F.: {1}”, valores.Nome, valores.Cpf))
    };
}

HTTP/1.1 200 OK
Content-Length: 46
Content-Type: text/plain; charset=iso-8859-1

Nome: “Israel Aece” – C.P.F.: “111.222.333-44”

WCF Web API – Testes

Há alguns dias eu mostrei aqui, que o WCF Web API possibilita a construção de serviços de modelo tradicional WCF, ou seja, definir tipos que refletem o nosso negócio (Cliente, Produto, Pedido, etc.), bem como tipos mais simples (inteiro, string, boleano, etc.). Como sabemos, a finalidade é conseguir desenhar um serviço que nada saiba sobre a infraestrutura, como ele é exposto, características, etc.

Naquela mesma oportunidade, eu comentei sobre a possibilidade que temos de receber e/ou retonar objetos que refletem e fazem uso da estrutura do protocolo HTTP, que é um detalhe muito importante na estrutura REST. Ao utilizar as classes que descrevem a requisição (HttpRequestMessage) e a resposta (HttpResponseMessage), podemos interagir com detalhes do protocolo, que antes exigia-se um conhecimento de baixo nível do WCF.

Como sabemos, assim como já acontece com os serviços tradicionais construídos em WCF, podemos aplicar testes em cima da classe que representa o serviço sem maiores problemas. Isso se deve ao fato de que em ambos os modelos que temos agora, como eles são simples classes, com métodos que executam tarefas e, eventualmente, retornam algum resultado.

Mas e quando queremos receber e/ou enviar dados para este serviço, utilizando instâncias das classes HttpRequestMessage e HttpResponseMessage? Felizmente, assim como no ASP.NET MVC, a Microsoft está desenhando o WCF Web API com a possibilidade de testá-lo sem estar acoplado à infraestrutura do WCF, o que permite testar a classe do serviço, mesmo que ela receba ou devolva objetos característicos do protocolo HTTP.

Se repararmos no artigo anterior, onde eu mostro um serviço que possui dois métodos (Ping e PingTipado), podemos escrever testes e, consequentemente, utilizar a IDE do Visual Studio para executá-los, e como isso, nos antecipamos à eventuais problemas que possam acontecer, pois talvez seja possível capturar alguns desses problemas antes mesmo de levar o mesmo ao ambiente de produção.

No primeiro exemplo, estamos testando o método Ping, instanciando a classe que representa o serviço, e passando ao método Ping a instância da classe HttpRequestMessage. Neste momento, poderíamos abastecer informações na coleção de headers da requisição, com o intuito de fornecer tudo o que é necessário para o que o método/teste possa executar com sucesso. Depois da requisição realizada, verificamos se o status da resposta corresponde ao status OK.

[TestMethod]
public void DadoUmaRequisicaoSimplesDeveRetornarStatusComoOK()
{
    var response = new ServicoDeExemplo().Ping(new HttpRequestMessage());

    Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}

O próximo passo é construir um teste para o método PingTipado. Esse método recebe como parâmetro a instância da classe HttpRequestMessage<T>, definindo o parâmetro genérico T como sendo do tipo Informacao. A finalidade do teste é assegurar que, se passarmos uma instância nula da classe Informacao, uma exceção do tipo HttpResponseException deverá ser lançada. O código abaixo ilulstra este teste:

[TestMethod]
[ExpectedException(typeof(HttpResponseException))]
public void DadoUmObjetoInfoNuloDeveDispararUmErro()
{
    var response =
        new ServicoDeExemplo()
            .PingTipado(new HttpRequestMessage<Informacao>(null));
}

Finalmente, o próximo passo consiste em criar um teste, também para o método PingTipado, mas agora fornecendo a instância do objeto Informacao em um estado válido, para que o teste possa suceder se o serviço retornar a mesma instância da classe Informacao, onde os membros da requisição reflitam o que está sendo retornado como reposta. Abaixo o código que efetua o tal teste:

[TestMethod]
public void DadoUmObjetoInfoDeveRetornarEleComInformacoesExtra()
{
    var request = new Informacao() { Codigo = “123”, Dado = “Alguma Info” };
    var response =
        new ServicoDeExemplo()
            .PingTipado(new HttpRequestMessage<Informacao>(request))
            .Content
            .ReadAs();

    Assert.AreEqual(request.Dado, response.Dado);
}

WCF Web API – Media Types

Ao efetuar uma requisição para algum recurso sobre o protocolo HTTP, o servidor identifica o mesmo, faz o processamento, gera o resultado e, finalmente, devolve o resultado para o cliente que fez a solicitação. Por mais que isso não fica explícito, o conteúdo que trafega do cliente para o servidor (requisição) e do servidor para o cliente (resposta), sempre possui um formato específico.

Em grande parte de todos os recursos fornecidos através do protocolo HTTP, uma das necessidades é justamente definir o formato deste conteúdo, que por sua vez, direciona a interpretação pelo navegador, por uma outra aplicação ou até mesmo de uma biblioteca, permitindo efetuar o parser do resultado e, consequentemente, materializar o mesmo em algo “palpável”/visível.

Os formatos são representados por uma simples string, onde você tem uma primeira parte para descrever qual o tipo de conteúdo, e depois o seu formato. Por exemplo, ao invocar uma página onde o retorno é um conteúdo HTML, o formato será definido como text/html; ao solicitar uma imagem, o seu formato será definido como image/jpeg. Uma lista contendo todos os formatos de conteúdos disponíveis na internet, é gerenciada e mantida por entidade chamada IANA, e pode ser acessada aqui.

Como o WCF Web API tem uma forte afinidade com as características do HTTP, ele permite receber ou gerar conteúdos em formatos popularmente conhecidos pelo mercado. A finalidade deste artigo é mostrar como podemos proceder para configurar, utilizar e customizar os media types para os serviços. Em primeiro lugar, precisamos analisar a estrutura da classe que representa um media type. Para isso, a Microsoft criou uma classe abstrata chamada de MediaTypeFormatter, e já existem algumas implementações definidas dentro da API, como por exemplo, as classes XmlMediaTypeFormatter e JsonMediaTypeFormatter. A imagem abaixo mostra essa herança:

Como podemos perceber, os serviços que criamos utilizando essa nova API nada sabem sobre o formato em que ele chegou ou o formato em que ele será devolvido para o cliente. Na verdade o que ocorre é que os media types já são acoplados à execução, e para isso, a Microsoft utilizou um recurso de estensibilidade desta API, que são os operation handlers. Existem duas classes públicas chamadas RequestContentHandler e ResponseContentHandler, e como os próprios nomes dizem, fazem parte da requisição e da resposta, respectivamente.

Essas duas classes possuem uma propriedade chamada Formatters, que é uma coleção do tipo MediaTypeFormatterCollection. Ambos handlers já possuem as classes XmlMediaTypeFormatter (application/xml) e JsonMediaTypeFormatter (application/json) adicionadas por padrão. Através da imagem abaixo podemos visualizar onde se encontram cada um destes objetos que vimos acima:

Olhando para a imagem acima, uma pergunta que aparece é como o WCF escolhe qual dos formatadores utilizar. A escolha se baseia no formato solicitado pelo cliente. O formato pode ser incluído como um item na requisição, através do header Accept ou do Content-Type. O WCF escolhe o formatador de acordo com o valor que ele encontra em um desses headers, e caso o formato definido não for encontrado, o padrão é sempre devolver o conteúdo em formato Xml.

Customização

Apesar do Xml e Json serem os principais formatos que temos atualmente, pode haver situações onde desejamos criar um formato próprio, para que esse possa gerar e/ou receber o conteúdo em um formato específico. Como já percebemos acima, se desejarmos fazer isso, temos que recorrer à implementação da classe abstrata MediaTypeFormatter, customizando basicamente dois métodos principais OnReadFromStream e OnWriteToStream. Vamos analisar cada um desses métodos abaixo:

  • OnCanReadType: Dado um objeto do tipo Type, este método retorna um valor boleano (onde o padrão é True), indicando se aquele tipo é ou não entendido por aquele formatador. O tipo identifica os eventuais parâmetros que existem no método do serviço. Aqui podemos fazer validações, como por exemplo, identificar se o tipo é ou não serializável, se possui um determinado atributo, etc.
  • OnReadFromStream: Se o método acima retornar True, então este método é executado. Como parâmetro ele recebe o tipo do objeto (o mesmo que foi passado para o método acima), e um objeto do tipo Stream, que é fonte das informações (estado) do objeto a ser criado. Este método retorna um object, que corresponde à um objeto criado dinamicamente e configurado com os valores provenientes do Stream.
  • OnCanWriteType: Dado um objeto do tipo Type, este método retorna um valor boleano (padrão é True), indicando se aquele tipo é ou não entendido pelo formatador. O tipo identifica o retorno do método do serviço. Aqui podemos fazer validações, como por exemplo, identificar se o tipo é ou não serializável, se possui um determinado atributo, etc.
  • OnWriteToStream: Se o método acima retornar True, então este método é executado. Como parâmetro, ele recebe o tipo do objeto a ser serializado e um object que corresponde a instância do objeto a ser gravado. Ainda recebemos um Stream, que é o destino do objeto serializado.

Para podemos exemplificar, vamos criar um formatador específico para o formato CSV, que é um padrão bem tradicional, onde cada valor é separado pelo caracter “;”. Abaixo temos a classe CsvMediaTypeFormatter, que herda de MediaTypeFormatter. O que vemos de interessante no trecho de código abaixo, é a definição do formato application/csv sendo adicionado à coleção de media types suportados por este formatador customizado, que está acessível através da propriedade SupportedMediaTypes.

public class CsvMediaTypeFormatter : MediaTypeFormatter
{
    private const char SEPARATOR = ‘;’;

    public CsvMediaTypeFormatter()
    {
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue(“application/csv”));
    }

    public override object OnReadFromStream(Type type, Stream stream, HttpContentHeaders contentHeaders)
    {
        //implementação
    }

    public override void OnWriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext context)
    {
        //implementação
    }
}

A implementação é bastante trivial, e optei por não colocar toda ela aqui por questões de espaço. A implementação na íntegra está no arquivo que está vinculado à este artigo. De qualquer forma, ao gerar um conteúdo, estou analisando se o objeto é ou não uma coleção; se for, fazemos um trabalho diferenciado para capturar o tipo concreto do elemento que temos. Depois de chegar no elemento concreto, percorremos os itens da coleção, extraindo via Reflection, os valores das propriedades de cada item. Caso seja um objeto único, apenas extraímos as suas respectivas propriedades. Para ambos os casos, depois de encontrar o tipo concreto, percorremos as propriedades capturando os respectivos nomes, para que seja possível gerar o header do conteúdo.

Já quando lemos o conteúdo que está chegando ao serviço, extraímos a primeira linha (header do conteúdo) para capturar as propriedades que temos dentro do corpo da mensagem. Em seguida, se for uma coleção, criamos dinamicamente a instância de um objeto que implementa a interface IList, e para cada linha subsequente, criamos a instância do tipo concreto, adicionando na coleção criada no passo anterior. Caso seja um objeto único, tudo o que fazemos é instânciá-lo, configurar as propriedades e retorná-lo ao WCF.

Como já era esperado, o que precisamos fazer a partir de agora é instalá-lo à execução. E para isso, recorremos à classe HttpHostConfiguration, que através do método AddFormatters, podemos incluir classes que herdam de MediaTypeFormatter. O que fazemos aqui é instanciar e adicionar a classe CsvMediaTypeFormatter:

protected void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.MapServiceRoute<ServicoDeTeste>(
        “teste”,
        HttpHostConfiguration.Create().AddFormatters(new CsvMediaTypeFormatter()));
}

Para exemplificar, temos alguns métodos definidos no serviço que manda e/ou recebe instância(s) da classe Informacao. Novamente estou omitindo parte da implmentação para poupar espaço. Abaixo temos as assinaturas de cada um desses métodos:

[ServiceContract]
public class ServicoDeTeste
{
    [WebGet]
    public List<Informacao> Exemplo1() { }

    [WebInvoke]
    public List<Informacao> Exemplo2(List<Informacao> info) { }

    [WebGet]
    public Informacao Exemplo3() { }

    [WebInvoke]
    public Informacao Exemplo4(Informacao info) { }

    public class Informacao
    {
        public string Dado { get; set; }
        public string Codigo { get; set; }
    }
}

Finalmente, abaixo temos o log salvo pelo Fiddler das requisições efetuadas para cada um dos métodos de exemplo. Repare que quando enviamos uma requisição via GET, definimos o header Accept como application/csv, para que o WCF utilize o formatador customizado que criamos. Ao enviar uma requisição via POST, também definimos o header Content-Type como application/csv, para indicar ao WCF que o conteúdo está no formato CSV, e deverá utilizar o formatador customizado que entenda este conteúdo. É interessante notar também  que no último log, onde omitimos o header Accept, e o resultado está sendo devolvido em Xml, que conforme foi comentado acima, é o formatador padrão.

Exemplo 1 – Requisição

GET http://localhost:2025/teste/Exemplo1 HTTP/1.1
User-Agent: Fiddler
Host: localhost:2025
Accept: application/csv
Content-Length: 0

Exemplo 1 – Resposta

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 04 May 2011 01:59:48 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 88
Cache-Control: private
Content-Type: application/csv
Connection: Close

Dado;Codigo
Alguma Info;0
Alguma Info;1
Alguma Info;2
Alguma Info;3
Alguma Info;4

————————————————————-

Exemplo 2 – Requisição

POST http://localhost:2025/teste/Exemplo2 HTTP/1.1
User-Agent: Fiddler
Host: localhost:2025
Content-Type: application/csv
Content-Length: 39

Dado;Codigo
Info1;111111
Info2;222222

Exemplo 2 – Resposta

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 04 May 2011 02:04:42 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 41
Cache-Control: private
Content-Type: application/csv
Connection: Close

Dado;Codigo
Info1;111111
Info2;222222

————————————————————-

Exemplo 3 – Requisição

GET http://localhost:2025/teste/Exemplo3 HTTP/1.1
User-Agent: Fiddler
Host: localhost:2025
Accept: application/csv

Exemplo 3 – Resposta

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 04 May 2011 02:06:54 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 30
Cache-Control: private
Content-Type: application/csv
Connection: Close

Dado;Codigo
Alguma Info;334

————————————————————-

Exemplo 4 – Requisição

POST http://localhost:2025/teste/Exemplo4 HTTP/1.1
User-Agent: Fiddler
Host: localhost:2025
Content-Type: application/csv
Content-Length: 29

Dado;Codigo
Algum Teste;1981

Exemplo 4 – Resposta

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 04 May 2011 02:08:32 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 41
Cache-Control: private
Content-Type: application/csv
Connection: Close

Dado;Codigo
Algum Teste ping;1981 ping

————————————————————-

Exemplo 1 (Xml) – Requisição

POST http://localhost:2025/teste/Exemplo4 HTTP/1.1
User-Agent: Fiddler
Host: localhost:2025

Exemplo 1 (Xml) – Resposta

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 04 May 2011 02:13:36 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 412
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close

<?xml version=”1.0″ encoding=”utf-8″?>
<ArrayOfInformacao>
  <Informacao>
    <Dado>Alguma Info</Dado>
    <Codigo>0</Codigo>
  </Informacao>
  <Informacao>
    <Dado>Alguma Info</Dado>
    <Codigo>1</Codigo>
  </Informacao>
  <Informacao>
    <Dado>Alguma Info</Dado>
    <Codigo>2</Codigo>
  </Informacao>
  <Informacao>
    <Dado>Alguma Info</Dado>
    <Codigo>3</Codigo>
  </Informacao>
  <Informacao>
    <Dado>Alguma Info</Dado>
    <Codigo>4</Codigo>
  </Informacao>
</ArrayOfInformacao>

TesteComWCFMediaTypes.zip (12.86 kb)

WCF Web API – Operation Handlers

No artigo anterior eu falei sobre os message handlers, que afetam todas as mensagens que chegam à um determinado serviço, e que podemos utilizá-los para implementar alguns recursos como autenticação, autorização, etc. Só que também temos cenários onde queremos afetar apenas uma determinada operação, fornecendo informações que talvez tenham sido omitidas na requisição, eventuais validações, etc.

Com isso, temos mais um ponto de estensibilidade no WCF Web API chamado de Operation Handlers. Depois da requisição passar pelos eventuais Message Handlers e da criação da classe que representa o serviço, momentos antes de executar a requisição solicitada, entram em cena os Request Operation Handlers. Na sequência, executa a requisição solicitada, e antes de devolver para o cliente, executa também os Response Operation Handlers, caso eles existam. Como podemos perceber, os Operation Handlers estão divididos em duas partes, onde temos aqueles que são executados antes da requisição ser executada, e aqueles que são executados depois que a requisição foi atendida. A imagem abaixo ilustra esse fluxo:

A implementação deste handler consiste em criar uma classe que herda da classe abstrata HttpOperationHandler. Essa classe fornece duas propriedades principais: InputParameters e OutputParameters. Cada uma dessas propriedades são representadas por uma coleção de somente leitura, onde cada parâmetro é representado por um objeto do tipo HttpParameter. Se analisarmos o modelo de classes, veremos que existem 16 versões da classe HttpOperationHandler, onde o que muda é a quantidade de parâmetros genéricos, que representarão os parâmetros de entrada da operação e um deles corresponderá ao resultado da mesma, e os handlers interferem indiretamente na criação e definição dos valores destas coleções.

Para exemplificar o seu uso, criaremos um operation handler que define algum header da requisição como sendo parâmetro para o método, entregando a informação de uma forma mais simples, deixando a extração da informação do header da requisição à cargo do handler. Abaixo vemos o serviço:

[ServiceContract]
public class ServicoDeTeste
{
    [WebGet]
    public HttpResponseMessage Informacoes(string codigo)
    {
        return new HttpResponseMessage()
        {
            StatusCode = HttpStatusCode.OK,
            Content = new ObjectContent<Informacao>(new Informacao(){ Codigo = codigo, Dado = “Alguma Info” })
        };
    }
}

Como podemos perceber, o método Informacoes espera um parâmetro chamado codigo, que o cliente não mandará como parte da Uri, mas sim como um header customizado na requisição. O nosso operation handler irá extrair esse header e irá “injetar” o valor no parâmetro esperado pelo método. Utilizaremos a versão da classe HttpOperationHandler que recebe dois parâmetros genéricos, onde o primeiro corresponde ao parâmetro de entrada, e o segundo, corresponde ao tipo do parâmetro que o handler está tratado.

Recebemos no construtor o nome do parâmetro que deve ser adicionado à coleção de InputParameters, e o nome do header que esse handler deverá extrair da requisição. O(s) parâmetro(s) do método OnHandle e o tipo de retorno são definidos de acordo com os parâmetros genéricos que foram definidos na construção da classe HttpOperationHandler. Esse método é disparado pelo runtime, e é onde devemos colocar o tratamento necessário. Para o exemplo, se não encontrarmos o header na requisição, uma exceção deverá ser disparada, informando que a requisição é inválida. Abaixo temos o código que corresponde a esta implementação:

public class HeaderAsParameterHandler : HttpOperationHandler<HttpRequestMessage, string>
{
    private readonly string headerName;

    public HeaderAsParameterHandler(string outputParameterName, string headerName)
        : base(outputParameterName)
    {
        this.headerName = headerName;
    }

    public override string OnHandle(HttpRequestMessage input)
    {
        if (!input.Headers.Contains(headerName))
            throw new HttpResponseException(HttpStatusCode.BadRequest);

        return input.Headers.GetValues(headerName).First();
    }
}

Da mesma forma que temos um handler para extrair o header da requisição e injetar na coleção de parâmetros, vamos exemplificar também a volta, ou seja, capturar alguma informação do resultado, para definirmos também como um header customizado na resposta. Agora herdaremos da classe HttpOperationHandler, e definiremos os parâmetros de entrada e saída como sendo do tipo HttpResponseMessage, pois ela é o alvo da alteração e também de onde extrairemos as informações.

public class OutputAsHeaderHandler : HttpOperationHandler<HttpResponseMessage, HttpResponseMessage>
{
    private readonly string headerName;
    private readonly Func<HttpResponseMessage, string> extractor;

    public OutputAsHeaderHandler(string headerName, Func<HttpResponseMessage, string> extractor)
        : base(“response”)
    {
        this.headerName = headerName;
        this.extractor = extractor;
    }

    public override HttpResponseMessage OnHandle(HttpResponseMessage input)
    {
        input.Headers.AddWithoutValidation(this.headerName, this.extractor(input));
        return input;
    }
}

Note que o construtor da classe acima, recebe o nome do header a ser inserido na resposta, e um delegate que permite ao consumidor da classe, extrair a informação do lugar que ele achar mais conveniente. No método OnHandle, capturamos a mensagem de resposta, invocamos o delegate e extraimos o valor do header e, finalmente, retornamos a mensagem já com o header devidamente adicionado.

Depois dos handlers criados, precisamos incorporá-los na execução. Para isso, vamos recorrer à uma outra classe, chamada de HttpOperationHandlerFactory. Esse classe fornece dois métodos, onde cada um corresponde à criação de handlers para requisição (OnCreateRequestHandlers) e outro para a resposta (OnCreateResponseHandlers). Como temos dois handlers, um que trata a requisição e outro que trata a resposta, então vamos sobrescrever ambos os métodos, assim como é mostrado abaixo:

public class HeaderMapperOperationHandlerFactory : HttpOperationHandlerFactory
{
    protected override Collection<HttpOperationHandler> OnCreateRequestHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation)
    {
        var handlers = base.OnCreateRequestHandlers(endpoint, operation);
        handlers.Add(new HeaderAsParameterHandler(“codigo”, “CodigoDoCliente”));

        return handlers;
    }

    protected override Collection<HttpOperationHandler> OnCreateResponseHandlers(ServiceEndpoint endpoint, HttpOperationDescription operation)
    {
        var handlers = base.OnCreateResponseHandlers(endpoint, operation);

        handlers.Add(new OutputAsHeaderHandler(“CodigoDoCliente”,
            response => ((ObjectContent<Informacao>)response.Content).ReadAs().Codigo));

        return handlers;
    }
}

No método OnCreateRequestHandlers adicionamos a instância da classe HeaderAsParameterHandler, mapeando o header CodigoDoCliente para o parâmetro codigo. Já no método OnCreateResponseHandlers, criamos a instância da classe OutputAsHeaderHandler, informando no seu construtor o nome do header que desejamos que seja adicionado, e em seguida, um delegate que extrai o valor do header do corpo da mensagem.

Finalmente, tudo o que precisamos fazer é configurar o serviço para utilizar a factory de criação de operation handlers. Para isso, recorremos ao método genérico SetOperationHandlerFactory<T> da classe HttpHostConfiguration, onde informamos em seu tipo genérico T, o tipo da classe que criamos, que é a HeaderMapperOperationHandlerFactory. Abaixo temos um exemplo de como proceder com essa configuração:

protected void Application_Start(object sender, EventArgs e)
{
    var config =
        HttpHostConfiguration.Create().SetOperationHandlerFactory<HeaderMapperOperationHandlerFactory>();

    RouteTable.Routes.MapServiceRoute<ServicoDeTeste>(“teste”, config);
}

Depois de toda essa customização e configuração, tudo o que nos resta é realizar um teste. Ao efetuarmos uma requisição, vamos incluir o header customizado CodigoDoCliente, e o resultado deve voltar também com este mesmo header informado. Abaixo temos o log da requisição e da resposta que foi capturado pelo Fiddler:

Requisição
GET http://localhost:1989/teste/Informacoes HTTP/1.1
User-Agent: Fiddler
Host: localhost:1989
Accept: application/xml
CodigoDoCliente: 1291

Resposta
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 02 May 2011 02:09:22 GMT
X-AspNet-Version: 4.0.30319
CodigoDoCliente: 1291
Content-Length: 113
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close

<?xml version=”1.0″ encoding=”utf-8″?>
<Informacao>
  <Dado>Alguma Info Aqui</Dado>
  <Codigo>1291</Codigo>
</Informacao>

WCF Web API – Message Handlers

Seguindo as funcionalidades fornecidas pela WCF Web API, este artigo começa a abordar os pontos de estensibilidade que a mesma fornece, para que assim possamos interceptar vários estágios do processamento da mensagem no servidor, bem como do lado do cliente, caso estejamos utilizando a classe HttpClient para consumo de um serviço.

A Microsoft está construindo essa API em cima do próprio WCF, aproveitando a flexibilidade que ele fornece, para assim tornar a construção de um serviço que utiliza os princípios REST e, consequentemente, o protocolo HTTP, em algo muito mais simples. Já pudemos perceber isso nos artigos anteriores, e o fato da criação desta nova API, também há situações onde precisamos de algumas customizações, o que obriga a Microsoft a colocar nesta mesma API, recursos para que seja possível tal customização, sem complicar tanto como antes, afinal, estamos “um nível acima”.

Como sabemos, o WCF extrai os bytes correspondentes a solicitação no protocolo, utilizando toda sua infraestrutura, que a partir de agora, fica totalmente encapsulada nesta nova API. Depois de capturado, o WCF encaminha essa solicitação para esta nova layer, e a partir de agora inicia o um novo pipeline, que está dentro desta API. É este pipeline que possui vários pontos, e o primeiro deles é chamado de Message Handlers. Como o próprio nome diz, eles estão logo no primeiro estágio do pipeline, ou seja, independentemente de qual sua intenção para com o serviço, elas serão sempre serão executadas, a menos que haja algum critério que você avalie e rejeite a solicitação, o que proibirá o avanço do processamento para os próximos handlers.

Basicamente, esses handlers recebem a instância de uma classe do tipo HttpRequestMessage, que traz toda a solicitação do usuário, e retornam a instância da classe HttpResponseMessage, contendo a resposta gerada para aquela solicitação. E como já ficou subententido, podemos ter vários handlers adicionados ao pipeline, onde cada um deles pode ser responsável por executar uma tarefa distinta, como logging, autenticação, autorização, etc. A imagem abaixo ilustra esse fluxo:

A imagem se preocupa em mostrar a posição dos message handlers em relação ao serviço, mas ela está incompleta. Há ainda outros pontos entre o último message handler e a execução do serviço em si. O próximo passo é a criação da classe que representa o serviço, qual utilizamos uma implementação da interface IResourceFactory para customizar isso, ou até mesmo, acoplar um container de injeção de dependência, como vimos detalhadamente neste artigo.

A primeira ação para a criação que um message handler customizado, é herdar da classe abstrata DelegatingChannel. Essa classe recebe em seu construtor um objeto do tipo HttpMessageChannel. A finalidade deste objeto que é passado no construtor, é com o intuito de cada handler seja responsável por executar uma determinada tarefa, e depois passar para o próximo, ou seja, uma implementação do padrão Decorator. Antes de prosseguirmos com as explicações, vejamos um exemplo simples para logging:

public class LoggingMessageChannel : DelegatingChannel
{
    public LoggingMessageChannel(HttpMessageChannel innerChannel)
        : base(innerChannel) { }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(tr =>
        {
            var response = tr.Result;
            this.LogMessage(response);

            return response;
        }, cancellationToken);
    }

    private void LogMessage(HttpResponseMessage response)
    {
        Debug.WriteLine(
            string.Format(“Request:rn{0}rnResponse:rn{1}”,
                response.RequestMessage, response));
    }
}

Analisando o código acima, podemos perceber que sobrescrevemos o método SendAsync. Quando uma requisição chega para o serviço e temos um message handler adicionado, o método SendAsync dele é disparado. Note que como parâmetro recebemos uma instância da classe HttpRequestMessage, que como já está claro, reflete a requisição do cliente; em seguida, nós invocamos o método SendAsync da classe base (DelegatingChannel), e com isso, a requisição é encaminhada para o respectivo método no serviço que criamos, executando-o. O interessante é que o retorno deste método trata-se de uma instância da classe Task<HttpResponseMessage>, e com isso, permite a execução da tarefa para uma outra thread, sem bloquear a thread que iniciou a requisição, podendo esta ir atender uma outra requisição. Isso facilita bastante quando temos alguma operação de I/O, que exige um processamento mais complexo, o que resultará em mais tempo esperando pela resposta, e o uso do processamento assíncrono dará uma maior escalabilidade.

Este método está fazendo uso da classe Task<TResult>, que faz parte do modelo de programação assíncrona do .NET conhecido como TAP (Task Asynchronous Programming). Essa classe fornece um método chamado ContinueWith<TResult>, que me permite executar, também assincronamente, uma outra tarefa quando a tarefa principal for concluída, ou seja, no exemplo que temos acima, invocamos o método SendAsync da classe base, o método do serviço é disparado, e quando ele finalizar, irá executar a tarefa que colocamos dentro do método ContinueWith<TResult>, que nada mais é do que fazer o logging da mensagem de requisição e resposta.

Apesar de toda essa implementação, ela não funciona por si só. Precisamos adicioná-la à execução, e para isso, vamos recorrer ao objeto responsável pela configuração destes serviços, que é a classe HttpHostConfiguration. Essa classe possui um método chamado AddMessageHandlers, que permite informar um array de objetos do tipo Type, onde devemos mencionar as implementações (message handlers) da classe DelegatingChannel que desejamos adicionar à execução. Abaixo temos parte do arquivo Global.asax com a configuração para o serviço, e estamos fazendo uso de sua interface fluente para incrementarmos as customizações.

protected void Application_Start(object sender, EventArgs e)
{
    var config =
        HttpHostConfiguration.Create()
            .SetResourceFactory<MEFResourceFactory>()
            .SetErrorHandler<BadRequestErrorHandler>()
            .AddMessageHandlers(typeof(LoggingMessageChannel));

    RouteTable.Routes.MapServiceRoute<ServicoDeProspeccao>(“prospeccoes”, config);
}

Como já mostrei aqui, há também a possibilidade de utilizar essa mesma API em aplicações cliente, para o consumo destes tipos de serviços. E como era de se esperar, há também a possibilidade de fazer a interceptação da requisição e da resposta deste lado, e com isso, conseguir realizar verificações, consistências antes de enviar e/ou receber.

A classe HttpClient é a principal responsável por efetuar toda a coordenação das requisições e encaminhar as respectivas respostas para quem solicitou. Essa classe possui uma propriedade chamada Channel, que permite referenciar uma classe que herda da classe abstrata MessageProcessingChannel. Essa classe nos obriga a sobrescrever dois métodos, também autoexplicativos: ProcessRequest e ProcessResponse. O primeiro método é invocado momentos antes de enviar a requisição ao serviço, já o segundo, é invocado assim que a resposta volta. Abaixo temos uma implementação simples do mesmo:

public class LogProcessingChannel : MessageProcessingChannel
{
    public LogProcessingChannel(HttpMessageChannel innerChannel)
        : base(innerChannel) { }

    protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        ConsoleLog(request);
        return request;
    }

    protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken)
    {
        ConsoleLog(response);
        return response;
    }
}

Depois disso, tudo o que precisamos fazer é adicioná-la à classe HttpClient, definindo-a na propriedade Channel. Só que o construtor recebe a instância de uma classe que corresponde ao canal final, que neste caso é a classe HttpClientChannel, que faz uso interno das classes de baixo nível do namespace System.Net (HttpWebRequest e HttpWebResponse). Abaixo temos um exemplo de como proceder com essa configuração:

using (HttpClient client = new HttpClient(“http://localhost:1989/prospeccoes/&#8221;))
{
    client.Channel = new LogProcessingChannel(new HttpClientChannel());

    using (HttpRequestMessage request2 =
        new HttpRequestMessage(HttpMethod.Get, “RecuperarEmpresasEmProspeccao”))
    {
        Console.WriteLine(client.Send(request2).Content.ReadAsString());
    }
}

Percebemos que há uma certa discrepância nos nomes, pois em alguns lugares vemos este recurso mencionado como message handlers e em outros como message channels. Eu estou utilizando o que imagino ser a mais recente, mas é importante salientar que este projeto ainda está em sua fase inicial, e pode haver mudanças significativas no decorrer de seu desenvolvimento.

WCF Web API – Tratamento de Erros

O WCF fornece um ponto de estensibilidade que nos permite centralizar o tratamento das exceções que eventualmente acontecem dentro do nosso serviço, e que já tive a oportunidade de falar disso aqui. Basicamente você recorre à uma interface chamada IErrorHandler, que quando acoplado ao runtime do WCF, qualquer exceção disparada será enviada à este handler, dando a oportunidade dele fazer o que achar necessário, incluindo o logging da mesma.

Com o WCF Web API, a Microsoft também está criando um ponto para permitir que se adicione um código customizado para interceptar as exceções que são disparados por estes tipos de serviços. Como já era de se esperar, essa classe implementa a interface IErrorHandler, e mais uma vez, a Microsoft cria “uma classe acima” para abstrair a complexidade do WCF.

Tudo o que precisamos fazer é criar uma classe que herda da classe abstrata HttpErrorHandler, sobrescrevendo os métodos OnHandleError e OnProvideResponse. O primeiro método retorna um valor boleano indicando se deve ou não abortar a sessão, caso exista uma. Já o segundo método, OnProvideResponse, recebe como parâmetro a exceção que foi disparada pelo serviço, e permite customizarmos o retorno para o cliente, como por exemplo, definir um status do HTTP coerente com o problema que ocorreu. O retorno deste método é representado por um objeto do tipo HttpResponseMessage, que define a mensagem que chegará ao cliente que solicitou. Abaixo temos um exemplo desta implementação:

public class BadRequestErrorHandler : HttpErrorHandler
{
    protected override bool OnHandleError(Exception error)
    {
        return true;
    }

    protected override HttpResponseMessage OnProvideResponse(Exception error)
    {
        HttpResponseException exception = error as HttpResponseException;               
        response = exception.Response;
        response.StatusCode = HttpStatusCode.BadRequest;

        return response;
    }
}

Uma vez criado o tratador de erros, precisamos adicioná-lo à execução, e para isso, vamos recorrer ao configurador desta nova API, que é a classe HttpHostConfiguration. Esse classe possui uma interface fluente, expondo um método genérico chamado SetErrorHandler<T>, onde o tipo T deve ser uma classe que herda de HttpErrorHandler.

protected void Application_Start()
{
    var config =
        HttpHostConfiguration.Create()
            .SetResourceFactory<MEFResourceFactory>()
            .SetErrorHandler<BadRequestErrorHandler>();

    RouteTable.Routes.MapServiceRoute<ServicoDeProspeccao>(“prospeccoes”, config);
}

Finalmente, o que precisamos fazer dentro do serviço quando há algum problema, é disparar uma exceção do tipo HttpResponseException, que por sua vez, retrata uma falha que houve durante a execução do mesmo.

Reutilizando tipos entre serviços

Ao consumir um serviço SOAP, geralmente utilizamos o documento WSDL para montar todos os artefatos necessários para efetuar a comunicação com o serviço, que é executado remotamente. Esse documento traz todas as funcionalidades expostas por aquele serviço, diretivas de como essa comunição deve ser realizada, e ainda, informações que descrevem eventuais objetos complexos que fazem parte do contrato.

Em um cenário específico, podemos criar mais de um serviço que recebe ou expõe uma mesma classe. Essa classe é criada do lado do serviço, e este tipo pode ser utilizado por qualquer serviço que consiga acessá-la. Subindo estes dois serviços, ambos podem também ser referenciados em um mesmo cliente, e quando utilizamos o Visual Studio, ele faz o download do WSDL, constrói proxy e remonta todos os tipos deste lado, para que o cliente possa enviá-los e/ou recebê-los, conseguindo assim reconstruir os objetos no cliente, trabalhando de forma tipada.

Só que quando temos esse cenário, podemos nos deparar com um detalhe no cliente que pode parecer confuso em um primeiro momento. Para exemplificarmos, repare no simples código que temos abaixo. Há uma classe chamada Usuario com apenas uma propriedade. Temos dois contratos, onde cada um está implementado em um serviço diferente, e tudo o que as operações destes contratos fazem, é enviar e receber a instância da classe Usuario.

public class Usuario
{
    public string Nome { get; set; }
}

[ServiceContract]
public interface IContrato1
{
    [OperationContract]
    Usuario Metodo1(Usuario usuario);
}

public class Servico1 : IContrato1
{
    public Usuario Metodo1(Usuario usuario) { }
}

[ServiceContract]
public interface IContrato2
{
    [OperationContract]
    Usuario Metodo2(Usuario usuario);
}

public class Servico2 : IContrato2
{
    public Usuario Metodo2(Usuario usuario) { }
}

Aqui foi omitido, mas temos o código que expõe estes dois serviços através do protocolo HTTP, utilizando o binding BasicHttpBinding. Em primeiro lugar, referenciamos na aplicação cliente o Servico1. Isso faz com que o Visual Studio crie o tipo Usuario no cliente. Depois disso, decidimos referenciar o Servico2 também neste mesmo cliente, que como sabemos, também utiliza a classe Usuario. Ao fazer isso, o Visual Studio acaba recriando o tipo Usuario, mas agora para atender o segundo serviço.

Apesar de que do lado do servidor ambos serviços utilizam o mesmo tipo, ao referenciar no cliente, o Visual Studio não tem poder de identificar isso e, consequentemente, acaba sendo criado duas classes Usuario, onde cada um atende um serviço específico, e elas não podem ser compartilhadas, ou melhor, utilizar uma delas no lugar da outra. A imagem abaixo ilustra isso essa separação:

Para certificar da independência entre as classes Usuario dos servicos, podemos visualizar o código abaixo, que é uma versão resumida do que é gerado do lado do cliente para ambos os serviços referenciados. Para cada um dos deles, temos um namespace distinto, e debaixo destes, uma classe Usuario para cada. Mais adiante, podemos perceber que no interior de cada um dos métodos, eles referenciam a respectiva classe Usuario que está dentro de seu próprio namespace.

namespace Cliente.Servico1
{
    public partial class Usuario { }
    
    public interface IContrato1
    {
        Cliente.Servico1.Usuario Metodo1(Cliente.Servico1.Usuario usuario);
    }

    public partial class Contrato1Client
    {
        public Cliente.Servico1.Usuario Metodo1(Cliente.Servico1.Usuario usuario) { }
    }
}

namespace Cliente.Servico2
{
    public partial class Usuario { }
    
    public interface IContrato2
    {
        Cliente.Servico2.Usuario Metodo2(Cliente.Servico2.Usuario usuario);
    } 

    public partial class Contrato2Client
    {
        public Cliente.Servico2.Usuario Metodo2(Cliente.Servico2.Usuario usuario) { }
    }
}

O que vimos acima, é o suficiente para proibir que se use uma mesma classe Usuario para ambos os serviços. E, para que isso seja possível, temos algumas alternativas. A primeira delas seria, depois de referenciado ambos os serviços, elegermos uma das classes Usuario e levá-la para um local centralizado, talvez em um outra namespace, e depois disso, alterar os locais em que elas estão sendo referenciadas, e apontar para esta “nova” classe/local.

Uma outra opção que temos, que acredito ser até mais elegante, seria recorrer ao utilitário svcutil.exe, e gerar todos os recursos necessários para consumir um dos serviços. Depois das classes criadas, você deve criar um projeto do tipo Class Library, e colocar essas classes que foram geradas dentro dele. Com isso, você compila o projeto e gera uma DLL contendo o proxy e a classe Usuario. De posse desta DLL, você referencia na aplicação cliente que deve utilizar este serviço. Abaixo temos a linha de comando que gera as classes para o primeiro servico:

C:>svcutil http://localhost:8888

Depois da DLL referenciada, podemos agora adicionar a referência para o segundo serviço no próprio Visual Studio, que por padrão, ele é capaz, de antes de gerar os tipos que o serviço disponibiliza, analisar se esse tipo já existe referenciado na aplicação local, e caso exista, ele o reutilizará. Abaixo temos a imagem que ilustra essa configuração de reuso:

Finalmente, a última opção que temos é o compartilhamento do tipo entre o serviço e o cliente. Inclusive, isso evita a necessidade de fazer a referência para o serviço, pois todos os artefatos que você precisa para consumir o serviço (que são os contratos), já estarão disponíveis através do compartilhamento de uma DLL entre as pontas, o que obriga ambos os lados serem .NET. Qualquer uma dessas alternativas apontadas acima, tem como objetivo o compartilhamento/centralização de tipos do lado do cliente, assim como podemos visualizar na imagem abaixo:

WCF Web API – Consumo de Serviços

O WCF-REST Starter Kit fornece uma classe chamada HttpClient, que tem a finalidade de facilitar o consumo de serviços REST em aplicações escritas em .NET (ASP.NET, Console, Windows, etc.). Apesar de ser possível efetuar o consumo de serviços utilizando as classes de baixo nível (HttpWebRequest e HttpWebResponse), a classe HttpClient acaba sendo uma classe “intermediária”, ou seja, não chega a ser tão simples como um proxy de um serviço e não tão complicada como as classes do namespace System.Net.

Essa classe possibilita o consumo deste tipo de serviço sem tirar do desenvolvedor, o controle de todo o protocolo onde as requisições e respostas trafegam, fornecendo outras classes que foram construídas com o intuito de compor e facilitar essa interação. A classe HttpClient já está disponível há algum tempo, e já falei sobre ela neste artigo.

O que quero com este breve post, é alinhar a evolução da mesma, que assim como está acontecendo com o restante do WCF-REST Starter Kit, ela também está sendo trazida para dentro do .NET/WCF, que por sua vez, se tornará a API para o consumo de serviços REST na plataforma .NET. Assim como comentei no artigo anterior, basta você adicionar o WCF Web API através do Nuget, que vários assemblies serão incluídos na aplicação, e com isso, já poderá fazer uso dela.

Ao adicionar este pacote, o assembly Microsoft.Net.Http.dll é referenciado na aplicação, o que nos permitirá fazer uso de classes como HttpClient, HttpRequestMessage (HttpRequestMessage<T>), HttpResponseMessage (HttpResponseMessage<T>), ObjectContent<T>, etc. Essas classes estão debaixo do namespace System.Net.Http, e para consumir aquele mesmo serviço de prospecção que foi criado no artigo anterior, poderíamos proceder da seguinte forma:

using (HttpClient http = new HttpClient(“http://localhost:1989/prospeccoes/&#8221;))
{
    using (HttpRequestMessage request1 = new HttpRequestMessage(HttpMethod.Post, “Adicionar”))
    {
        request1.Content = new ObjectContent<Empresa>(new Empresa() { Nome = “Empresa 1” });
        request1.Content.Headers.ContentType = new MediaTypeHeaderValue(“application/xml”);

        Console.WriteLine(http.Send(request1).StatusCode);
    }

    using (HttpRequestMessage request2 = 
        new HttpRequestMessage(HttpMethod.Get, “RecuperarEmpresasEmProspeccao”))
    {
        foreach (var item in http.Send(request2).Content.ReadAs<List<Empresa>>())
            Console.WriteLine(item.Nome);
    }
}

Como podemos perceber, grande parte do que existe no WCF-REST Starter Kit começa a ser transportado para esta nova API.

WCF Web API – Overview

Que o Windows Communication Foundation (WCF) é o pilar de comunicação dentro da plataforma .NET já é sabido. Com uma estrutura flexível, permite a construção de qualquer serviço independentemente de protocolo, pois ele não mantém ou menciona qualquer característica referente a isso, e apenas com a adição de uma pequena configuração (endpoint), é possível expor aquele serviço atendendo os mais diversos clientes.

Na versão 3.0 do .NET Framework, a Microsoft lançou o WCF, dando suporte a exposição de serviços aos mais diversos tipos de protocolos, “abraçando” várias tecnologias, onde as mensagens sempre são trocadas em formato SOAP. Muitas aplicações que precisam expor ou consumir serviços, as vezes não necessitam ter toda a complexidade e overhead que o SOAP impõe. Devido a isso, a Microsoft incluiu na versão 3.5 do .NET Framework, um conjunto de funcionalidades que permitem expor serviços construídos em WCF, seguindo os princípios REST, que possui uma afinidade com o protocolo HTTP, recorrendo a toda estrutura deste protocolo para manipular os serviços.

Mais tarde, a Microsoft também criou o WCF-Rest Starter Kit, que é uma biblioteca que traz várias funcionalidades à serviços REST, e facilita o consumo de serviços REST dentro de aplicações construídas em .NET bem mais simples do que trabalhar diretamente com a classes de baixo nível, tais como HttpWebRequest e HttpWebResponse, que estão dentro do namespace System.Net.

Depois de tudo isso, a Microsoft agora trabalha em um novo projeto chamado WCF Web API. Esta biblioteca melhora, incrementa e facilita a construção e consumo de serviços REST dentro do WCF. A grande finalidade deste novo conjunto de funcionalidades, é dar controle total ao desenvolvedor na construção de serviços REST, encapsulando toda a complexidade que o WCF possui, fornecendo classes de mais alto nível para lidar com protocolo HTTP, sua infraestrutura e as funcionalidades para qual o serviço está sendo construído.

Há algum tempo eu comentei aqui, que a Microsoft está trabalhando em um projeto para incorporar dentro do .NET Framework classes para trabalhar com serialização Json. Naquela oportunidade eu já tinha introduzido o site http://wcf.codeplex.com, que é onde a Microsoft está centralizando todas as inovações que estão sendo construídas no WCF, incluindo a Web API, que é alvo deste artigo.

Seguindo a linha de alguns outros produtos, a Microsoft também disponibilizou esta API no Nuget, ou seja, os assemblies que a compõem estão disponíveis através da adição de um packege que é acessado e instalado pelo Nuget. Ao adicionar o package WebApi.All, ele faz o download dos assemblies e já os referenciam na aplicação corrente. A imagem abaixo ilustra essa etapa:

Estrutura do Serviço

Apesar de toda a flexibilidade do WCF, uma de suas maiores reclamações é a complexidade da configuração de um serviço, e que dependendo dos recursos que você quer habilitar, a configuração pode ser ainda mais difícil. Apesar de algumas alternativas que a Microsoft criou, como é o caso do utilitário chamado WCF Service Configuration Editor, ainda assim é muito complicado, principalmente em serviços mais simples, como são os casos de serviços baseados em REST.

Essa API torna a criação de serviços REST mais simples, e a configuração para que eles funcionem quase não existe. Não que ela não seja mais necessária, mas como comentei acima, há muitas classes de mais alto nível, que encapsulam e acoplam esse serviço e sua respectiva configuração no runtime/pipeline do WCF, sem a necessidade de conhecer e gerenciar ServiceHosts, InstanceContexts, Contracts, etc. Para exemplificar, vamos criar um serviço simples, que gerenciam empresas que estão sendo prospectadas:

[ServiceContract]
public class ServicoDeProspeccao
{
    [WebGet]
    public List<Empresa> RecuperarEmpresasEmProspeccao()
    {
        //implementação
    }

    [WebInvoke]
    public void Adicionar(Empresa empresa)
    {
        //implementação
    }
}

Como podemos perceber, diferentemente de serviços que construímos até então, a classe que representa o serviço não implementa uma interface de contrato. Isso se deve ao fato de serviços REST não possuem um contrato definido, sem geração de metadados e sem operações (já que serviços REST são baseados em recursos). O atributo ServiceContractAttribute ainda continua sendo necessário, já que é esse metadado que o runtime utiliza para construir e disponibilizar o serviço para os clientes.

Os atributos WebGetAttribute e WebInvokeAttribute não são novidades, já que eles foram incluídos a partir da versão 3.5 do .NET Framework. Para recapitular, a grosso modo, WebGetAttribute permite que o acesso à operação seja feita através do método GET, enquanto o atributo WebInvokeAttribute faz com que a operação seja acessível através do método POST (padrão), PUT, etc., e você configura isso através da propriedade Method. Para mais detalhes, você pode consultar este artigo.

Utilizando a estrutura tradicional do WCF, teríamos que criar um arquivo *.svc para representar o serviço, configurar o mesmo através do Web.config, definindo os endpoints, behaviors, bindings, etc. Podemos também utilizar a classe ServiceRoute para acoplar o serviço ao sistema de roteamento de URL do ASP.NET, que apesar de trazer uma nova forma de publicar o serviço, ainda exige o conhecimento em objetos característicos do WCF. Para melhorar isso também, temos um método genérico de estensão chamado MapServiceRoute, que em um dos seus overloads, apontamos o tipo do serviço e uma string representando o prefixo. Essa configuração é realizada em nível de aplicação, e como se trata de um projeto Web, o local para colocarmos isso é o evento Application_Start, acessível através do arquivo Global.asax:

protected void Application_Start()
{
    RouteTable.Routes.MapServiceRoute<ServicoDeProspeccao>(“prospeccoes”);
}

Isso já é o suficiente para publicar e acessar o serviço. Baseando nas configurações acima, para acessar a primeira operação, utilizamos o seguinte endereço: http://localhost:1989/prospeccoes/RecuperarEmpresasEmProspeccao. Apenas variando o nome da operação, é o necessário para invocar a segunda operação, chamada de Adicionar.

Internamente, o método MapServiceRoute cria toda a estrutura necessária para um serviço WCF funcionar. Isso quer dizer que ele fará a criação do ServiceHost, definindo o serviço para aquela classe que foi criada acima. Além disso, a criação do endpoint é também realizada no interior deste método, e como sabemos, uma das características de um endpoint é o binding. E para alinhar com esta nova tecnologia, temos um novo binding, chamado de HttpBinding, que é utilizado por serviços que utilizam requisições e respostas baseadas em mensagens HTTP. Falaremos mais detalhadamente destes tipos adiante, ainda neste artigo.

Um detalhe importante é com relação a serialização das informações que são encaminhadas para o serviço e aquelas que são retornadas pelo mesmo. Como sabemos, serviços REST não possuem o envelope o SOAP, mas o seu conteúdo, na maioria das vezes, deve ser serializado em formato conhecido, como é o caso do Xml ou Json. Ainda no formato antigo, a Microsoft incluiu uma opção no binding WebHttpBinding chamada automaticFormatSelectionEnabled, que quando está definida como True, ela gerará o retorno de acordo com a necessidade do cliente. Mas como o cliente demonstra a sua necessidade? Basicamente o WCF irá retornar o conteúdo serializado de acordo com o que o cliente determina no header accept do HTTP. Por padrão, quando você omite, o WCF retorna o conteúdo serializado em Xml. Para o método RecuperarEmpresasEmProspeccao, temos o seguinte resultado:

<?xml version=”1.0″ encoding=”utf-8″?>
<ArrayOfEmpresa>
  <Empresa>
    <Nome>Empresa de Teste 1</Nome>
  </Empresa>
  <Empresa>
    <Nome>Empresa de Teste 2</Nome>
  </Empresa>
</ArrayOfEmpresa>

Agora, se ao definir o header accept como sendo application/json, as empresas serão retornadas em formato Json, facilitando o consumo através de aplicações Javascript:

[{“Nome”:”Empresa de Teste 1″}, {“Nome”:”Empresa de Teste 2″}]

Injeção de Dependência

Uma das boas práticas que temos hoje ao construir qualquer classe, seja ela para servir ou não como um serviço, é desenhá-las separando as responsabilidades de cada uma delas, e tornando o acoplamento entre elas cada vez menor, para que assim possamos ter um código simples de evoluir e manter.

Como o uso de serviços é comum nos dias de hoje, é importante que também se crie serviços baseando-se nestes princípios. Para isso, precisamos de um ponto de estensibilidade que permita interceptar a criação das classes que representam o serviço, e com isso, utilizarmos algum container de injeção de dependência para abastecermos todas as necessidades do serviço que está sendo exposto, para que ele esteja pronto para atender a requisição.

Quando utilizamos o WCF puro, o ponto de estensibilidade que permite isso é através da implementação da interface IInstanceProvider. Nesta nova API, temos um ponto de estensibilidade para fornecer a instância da classe que representa o serviço, e ali acoplarmos um código customizado. Para exemplificar, vamos incrementar a classe do serviço que fizemos acima (ServicoDeProspeccao), passando agora a depender de uma classe que é a implementação concreta de algum repositório. Temos dentro da classe um membro privado chamado repositorio do tipo IRepositorio. Esse repositório é utilizando no interior dos métodos RecuperarEmpresasEmProspeccao e Adicionar, carregando ou salvando os dados da implementação do repositório concreto que será injetado.

[ServiceContract]
public class ServicoDeProspeccao
{
    [Import]
    private IRepositorio repositorio;

    [WebGet]
    public List<Empresa> RecuperarEmpresasEmProspeccao()
    {
        return this.repositorio.RecuperarEmpresas();
    }

    [WebInvoke]
    public void Adicionar(Empresa empresa)
    {
        this.repositorio.Adicionar(empresa);
    }
}

Se repararmos, o membro repositorio está decorado com o atributo ImportAttribute. Esse atributo é fornecido pelo MEF (Managed Extensibility Framework), que se baseia nestes metadados para relacionar e abastecer o membro com a instância de uma classe que implementa esta interface (IRepositorio). Como exemplo, vamos criar um repositório em memória, que armazena os dados em uma lista, e também através de um atributo (ExportAttribute), indicaremos ao MEF que essa classe é uma implementação concreta da interface IRepositorio.

[Export(typeof(IRepositorio))]
public class RepositorioEmMemoria : IRepositorio
{
    public void Adicionar(Empresa empresa)
    {
        //implementação
    }

    public List<Empresa> RecuperarEmpresas()
    {
        //implementação
    }
}

Ainda é necessário configurar o container do nosso framework de injeção de dependência, que neste caso, é o MEF. Para isso, vamos recorrer a interface IResourceFactory, que está disponível por essa nova API. Essa interface fornece apenas dois métodos autoexplicativos: GetInstance e ReleaseInstance. O que fazemos no interior do método GetInstance é instanciar a classe que representa o serviço (ServicoDeProspeccao) e recorrer ao container para resolver as dependências do mesmo. Depois disso, retornamos o objeto já configurado para o WCF poder proceder com a execução da requisição.

private class MEFResourceFactory : IResourceFactory
{
    private CompositionContainer container;

    public MEFResourceFactory()
    {
        AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        this.container = new CompositionContainer(catalog);
    }

    public object GetInstance(Type serviceType, InstanceContext instanceContext, HttpRequestMessage request)
    {
        var service = Activator.CreateInstance(serviceType);
        this.container.ComposeParts(service);

        return service;
    }

    public void ReleaseInstance(InstanceContext instanceContext, object service) { }
}

Só que isso ainda não é o suficiente. Precisamos indicar ao WCF para ele recorrer à esta factory para construir o serviço. E para isso, vamos utilizar um overload do método MapServiceRoute, que além do prefixo do serviço, recebe a instância da classe HttpHostConfiguration, que representa a configuração do host do serviço, obviamente de uma forma muito mais simplificada do que aquela que é utilizada pelo WCF. Essa classe fornece um método chamado SetResourceFactory, que como o próprio nome diz, recebe ou cria a instância da classe que representa o construtor de instâncias do serviço, que no nosso caso é aquela que acabamos de criar: MEFResourceFactory. Aquela configuração que tínhamos acima no arquivo Global.asax, agora passa a figurar da seguinte forma:

protected void Application_Start(object sender, EventArgs e)
{
    var config = HttpHostConfiguration.Create().SetResourceFactory<MEFResourceFactory>();

    RouteTable.Routes.MapServiceRoute<ServicoDeProspeccao>(“prospeccoes”, config);
}

Trabalhando com mensagens HTTP

Como sabemos, a construção de serviços WCF possibilita criarmos um contrato e, consequentemente, uma classe que representará o serviço, que por sua vez, deve implementar aquele contrato que descreve as funcionalidades expostas por ele. Ao utilizar esta técnica, construimos nosso contrato utilizando tipos simples (inteiro, decimal, boleano, etc.) ou até mesmo tipos complexos, que são classes contendo as propriedades que levam de um lado ao outro lado a informação em uma forma mais “tipada”.

Apesar disso ser interessante, e ser um grande diferencial do WCF, isso acaba sendo um problema ao trabalhar com serviços REST. Isso se deve ao fato de que há a necessidade de ter o controle total do HTTP, que é possível com o WCF puro, mas mais uma vez, exige um conhecimento de baixo nível para conseguir manipular o protocolo e suas respectivas características.

Com o intuito de facilitar e trazer, novamente, o controle para o desenvolvedor, a Microsoft inclui nesta API classes que representam a mensagem de requisição (HttpRequestMessage) e de resposta (HttpResponseMessage). Cada uma dessas classes trazem várias propriedades, onde cada uma delas expõe características do protocolo HTTP, tais como: Content, Headers, Method, Uri, StatusCode, etc.

O serviço passará a utilizar essas classes em suas operações, ou seja, receberá um parâmetro do tipo HttpRequestMessage, que possui todas as informações necessárias solicitadas pelo cliente, enquanto o retorno será do tipo HttpResponseMessage, que será onde colocaremos todas as informações de resposta para o mesmo cliente, sendo essas informações o resultado em si, o código de status do HTTP, eventuais headers, etc. Para exemplificar, vamos analisar o método Ping abaixo:

[ServiceContract]
public class ServicoDeTeste
{
    [WebGet]
    public HttpResponseMessage Ping(HttpRequestMessage request)
    {
        return new HttpResponseMessage(HttpStatusCode.OK, string.Empty)
        {
            Content = new StringContent(“algum conteudo”)
        };
    }
}

O ponto importante a se notar é a criação da classe HttpResponseMessage, que em seu construtor receber o código de status do HTTP, e logo em seguida, temos a propriedade Content, que como já suspeitamos, deve receber o resultado da operação a ser enviada para o cliente. Há uma classe abstrata que define a estrutura de um conteúdo de retorno, chamada de HttpContent. Já existem algumas implementações concretas desta classe nesta API, e uma delas é a StringContent, que recebe uma string e será devolvida para o cliente no corpo da resposta HTTP.

E se necessitarmos enviar e/ou receber objetos mais complexos, que vão além de simples strings? Para atender esta situação, na mais nova versão desta API, a Microsoft criou versões genéricas das classes HttpRequestMessage e HttpResponseMessage, que definem como parâmetro qualquer objeto que pode/deve ser serializado na comunicação entre as partes. Para exemplificar, considere o código abaixo:

[ServiceContract]
public class ServicoDeTeste
{
    [WebInvoke]
    public HttpResponseMessage<Informacao> PingTipado(HttpRequestMessage<Informacao> request)
    {
        Informacao informacao = request.Content.ReadAs();

        informacao.Dado += ” ping”;
        informacao.Codigo += 10;

        return new HttpResponseMessage<Informacao>(HttpStatusCode.OK)
        {
            Content = new ObjectContent<Informacao>(informacao)
        };
    }
}

public class Informacao
{
    public string Dado { get; set; }
    public int Codigo { get; set; }
}

Reparem que passamos a receber um parâmetro do tipo HttpRequestMessage<Informacao>, que indica que o conteúdo da mensagem será representado pelo tipo Informacao, que possui duas propriedades. Já o resultado é definido como sendo do tipo HttpResponseMessage<Informacao>, que indica que o conteúdo da mensagem também será representado pelo tipo Informacao. O que fazemos no interior deste método é recorrer à propriedade Content da classe HttpRequestMessage<T>, que retorna um objeto do tipo ObjectContent<T>, ou seja, mais uma classe que herda de HttpContent, para definir o tipo que será recebido/enviado ao HTTP. Este objeto fornece um método chamado ReadAs, que retorna a instância da classe que representa o conteúdo da mensagem, e que no caso acima, é a classe Informacao.

Depois de extraí-la, incrementamos os valores e criamos a instância da classe HttpResponseMessage<T>, também definindo a classe Informacao como tipo T, mas nada impede de utilizar um tipo diferente para a resposta. Tudo o que fazemos ali é definirmos o status como sendo OK, que caracteriza o sucesso da operação, e definimos o conteúdo da resposta através da propriedade Content, que recebe a instância da classe ObjectContent<T>, que exigirá logo no construtor da mesma, a instância do objeto tipado em T.

Para testar, podemos enviar a seguinte requisição à operação PingTipado da seguinte forma:

User-Agent: Fiddler
Content-Type: application/xml
Host: localhost:1989
Content-Length: 62

<Informacao>
  <Dado>teste</Dado>
  <Codigo>10</Codigo>
</Informacao>

E o resultado será:

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 18 Apr 2011 11:16:35 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 105
Cache-Control: private
Content-Type: application/xml; charset=utf-8
Connection: Close

<?xml version=”1.0″ encoding=”utf-8″?>
<Informacao>
  <Dado>teste ping</Dado>
  <Codigo>20</Codigo>
</Informacao>

Estou enviado e recebendo em formato Xml porque defini o header content-type como sendo application/xml, o que me devenvolverá o resultado no mesmo formato do envio. Nada impede de enviar o conteúdo em Json e definir application/json, que também me devolverá neste formato:

User-Agent: Fiddler
Content-Type: application/json
Host: localhost:1989
Content-Length: 34

{ “Dado”:”teste”, “Codigo”:123 }

HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 18 Apr 2011 11:20:23 GMT
X-AspNet-Version: 4.0.30319
Content-Length: 34
Cache-Control: private
Content-Type: application/json; charset=utf-8
Connection: Close

{“Codigo”:133,”Dado”:”teste ping”}

Conclusão: Vimos neste longo artigo a nova proposta da Microsoft para a criação de serviços WCF utilizando os princípios REST. Tudo o que vimos aqui, acaba nos bastidores utilizando todo o poder e flexibilização que o WCF fornece desde a sua primeira versão. Esta API vem com o intuito de redefinir como criamos e configuramos serviços baseados em REST, fornecendo classes de mais alto nível, sem exigir um conhecimento de toda arquitetura do WCF para saber onde e como interagir com um protocolo, que neste caso é o HTTP.

Tratamento de Erros e Instâncias

Uma das opções que temos para interceptar os problemas (exceções) que ocorrem dentro das operações de um serviço WCF, é a possibilidade de criar um error-handler, qual acoplamos à execução utilizando um behavior de nível de serviço. A finalidade deste handler é centralizar todo o processo de tratamento de erros, incluindo o procedimento de “tradução” da exceção em faults, e eventuais tarefas de logging.

Um artigo que escrevi há algum tempo, já detalha como criar o error-handler e o behavior para acoplá-lo à execução, mas para recapitular, o principal elemento é a interface IErrorHandler, que deve ser implementada, e mais tarde, acoplada à execução através da interface IServiceBehavior, que através do método ApplyDispatchBehavior, fornece um parâmetro do tipo ServiceHostBase, que com ele chegamos até o ChannelDispatcher, que finalmente expõe uma propriedade chamada ErrorHandlers, que é uma coleção de elementos do tipo IErrorHandler.

Como podemos perceber, o fato de ser uma coleção, podemos adicionar quantas instâncias de classes que implementam a interface IErrorHandler quisermos. Isso nos permite criar um conjunto destas classes, onde cada uma delas analisam/tratam o erro que aconteceu, permitindo isolar em cada implementação um tratamento específico, e incorporando à execução somente aquelas que são necessárias para atender aquele serviço.

internal class TratadorDeErros01 : IErrorHandler { }

internal class TratadorDeErros02 : IErrorHandler { }

internal class TratadorDeErros03 : IErrorHandler { }

Um dos métodos expostos pela interface IErrorHandler é o HandleError, que recebe como parâmetro a exceção que foi disparada pelo serviço, e retorna um valor boleano indicando se aquele erro foi ou não tratado da forma esperada por aquele tratador, e dependendo do que retornamos e como está configurado nosso serviço, influenciará drasticamente no comportamento do mesmo, em outras palavras, poderá afetar a instância da classe que representa o serviço.

Partimos do princípio que o nosso serviço esteja configurado com o modelo de gerenciamento de instância como PerSession, e há uma sessão criada. Se você tem vários tratadores acoplados à execução, como por exemplo, os três que criamos acima, eles serão disparados na ordem em que eles foram adicionados à coleção, caso uma exceção não tratada seja disparada no interior da operação que o cliente solicitou. Suponha que o segundo tratador seja capaz de entender o que representa aquela exceção e sabe como lidar com ela. Isso fará com o retorno do método HandleError seja definido como True e, consequentemente, a instância da classe que representa o serviço será mantida ativa, assim como podemos perceber através da imagem abaixo:

Tendo, no mínimo, um tratador que consiga lidar com a exceção disparada, é o suficiente para manter o serviço ativo. Agora, supondo que nenhum dos três tratadores sejam capazes de entender e tratar aquela execeção (todos os métodos HandleError retornarão False), e tendo o mesmo cenário acima, ou seja, um serviço configurado como PerSession, a instância da classe que representa o serviço será abortada, o que obrigará uma nova classe ser criada para atender aquele cliente, perdendo assim eventuais informações de estado que aquela instância mantenha. A imagem abaixo ilustra o que acabamos de ver:

Esse comportamento se deve ao fato de que uma exceção não tratada, por padrão, é entendida pelo runtime de que o serviço (instância) pode ter ficado em um estado falho, o que obriga ao WCF eliminar essa classe, a menos que você altere esse comportamento recorrendo ao recurso que vimos neste artigo.

Para finalizar, tudo o que vimos aqui serve apenas para quando utilizamos o modelo de gerenciamento de instância como PerSession. Para PerCall é indiferente, já que a instância da classe que representa o serviço serve apenas para atender a requisição corrente, e logo em seguida, é descartada. No modelo Single, tudo o que vimos aqui também não importa, já que temos uma única instância atendendo à todos os clientes, e qualquer erro não tratado não é suficiente para descartar a instância do serviço.