Compartilhar via


Retornando objetos dinâmicos a partir de serviços Web API

O objetivo deste artigo é detalhar o uso de objetos dinâmicos como uma alternativa para a implementação de serviços Web API. Em situações nas quais o resultado de uma operação não segue uma estrutura rígida (podendo então variar em virtude dos mais diversos motivos), este recurso pode ser de grande valia na construção de Web Services.

Introdução

Disponíveis desde a versão 4.0 do .NET Framework, os objetos dinâmicos são um tipo de construção que surgiu com a intenção de simplificar a manipulação de dados que não contam com uma estrutura fixa. Do ponto de vista prático, este mecanismo permite a criação de instâncias às quais podem ser adicionadas propriedades em tempo de execução (algo impossível se considerada apenas a utilização de classes concretas). De certa maneira esta é uma técnica similar àquela empregada com objetos pela linguagem JavaScript, através do modelo conhecido DOM (Domain Object Model).

Este comportamento flexível pode ser particularmente interessante na construção de Web Services baseados em uma arquitetura REST. Esta abordagem para a implementação de serviços não segue uma estrutura tão rígida, característica esta extremamente útil em cenários envolvendo o controle de informações como documentos fiscais, contratos e apólices de seguro. Em tais situações a quantidade e a variedade dos dados pode representar um grande desafio, resultando na implementação de numerosas classes e métodos que muitas vezes apresentam somente pequenas variações entre si (o que certamente contribuiria para tornar ainda mais complexa a tarefa de manutenção de um sistema).

Implementando a aplicação de exemplo

A fim de demonstrar o uso de objetos dinâmicos em aplicações Web API, será criada uma aplicação empregando os seguintes recursos:

  • O Microsoft Visual Studio Professional 2013 Update 4 como IDE de desenvolvimento;
  • O .NET Framework 4.5.1;
  • O Microsoft ASP.NET Web API 2.2.

A solução descrita neste artigo foi disponibilizada no Technet Gallery, com o download da mesma podendo ser realizado através do link:

https://gallery.technet.microsoft.com/Utilizando-tipos-dinmicos-60e57a32

O exemplo apresentado nesta seção será o de um serviço de consulta a cotações de moedas estrangeiras, tais como dólar norte-americano, euro e libra esterlina. Para que isto aconteça será criado um projeto do tipo “ASP.NET Web Application” chamado “TesteWebAPI.TiposDinamicos”:

Selecionar na sequência o template “Web API”:

O próximo passo agora será a criação de uma nova classe que terá por nome “SimulacaoCotacao” (este tipo ficará dentro da pasta “Models” do projeto TesteWebAPI.TiposDinamicos):

Na listagem especificada a seguir está a definição da classe SimulacaoCotacao, a qual retornará os valores atuais para moedas estrangeiras como dólar, euro e libra (as cotações geradas por este tipo representam valores fictícios, obtidos a partir de cálculos randômicos):

  • O atributo MOEDAS_DISPONIVEIS é um array que contém os códigos de moedas possíveis para a classe SimulacaoCotacao;
  • O processo de validação de uma sigla de moeda é realizado por meio do método MoedaValida, o qual faz uso do array MOEDAS_DISPONIVEIS;
  • Já a operação CalcularValorCotacao emprega a classe Random (namespace System), com o objetivo de gerar de maneira randômica um valor de cotação para uma moeda;
  • Caberá ao método ObterValorCotacao fornecer informações sobre a cotação de uma moeda estrangeira, devolvendo como resultado um objeto dinâmico (indicado através da palavra-chave “dynamic”). Esta operação recebe como parâmetro uma sigla de moeda, criando na sequência uma referência do tipo ExpandoObject (variável “cotacao”). Definida no namespace System.Dynamic, a classe ExpandoObject possibilita que propriedades sejam adicionadas a instâncias da mesma em tempo de execução (possuindo um comportamento semelhante ao do objeto ViewBag no ASP.NET MVC). As propriedades SiglaMoeda, Data e Horario são comuns a qualquer tipo de moeda, tendo sido adicionadas à referência "cotacao" logo após a criação desta última. Já o nome (propriedade NomeMoeda) e os valores de cotação podem variar (no caso de dólar, serão incluídas as propriedades ValorComercial e ValorTurismo; ao passo que para euro e libra esterlina existirá apenas a propriedade Valor).
using System.Linq;
using System.Dynamic;
 
namespace TesteWebAPI.TiposDinamicos.Models
{
    public static  class SimulacaoCotacao
    {
        private static  readonly string[] MOEDAS_DISPONIVEIS =
            { "USD",  "EUR", "LIB" };
 
        public static  bool MoedaValida(string siglaMoeda)
        {
            return MOEDAS_DISPONIVEIS.Contains(siglaMoeda);
        }
 
        private static  double CalcularValorCotacao(
            double valorBase,
            int menorValorDecimal,
            int maiorValorDecimal)
        {
            Random r = new  Random();
 
            return valorBase +
                (((double)r.Next(menorValorDecimal,
                                 maiorValorDecimal)) / 10000);
        }
 
        public static  dynamic ObterValorCotacao(
            string siglaMoeda)
        {
            DateTime dataAtual = DateTime.Now;
             
            dynamic cotacao = new  ExpandoObject();
            cotacao.SiglaMoeda = siglaMoeda;
            cotacao.Data = dataAtual.ToString("dd/MM/yyyy");
            cotacao.Horario = dataAtual.ToString("HH:mm");
 
            if (siglaMoeda == "USD")
            {
                cotacao.NomeMoeda = "Dólar norte-americano";
                cotacao.ValorComercial = CalcularValorCotacao(3, 2000, 3200);
                cotacao.ValorTurismo = CalcularValorCotacao(3, 2500, 4500);
            }
            if (siglaMoeda == "EUR")
            {
                cotacao.NomeMoeda = "Euro";
                cotacao.Valor = CalcularValorCotacao(3, 4000, 6000);
            }
            if (siglaMoeda == "LIB")
            {
                cotacao.NomeMoeda = "Libra esterlina";
                cotacao.Valor = CalcularValorCotacao(4, 7500, 9500);
            }
             
            return cotacao;
        }
    }
}

Um novo Controller também deverá ser criado, através da seleção do template “Web API 2 Controller - Empty”:

Definir como nome deste Controller o valor “CotacaoController”, como indicado na próxima imagem:

No Controller CotacaoController está a implementação do serviço para consulta aos valores de moedas estrangeiras. O código desta classe encontra-se na próxima listagem, sendo possível observar no mesmo:

  • O método GetCotacao recebe como parâmetro a sigla de uma moeda estrangeira. Este valor é verificado através de uma chamada ao método MoedaValida, o qual foi definido anteriormente na classe SimulacaoCotacao;
  • Se o código da moeda for inválido, uma nova instância do tipo HttpResponseMessage (namespace System.Net.Http) será gerada. O construtor de HttpResponseMessage recebe como parâmetro um valor definido no enum HttpStatusCode (namespace System.Net). Neste exemplo específico, o valor HttpStatusCode.BadRequest corresponde ao erro HTTP de código 400 (solicitação inválida). Estão sendo preenchidas ainda as propriedades Content (conteúdo da mensagem de erro, em que se informou um texto genérico) e ReasonPhrase (mensagem que geralmente detalha a razão que originou um erro) nesta referência da classe HttpResponseMessage. Por fim, uma exceção baseada no tipo HttpResponseException (namespace System.Web.Http) é disparada, com o objeto em questão recebendo como parâmetro em seu construtor a instância de HttpResponseMessage criada no início deste grupo de instruções;
  • Caso se indique uma moeda válida, a operação ObterValorCotacao da classe SimulacaoCotacao será acionada. O objeto retornado por esta ação (uma instância da classe ExpandoObject) será devolvido pela Action GetCotacao, para que consumidores do serviço possam ter acesso aos valores de moedas estrangeiras.
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TesteWebAPI.TiposDinamicos.Models;
 
namespace TesteWebAPI.TiposDinamicos.Controllers
{
    public class  CotacaoController : ApiController
    {
        public dynamic GetCotacao(string siglaMoeda)
        {
            siglaMoeda = siglaMoeda.ToUpper();
            if (!SimulacaoCotacao.MoedaValida(siglaMoeda))
            {
                var resp = new  HttpResponseMessage(HttpStatusCode.BadRequest)
                {
                    Content = new  StringContent("Erro de processamento"),
                    ReasonPhrase = "Sigla de moeda inválida."
                };
 
                throw new  HttpResponseException(resp);
            }
 
            return SimulacaoCotacao.ObterValorCotacao(siglaMoeda);
        }
    }
}

As chamadas ao Web Service descrito nesta seção utilizarão como endereço-base o valor http://site_service/api/Cotacao/{siglaMoeda}, em que {siglaMoeda} representa um código de moeda (os valores possíveis para este exemplo são “USD”, “EUR” e “LIB”). Para permitir este comportamento será necessário alterar a classe WebApiConfig (gerada automaticamente ao se criar um novo projeto Web API), conforme indicado na listagem detalhada a seguir:

  • O método Register é invocado durante a inicialização da aplicação, de forma a proceder com o registro das diferentes rotas/URLs para acesso às funcionalidades dos serviços disponíveis;
  • A chamada ao método MapHttpAttributeRoutes (a partir do objeto “config”), será responsável por registrar rotas que foram configuradas sob a forma de atributos, os quais podem ter sido vinculados a Actions nos diferentes Controllers de uma aplicação;
  • O acesso à propriedade Routes do objeto “config”, com a consequente invocação do método MapHttpRoute, possibilita a configuração de uma rota personalizada através do preenchimento do parâmetro “routeTemplate” (esta foi na prática a única alteração realizada na classe WebApiConfig).
using System.Web.Http;
 
namespace TesteWebAPI.TiposDinamicos
{
    public static  class WebApiConfig
    {
        public static  void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
 
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{siglaMoeda}",
                defaults: new  { id = RouteParameter.Optional }
            );
 
            config.Formatters.Remove(
                config.Formatters.XmlFormatter);
        }
    }
}

Testes

Será necessário agora iniciar execução da aplicação TesteWebAPI.TiposDinamicos. Para um primeiro teste utilizar a seguinte URL (o valor “59280” corresponde a uma porta disponível no momento da criação do projeto TesteWebAPI.TiposDinamicos):

http://localhost:59280/api/Cotacao/USD

Uma requisição será encaminhada ao serviço de cotações através do utilitário Fiddler, conforme especificado na próxima figura. O Fiddler é uma ferramenta de uso gratuito, empregada geralmente em testes e monitoramento de Web Services.

O retorno deste teste inicial está indicado na imagem seguinte (em que uma string JSON contém os dados do objeto devolvido pelo serviço de cotações):

Em um segundo teste considerar como URL:

http://localhost:59280/api/Cotacao/EUR

Como resultado desta nova requisição enviada ao Web Service será retornado um valor similar ao que consta na próxima imagem (notam-se aqui ligeiras diferenças em relação ao teste anterior, no qual a cotação do dólar norte-americano apresentava as propriedades ValorComercial e ValorTurismo):

Conclusão

Embora não represente um tipo de ocorrência tão comum, há casos nos quais o retorno de uma operação em um Web Service pode variar em virtude dos mais diversos critérios. Ao invés de definir uma classe com propriedades que não seriam preenchidas ou, mesmo, que assumiriam valores default, o uso de um objeto dinâmico pode simplificar a implementação de serviços construídos com a tecnologia ASP.NET Web API.

Referências

ASP.NET Web API
http://www.asp.net/web-api

Fiddler - free web debugging proxy
http://www.telerik.com/fiddler

Walkthrough: Creating and Using Dynamic Objects (C# and Visual Basic)
https://msdn.microsoft.com/en-us/library/ee461504.aspx