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”

Anúncios

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s