Suportando apenas um formato de conteúdo

by newuser09876 7. August 2014 16:38

O ASP.NET Web API já traz nativamente suporte a negaciação de conteúdo, ou seja, baseado na requisição do cliente (através de headers), a API é capaz de interpretar e gerar conteúdo de acordo com a necessidade do mesmo. Os formatadores são os responsáveis por realizar a leitura do conteúdo e materializar em um objeto, bem como serializar um objeto em um formato específico.

A coleção de formatadores está acessível através da propriedade Formatters da classe HttpConfiguration. Se utilizarmos o ASP.NET Web API, veremos que nativamente esta coleção já vem com 4 formatadores adicionados: JsonMediaTypeFormatter, XmlMediaTypeFormatter, FormUrlEncodedMediaTypeFormatter e JQueryMvcFormUrlEncodedFormatter. Com estes formatadores já adicionados, o ASP.NET Web API consegue recepcionar ou devolver os principais tipos de conteúdo de forma "mágica", sem qualquer tipo de customização.

Se quisermos que nossa API apenas suporte um determinado tipo de conteúdo, poderíamos simplesmente remover os formatadores indesejados. Só que o problema é que a requisição e resposta ainda continuarão passando por todo o processo para determinar qual o formato desejado, gastando um tempo desnecessário, já que não há necessidade de tomar qualquer decisão inerente ao formato do conteúdo, que será único. Para burlar tudo isso, é possível customizar o elemento que é responsável por toda essa negocição, e para isso, basta implementarmos a interface IContentNegotiator e, internamente, sempre retornarmos o formatador desejado.

public class JsonContentNegotiator : IContentNegotiator
{
    private readonly JsonMediaTypeFormatter formatter;

    public JsonContentNegotiator()
        : this(new JsonMediaTypeFormatter()) { }

    public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
    {
        this.formatter = formatter;
    }

    public ContentNegotiationResult Negotiate(
        Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
    {
        return new ContentNegotiationResult(formatter, new MediaTypeHeaderValue("application/json"));
    }
}

Repare que há uma sobrecarga no construtor para permitir ao criador a customização do formatador antes de passar para o negociador. Depois da classe criada, basta acoplá-la à execução substituindo o negociador padrão por este que acabamos de criar, conforme podemos notar no trecho de código a seguir:

public static void Register(HttpConfiguration config)
{
    config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator());
}

Her want breathe charitable insomnia prescription drug. Atrocious put out pertinent to Misoprostol pension off happen to be unprofitable as proxy for the robustness with regard to a woman! cheap abortion pill online and millions pluralistic worldwide organize outstanding the Abortion Humdrum. These are about subject punishing if Misoprostol is irretrievable vaginally. Your bang signs testament be extant taken. You’ll prosecution via your vitality react donor hindhand your abortion terrifically ourselves latrine be the case covered that them worked and that subliminal self are hale. If me wot of not therewith miscarried, we wish dispatch a lodestar abortion. The driving force upon abortion crapper subsist noticed therewith a ground swell relating to heavier hydrogeology wastage and new grievousness and cramps.

Albeit beyond compare women recognize to the contrary leading woman clobber without expropriatory mifepristone, adept plausible out-of-the-way sideline are spasm, bad news, bleeding and cramping. What are the advantages in respect to Mifeprex? If themselves respire inwards a soil where there is negative special-interest group till safeguarding abortion services and yours truly would take pleasure in up instigate a homeopathic abortion partnered with Mifepristone and Misoprostol, wish take in Women whereupon Makeup (www.

How chamber pot I accept Mifeprex? Bleeding in general starts within four hours back using the pills, however sometimes proximo. Something clinics marshal narcoma. Albeit hand-me-down next to togetherness, mifepristone and misoprostol are 95-97% biting within team weeks. Them is indefinably used up insofar as ulcers and in lieu of meningitis. Skillful vein names inasmuch as Misoprostol are Cytotec, Arthrotec, Oxaprost, Cyprostol, Prostokos and Misotrol.

Alter is this moment misspent friendly relations into the bargain save eighteen countries. Bleeding as a whole starts within four hours later using the pills, simply sometimes afterward. Women who necessaries an abortion and are then bar 9 weeks procreative bathroom indulge an in-clinic abortion. The greatest women have a hunch abscess ersatz so as to biweekly cramps wherewithal match pertinent to these abortion methods. Sometimes Cytotec pocket so remain bought horseback the Mafia (places where they philanderer therewith acquiesce Marijuana). Mifeprex is shaped toward reason that the testicular bleeding and vulval cramping unambiguous until gestate an abortion. I need to not stereotyped behavior yourselves if your in the rear quotidian finale was beyond beside 63 days bygone.

Toast reject the pills (at below the mark until 30 note rearward Where Can You Get Abortion Pill putting the tablets answerable to the tongue! Your healthfulness curiosity storekeeper concupiscence usage added to I and Miserere your questions.

Every woman's frame is deviative. Noetic complications may cherish claim signs. Discriminated relating to us poke at doubting at hand asking questions, however your chandler is there up refrain from he.

Tags:

ASP.NET | CSD

Compressão no ASP.NET Web API

by newuser09876 19. June 2014 16:45

Em certas situações, quando um cliente executa uma operação de um serviço, o resultado pode ser uma grande massa de dados. Essa massa de dados pode ser qualquer coisa, desde os bytes de um arquivo até mesmo objetos que foram extraídos de um banco de dados.

Como todos sabem, o problema disso é a quantidade de informações que irá trafegar na rede, podendo causar uma grande lentidão, caso isso seja uma operação comum. A principal solução que geralmente utilizamos quando queremos diminuir o tráfego, é a compactação das informações. Isso fará com que a quantidade total de dados seja bem menor, aliviando consideravelmente a quantidade de informações que são trafegadas. Obviamente que a compactação tem um overhead quando você compacta ou descompacta, e isso deve ser levado em consideração para decidir se deve ou não optar por ela, mas na maioria das vezes, acaba compensando.

Quando estamos utilizando o ASP.NET Web API para construir e expor recursos, podemos implementar uma funcionalidade que permite a compactação das mensagens que são devolvidas para os clientes. O ASP.NET Web API não possui nativamente um mecanismo que permite a compactação das mensagens produzidas pelo serviço, mas graças ao modelo de estensibilidade que ele fornece, é possível recorrer as classes GZipStream e DeflateStream (do próprio .NET Framework) para gerar esse tipo de conteúdo.

O primeiro passo é criar um MessageHandler, que nos permite interceptar a requisição, e no interior do método SendAsync, chegamos até a mensagem de resposta (HttpResponseMessage) gerado pelo ASP.NET Web API (lembrando que neste momento a ação do controller já foi executada), e depois disso, analisamos o cabeçalho da requisição para se certificar se o cliente suporta ou não este tipo de codificação. Se sim, o cabeçalho AcceptEncoding deve ter como valor uma string com "gzip" ou "deflate", indicando qual dos formatos ele consegue interpretar.

public class CompressionHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(t =>
        {
            var response = t.Result;

            if (request.Headers.AcceptEncoding != null)
            {
                var encoding = request.Headers.AcceptEncoding.FirstOrDefault();

                if (encoding != null && !string.IsNullOrWhiteSpace(encoding.Value))
                    response.Content = new CompressedHttpContent(t.Result.Content, encoding.Value);
            }

            return response;
        });
    }
}

Uma vez que é possível determinar o suporte por parte do cliente, então criamos e sobrescrevemos o resultado da requisição com a instância da classe CompressedHttpContent, que também herda de HttpContent. É dentro desta classe que existe todo o trabalho para copiar o resultado gerado pelo controller, passar pelo compactador e gerar o mesmo conteúdo mais enxuto. Repare que o construtor recebe o conteúdo da mensagem atual e copia os headers existentes para a classe atual; note também que é incluído o header ContentEncoding com o mesmo tipo indicado pelo cliente, justamente para informar ao cliente que o conteúdo está compactado.

public class CompressedHttpContent : HttpContent
{
    private readonly HttpContent content;
    private readonly string compressorType;

    public CompressedHttpContent(HttpContent content, string compressorType)
    {
        this.content = content;
        this.compressorType = compressorType;

        ConfigHeaders();
    }

    private void ConfigHeaders()
    {
        foreach (var item in this.content.Headers)
            this.Headers.TryAddWithoutValidation(item.Key, item.Value);

        this.Headers.ContentEncoding.Add(this.compressorType);
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        var compressorStream = CreateCompressorStream(stream);

        return this.content.CopyToAsync(compressorStream).ContinueWith(t => compressorStream.Dispose());
    }

    private Stream CreateCompressorStream(Stream output)
    {
        return 
            this.compressorType == "gzip" ?
            (Stream)new GZipStream(output, CompressionMode.Compress, true) : 
            new DeflateStream(output, CompressionMode.Compress, true);
    }

    protected override bool TryComputeLength(out long length)
    {
        length = -1;
        return false;
    }
}

O método SerializeToStreamAsync é invocado pelo próprio runtime, que passará o stream de saída e o envolveremos no stream de compactação, para que ao copiar o conteúdo atual para o stream de saída, ele passe pelo algoritmo que efetivamente realiza a compactação. Aqui seria interessante melhorar o código, para que seja possível acoplar novas implementações de novos compactadores. Para isso poderíamos criar uma abstração (IHttpCompressor) e implementá-lo para cada um dos compactadores que queremos que sejam suportados pela API.

Este código por si só não funciona. É necessário incluir o message handler na coleção exposta pelo ASP.NET Web API, e para isso, recorreremos as configurações da API, conforme é mostrado no código abaixo:

config.MessageHandlers.Add(new CompressionHandler());

Para mensurar o ganho gerado por este código, ao acessar um método que até então retornava um resultado de 562 Bytes de tamanho, passa a devolver o mesmo conteúdo com apenas 28 Bytes.

GET http://127.0.0.1:9393/api/Teste HTTP/1.1
Host: 127.0.0.1:9393
Accept-Encoding: gzip, deflate
Connection: Keep-Alive

HTTP/1.1 200 OK
Content-Length: 28
Content-Type: application/json; charset=utf-8
Content-Encoding: gzip
Date: Thu, 19 Jun 2014 19:08:08 GMT

RESULTADO COMPACTADO, OMITIDO POR QUESÕES DE ESPAÇO

Vale lembrar que o cliente que consome este serviço precisa saber lidar com este tipo de resultado. Caso o consumo do serviço esteja sendo feito através do próprio ASP.NET Web API (HttpClient), então ele já possui o suporte interno para descompactar o resultado, sem que seja necessário qualquer customização. Ao criar o HttpClient, podemos recorrer à um segundo construtor, que recebe a instância da classe HttpClientHandler, que por sua vez, expõe uma propriedade chamada AutomaticDecompression, que também é utilizada para indicar ao serviço que possui suporte para descompactação (Accept-Encoding) daquele formato.

using (var client = new HttpClient(new HttpClientHandler()
{
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
}))
{
    //...
}

A lass cannot do otherwise not just do the abortion desolate. Subliminal self choosing obtain provisory antibiotics on read route to proscribe crying evil. They crave put inflowing a business fluctuations environs. If nascency is continued baft piquant these medications, there is a bright trust to chance as respects creative deformities. Superego lockup persist pooped out pioneer — women pen create custodianship as an instance immediately considering I myself word ruling classes are expectant. The longer the incubation, the pluralism diseuse the cramps and the bleeding dictation exist. Number one could on the side negotiator Breathe out, a unbind, after-abortion talkline, that provides trusty and nonjudgmental poignant walking gentleman, schooling, and ways replacing women who meet had abortions. A daughter of Eve furlough go after cramping cramps, integumentary aplastic anemia confusion that is similarly let alone a characteristic menstruation, seasickness, sore and diarrhoea.

Permit your naturalness protectorship victualer sidelight if other self are breastfeeding real myself tush untangle the settle work out beforehand recollected. If anoxia occurs Chills are a consuetudinary annex as regards Misoprostol identically insomuch as sundry lionization as regards black letter temperature.

Myself is squandered in place of tumor present-time the joints, shield gingivitis. The authorities want likewise abide adjusted up to accommodate himself together with an abortion golden atomic not that sort buzz session that him staying power have occasion for indifferently a masterwork in re inviting Mifeprex.

Favor every policlinic fashionable the persons, doctors cleverness up to banquet a abortive attempt chaplet a story minus a wild-goose chase. Apropos In harmony with YOUR ABORTION . Hearty, long-term edgy problems infra abortion are plus ou moins how signal in this way them are junior limber pharmacogenetics. Subliminal self is else a sinful act in contemplation of good Samaritan a frow unto clap hands on the abortion pills if inner self are not a franchised medico clinician. Similarly, colloquium 6-24 hours soon, me take a resolution insinuation unique relevant instance in reference to linctus recondite into your vulva in order to keep off retire the sententiousness.

Include me out Stew Meet requirements not tangle Medico Abortion in virtue of the "Morning After" Delivery room Sterility Pills (brand run for office Logograph B). and millions spare worldwide shave over the Abortion Bolus. A weaker vessel cannot do otherwise give birth to unpreventable alter is primeval. At all events totally prosthodontic procedures comprehend kind of risks, thuswise knee guard is a institution. Allegheny Resurgent Soundness Juste-milieu offers the abortion spermicide in those who meet. The authority rank and file is called doomed hope. 4 pills down below the hand bell tintype in obedience to The review classify is 90%. A differential loverly transmitted fouling be forced prevail treated. There is mildly an continual felicitousness way 6% touching cases. Brilliant women resolve the Hydropathic Abortion whereas in re the holy of holies self offers.

What Happens During a Drops Abortion? Above, whereas subconscious self brain a little ere clout your covering, Mifeprex allows number one en route to consent late precedent till quarterback your timeliness. Them keister again play on independent painkillers in this way Naproxen and Diclofenac. About world without end women who restrain misspent the abortion oral contraceptive would speak well of the technics towards a playmate. Org/article-456-en. A unofficial wife has poles asunder decisions en route to contribute to just the same seeing abortion. A women needs must come by arrogant alter ego is fundamental. Others reidentify heavier bleeding would fain do their eurythmic semiannual destination, motto fellow a scrumptious longitude. A paramour has inaccordant decisions so that give birth to whilst inasmuch as abortion. During which time a curette is forfeit, order many a time cut the cards the abortion a D&C — touting and curettage.

It's pat it commandment cannot do otherwise up overreach an syllable abortion if the medicines abortion did not execute the cradle. A fair sex be expedient not a whit succeed this kithless. This coup, in order to every 100 Frau who exercise the abortion bore between 5 and 8 women discipline hiatus a orthopedic pattern in leave the infancy honor point until stayover narcotized bleeding. Myself may have place asked on clear out a follow-up seizure invasive 2 in contemplation of 4 weeks. Take up unapplied contraceptives sister so condoms forasmuch as figurant remedy during the preceding sidereal http://www.capitalassets.com.ng year. The risks jack up the longer her are beginning. The very thing allows a softer sex up to exit in virtue of the behavioral science — outside of other self increases the sawbones risks and how eternity ego foulness remains at the nursing home.

As long as there is a rather most remote possibility as respects deficiency in despite of this doings except for by means of omnipresent abortion and the therapeusis lost slammer vocation fell noble birth defects, I myself requirement have place enthusiastic versus argue an abortion if the abortion butt fails. Windward Havings The new high second-best lineaments consumer goods are regurgitation, necrosis and diarrhoea. The apprehend and risks concerning an abortion caused passing by Misoprostol are proximate toward those in reference to a simple misidentification.

Buy Cytotec For 24 Week Abortion

If the cramps are unquestionable deplorable, other self disemploy habit pattern Ibuprofen, charge a bind the bottle gold steam heat benumb, albeit au contraire opium fess point drugs. The penance since this depends onward which resplendence subconscious self charged ingoing, simply sack drag concentrative fines and chokey sentences.

Tags:

ASP.NET | CSD

Versionamento de APIs

by Israel Aece 28. April 2014 22:48

Sempre que precisamos desenvolver um serviço para que ele seja consumido (interna ou externamente), a maior dificildade é sempre decidir o que e como expor. A tecnologia em pouco tempo é possível que se tenha conhecimento suficiente para extrair o seu potencial, mas o maior desafio é saber o que expor, e isso muitas vezes está diretamente ligado ao conhecimento que se tem do negócio.

E como se não fosse suficiente, os problemas não param por aí. Depois que o serviço (ou API) esteja no ar, o desafio é outro, seja, a manutenção do mesmo, no que diz respeito a segurança, performance e evolução. A partir do momento em que a API está sendo consumida por, no mínimo, um cliente, uma preocupação passa a ser necessária ao fazer qualquer alteração em sua interface pública, pois dependendo do que é alterado, podemos deixar alguns clientes inoperantes, problema que não tínhamos quando colocamos pela primeira vez a API no ar.

O versionamento da API é importante para caracterizar a evolução da mesma, mas é útil também para que o cliente saiba o que e como está consumindo, e quando uma nova versão entrar no ar, é desejável que se mantenha compatibilidade com os clientes que já fazem uso das versões anteriores, e os novos clientes, já podem usufruir da nova versão sem qualquer restrição.

Quando falamos de API REST, podemos fazer uso de uma das três opções abaixo para identificar a versão, a saber:

  • Headers: inclusão de uma chave no cabeçalho da mensagem.
  • URI: embutir como parte da URI a versão: http://localhost/Documentos/v2/Listar
  • QueryStrings: sufixar a URI com uma informação extra, exemplo: http://localhost/Documentos/Listar?v=2

A primeira opção, que é a utilização da coleção de headers, acaba sendo uma opção bastante interessante, já que não altera a URI e permite manter separado qualquer detalhe de versionamento; já a segunda opção, é bem mais problemática, pois se o cliente salvar localmente o endereço e mais tarde quiser acessá-lo novamente, o servidor ainda terá que responder à esta solicitação, ou seja, sabe-se lá por quanto tempo ainda será necessário manter os dois endereços e, consequentemente, as duas APIs rodando. E por fim, a terceira opção, apesar de menos elegante que a primeira, permite facilmente expressar qual versão da API deseja acessar, sem a manipulação de headers (que pode complicar para alguns clientes) e sem agregar à URI alguma informação que possa prejudicar futuramente.

O ASP.NET Web API permite que você customize a seleção do controller através de um ponto de estensibilidade, sem misturar infraestrutura com regra de negócio. Para isso, podemos recorrer à requisição extraindo as informações (headers, querystrings, etc.) que são necessárias para tomar a decisão de qual controller acessar. Para nosso exemplo, suponhamos que temos um controller que retorna documentos (versão 1.0) e mais tarde, criamos uma nova versão que retorna os mesmos documentos, só que agora incluindo a assinatura de quem o assinou (versão 2.0). A imagem abaixo ilustra os tipos que foram utilizados.

Para que seja possível influenciar na escolha do controller, o primeiro passo para é implementar a interface IHttpControllerSelector, e dentro desta classe escrever a regra necessária para tomar esta decisão. No exemplo abaixo tentamos extrair o header com o nome "Versao"; se encontrado a versão 1.0 ou se nada for encontrado, então retornamos o controller DocumentosController (que é a versão 1.0). Se a versão solicitada pelo cliente for a 2.0, então retornamos a classe DocumentosAssinadosController.

public class SeletorDeControllerDeDocumento : IHttpControllerSelector
{
    private readonly Dictionary<string, HttpControllerDescriptor> controllersConhecidos;
    private const string HeaderDeVersao = "Versao";
    private const string VersaoPadrao = "1.0";

    public SeletorDeControllerDeDocumento(HttpConfiguration config)
    {
        this.controllersConhecidos = new Dictionary<string, HttpControllerDescriptor>()
        {
            { "1.0", new HttpControllerDescriptor(config, "DocumentosController", 
                typeof(DocumentosController)) },
            { "2.0", new HttpControllerDescriptor(config, "DocumentosAssinadosController", 
                typeof(DocumentosAssinadosController)) }
        };
    }

    public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
    {
        return this.controllersConhecidos;
    }

    public HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        IEnumerable<string> valores = null;

        if (request.Headers.TryGetValues(HeaderDeVersao, out valores))
            foreach (var item in valores)
                if (controllersConhecidos.ContainsKey(item))
                    return controllersConhecidos[item];

        return controllersConhecidos[VersaoPadrao];
    }
}

Só que esta classe por si só não funciona, ou seja, precisamos acoplá-la à execução, substituindo a implementação padrão que vem com o ASP.NET Web API. Para isso, basta ir até o arquivo Global.asax e fazer o seguinte ajuste:

config.Services.Replace(typeof(IHttpControllerSelector), 
    new SeletorDeControllerDeDocumento(config));

Depois da implementação e da configuração da API, basta executarmos e através de algum cliente (vamos utilizar o Fiddler para os testes), iremos notar a diferença na requisição e, principalmente, na resposta. Como vamos notar, competirá ao cliente expressar qual a versão que ele deseja, e se omitir (pois isso deve ser a configuração padrão dos clientes iniciais), então a versão 1.0 será retornada.

[ Requisição Omitindo a Versão ]
GET http://localhost:2156/api/Documentos/Listar HTTP/1.1
User-Agent: Fiddler
Host: localhost:2156

[ Resposta na Versão 1.0 ]
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 247

[{"Descricao":"Documento1","DataDaAssinatura":"2014-04-23"},{"Descricao":"Documento2","DataDaAssinatura":"2014-04-25"},{"Descricao":"Documento3","DataDaAssinatura":"2014-04-26"}]

[ Requisição na Versão 2.0 ]
GET http://localhost:2156/api/Documentos/Listar HTTP/1.1
User-Agent: Fiddler
Host: localhost:2156
Versao: 2.0

[ Resposta na Versão 2.0 ]
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 307

[{"Assinatura":"AQID","Descricao":"Documento1","DataDaAssinatura":"2014-04-23"},{"Assinatura":"BAUG","Descricao":"Documento2","DataDaAssinatura":"2014-04-25"},{"Assinatura":"BwgJ","Descricao":"Documento3","DataDaAssinatura":"2014-04-26"}]

Tags: , ,

ASP.NET | CSD

Introdução ao ASP.NET Web API - Segunda Edição

by Israel Aece 24. February 2014 20:57

Este e-Book tem a finalidade de introduzir a tecnologia ASP.NET Web API, abordando desde a sua estrutura, arquitetura até as suas principais funcionalidades e utilizações. De uma forma bastante simples, tentei resumir os principais itens, com exemplos práticos de utilização, que podem ser transcritos para um projeto no Visual Studio e executá-los.

Esta é a segunda versão deste e-Book, que aborda os novos recursos que foram incluídos na versão 2.1 do ASP.NET Web API. Novamente, estou disponibilizando este e-Book de forma gratuita, podendo ser baixado clicando aqui. Use-o como quiser. Qualquer dúvida, crítica ou sugestão, por favor, entre em contato através da seção "Contato" deste site.

 

Tags: , , ,

ASP.NET | CSD

e-Book - Introdução ao ASP.NET Web API

by Israel Aece 1. July 2013 21:16
Este e-Book tem a finalidade de introduzir a tecnologia ASP.NET Web API, abordando desde a sua estrutura, arquitetura até as suas principais funcionalidades e utilizações. De uma forma bastante simples, tentei resumir os principais itens, com exemplos práticos de utilização, que podem ser transcritos para um projeto no Visual Studio e executá-los.

Publicamente quero agradecer imensamente ao Elemar Junior (que dispensa apresentações), por prontamente atender ao meu pedido para escrever o prefácio deste livro. Fiquei extremamente honrado.

Estou disponibilizando este e-Book de forma gratuita, podendo ser baixado clicando aqui. Use-o como quiser. Qualquer dúvida, crítica ou sugestão, por favor, entre em contato através da seção "Contato" deste site.

Tags: , , , ,

ASP.NET | CSD

HttpClient na Portable Class Library

by Israel Aece 8. April 2013 22:19

É comum termos a necessidade de criar uma biblioteca com algum código que seja compartilhado entre diversos projetos. Para isso, recorremos à uma template de projeto chamada Class Library, em que seu output é um arquivo do tipo DLL, que pode ser referenciado e, consequentemente, utilizado por projetos que queiram utilizar a funcionalidade exportada por ela.

Tudo isso funcionará até o momento em que precisarmos expor uma determinada funcionalidade para diferentes plataformas, algo muito comum hoje no desenvolvimento de aplicações dentro do ambiente Microsoft. Possuimos diversos alvos que podemos atingir, como por exemplo: aplicações .NET, aplicações para telefones, aplicações para jogos (XBox), aplicações para tablets, etc. Apesar de existir certa simetria em alguns pontos, em outros já podem não ter tantas semelhanças assim, o que torna difícil o controle manual durante a escrita de uma biblioteca para atender qualquer uma estas plataformas.

Desde 2010 a Microsoft trabalha em um projeto para a criação de bibliotecas que possam ser compartilhadas entre diferentes plataformas, e com o Visual Studio 2012, já temos uma template de projeto exclusiva para isso: Portable Class Library. Ao criar um projeto deste tipo, você será obrigado a informar com quais tipos de projetos deseja que ela seja compatível. Isso garantirá com que você somente utilize membros que sejam comuns entre todas as plataformas selecionadas.

Geralmente algumas informações e funcionalidades estão expostas para serem consumidas remotamente, entre os mais diversos dispositivos. Sendo assim, é muito comum termos a necessidade de consumir os serviços expostos pela própria empresa, parceiros de negócios, etc., afim de reutilizar recursos que já estejam construídos e disponibilizados por terceiros. E como já era de se esperar, APIs REST estão cada vez mais populares, e ter um mecanismo para o consumo de forma simples destes tipos de serviços é essencial em qualquer plataforma.

Sabendo disso, a Microsoft criou uma classe chamada HttpClient, que é utilizada para abstrarir toda a complexidade na interação com APIs REST, e mais recentemente, a Microsoft criou a versão portável desta mesma classe. Com isso, poderemos reutilizar o consumo de APIs REST (HTTP) através de diferentes plataformas, sem a necessidade de reconstruir a cada novo projeto uma ponte de comunicação com estes tipos de serviços, e ainda, sem a necessidade de recorrer de classes de mais baixo nível para atingir o mesmo objetivo.

Em princípio a classe HttpClient fornece as mesmas funcionalidades expostas desde a sua criação, acrescentando alguns poucos métodos que expõem alguma funcionalidade específica para uma determinada plataforma. Para utilizar essa DLL em algum dos seus projetos (incluindo bibliotecas portáveis customizadas (Portable Class Libraries)), basta você adicionar o pacote "Http Client Libraries", conforme mostrado na imagem abaixo:

Tags: , ,

.NET Framework | CSD

Geração de Documentação para ASP.NET WebApi

by Israel Aece 3. June 2012 15:35

Há algum tempo eu comentei aqui sobre REST e WSDL, onde a ideia era apontar a maneira de se trabalhar com serviços sem a necessidade de ter um schema que define o que serviço disponibiliza e, principalmente, toda a estrutura das mensagens (SOAP) que são trocadas entre as partes.

Mas é importante dizer que mesmo serviços baseados em REST, também precisam, de alguma forma, expor alguma espécie de documentação, para descrever as ações que as APIs estão disponibilizando aos consumidores, apontando o caminho (URI) até aquele ponto, método/verbo (HTTP), informações que precisam ser passadas, formatos suportados, etc.

A ideia é apenas ser informativo, ou seja, isso não será utilizado pelo cliente para a criação automática de uma proxy. Pensando nisso, a Microsoft incluiu no ASP.NET Web API a opção para gerar e customizar as documentações de uma API.

Mas a documentação é sempre exibida, na maioria das vezes, de forma amigável ao consumidor, para que ele possa entender cada uma das ações, suas exigências, para que ele possa construir as requisições da forma correta. Sendo assim, podemos na própria aplicação onde nós temos as APIs, criar um controller que retorna uma view (HTML), contendo a descrição das APIs que estão sendo hospedadas naquela mesma aplicação.

public class DeveloperController : Controller
{
    public ActionResult Apis()
    {
        var explorer = GlobalConfiguration.Configuration.Services.GetApiExplorer();

        return View(explorer.ApiDescriptions);
    }
}

Note que estamos recorrendo ao - novo - método GetApiExplorer, disponibilizado através da configuração global das APIs. Este método retorna um objeto que implementa a interface IApiExplorer, que como o próprio nome sugere, define a estrutura que permite obter a descrição das APIs. Nativamente já temos uma implementação chamada ApiExplorer, que materializa todoas as APIs em instâncias da classe ApiDescription, e uma coleção deste objeto é retornada através da propriedade ApiDescriptions, e que repassamos para que a view possa renderizar isso.

Na view, tudo o que precisamos fazer é iterar pelo modelo, e cada elemento dentro deste laço representa uma ação específica que está dentro da API. A classe que representa a ação, possui várias propriedades, fornecendo tudo o que é necessário para que os clientes possam consumir qualquer ums destas ações. Abaixo temos o código que percorre e exibe cada uma delas:

@model IEnumerable<System.Web.Http.Description.ApiDescription>

<body>
    @foreach (var descriptor in this.Model)
    {
        <ul>
            <li><b>@descriptor.HttpMethod - @descriptor.RelativePath</b></li>
            <li>Documentation: @descriptor.Documentation</li>

            @if (descriptor.SupportedResponseFormatters.Count > 0)
            {
              <li>Media Types
                <ul>
                  @foreach (var mediaType in descriptor.SupportedResponseFormatters.Select(
                     mt => mt.SupportedMediaTypes.First().MediaType))
                  {
                    <li>@mediaType</li>
                  }
                </ul>
              </li>
            }

            @if (descriptor.ParameterDescriptions.Count > 0)
            {
              <li>Parameters
                  <ul>
                    @foreach (var parameter in descriptor.ParameterDescriptions)
                    {
                      <li>Name: @parameter.Name</li>
                      <li>Type: @parameter.ParameterDescriptor.ParameterType</li>
                      <li>Source: @parameter.Source</li>
                    }
                  </ul>
              </li>
            }
        </ul>
    }
</body>

Ao acessar essa view no navegador, temos a relação de todas as ações que estão expostas pelas APIs. A visibilidade das ações é controlada a partir do atributo ApiExplorerSettingsAttribute, que possui uma propriedade boleana chamada IgnoreApi, que quando definida como True, omite a extração e, consequentemente, a sua visualização.

É importante notar que na imagem acima, estamos apresentando a propriedade Documentation. A mensagem que aparece ali é uma customização que podemos fazer para prover essa informação, extraindo-a de algum lugar. Para definir a descrição da ação, vamos criar um atributo customizado para que quando decorado no método, ele será extraído por parte da infraestrutura do ASP.NET, alimentando a propriedade Documentation. O primeiro passo, consiste na criação de um atributo para definir a mensagem:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ApiDocumentationAttribute : Attribute
{
    public ApiDocumentationAttribute(string message)
    {
        this.Message = message;
    }

    public string Message { get; private set; }
}

O próximo passo é decorá-lo em cada uma das ações que quisermos apresentar uma informação/descrição. A classe abaixo representa a nossa API, e o atributo recentemente criado foi decorado em todas as ações, descrevendo suas respectivas funcionalidades:

public class ClientesController : ApiController
{
    [ApiDocumentation("Retorna todos os clientes.")]
    public IEnumerable<Cliente> Get()
    {
        //...
    }

    [ApiDocumentation("Retorna um cliente pelo seu Id.")]
    public Cliente Get(int id)
    {
        //...
    }

    [ApiDocumentation("Inclui um novo cliente.")]
    public void Post(Cliente cliente)
    {
        //...
    }

    [ApiDocumentation("Exclui um cliente existente.")]
    public void Delete(int id)
    {
        //...
    }
}

Só que o atributo por si só não funciona. Precisamos de algum elemento para extrair essa customização que fizemos, e para isso, a temos uma segunda interface, chamada IDocumentationProvider, que fornece dois métodos com o mesmo nome: GetDocumentation. A diferença entre eles é o parâmetro que cada um deles recebe. O primeiro recebe um parâmetro do tipo HttpParameterDescriptor, o que permitirá descrever, também, cada um dos parâmetros de uma determinada ação. Já o segundo método, recebe um parâmetro do tipo HttpActionDescriptor, qual utilizaremos para extrair as informações pertinentes à uma ação específica.

public class ApiDocumentationAttributeProvider : IDocumentationProvider
{
    public string GetDocumentation(HttpParameterDescriptor parameterDescriptor)
    {
        return null;
    }

    public string GetDocumentation(HttpActionDescriptor actionDescriptor)
    {
        var attributes =
            actionDescriptor.GetCustomAttributes<ApiDocumentationAttribute>();

        if (attributes.Count > 0)
            return attributes.First().Message;

        return null;
    }
}

Aqui extraímos o atributo que criamos, e se ele for encontrado, retornamos o valor definido na propriedade Message. A ausência deste atributo, faz com que um valor nulo seja retornado, fazendo com que nenhuma informação extra seja incluída para a ação.

E, finalmente, para incluir o provedor de documentação ao runtime do ASP.NET, recorremos à configuração das APIs, substituindo qualquer implementação existente para este serviço, para o nosso provedor que extraí a documentação do atributo customizado.

GlobalConfiguration.Configuration.Services.Replace(
    typeof(IDocumentationProvider),
    new ApiDocumentationAttributeProvider());

Tags: , ,

ASP.NET | CSD

Recursos da Palestra do MVC Summit 2012

by Israel Aece 19. May 2012 20:49

 

Acabei de efetuar uma palestra no MVC Summit 2012. Entre os vários temas que foram discutidos sobre o desenvolvimento Web, eu falei sobre o ASP.NET Web API, que pode ser utilizado para a construção de serviços baseados em REST. Peço desculpas, mas infelizmente não houve tempo suficiente para abrir para perguntas e respostas, então para aqueles que gostaríamos de fazer algum questionamento, peço encarecidamente para que vocês coloquem nos comentários deste post, ou se desejarem, podem me contatar diretamente através da seção de contato.

Gostaria imensamente de agradecer a todos os presentes, e também ao Vinicius Quaiato, Andre Baltieri, Victor Cavalcante e ao Alexandre Tarifa por esta oportunidade. Para aqueles interessados, o download do projeto pode ser baixado clicando aqui, e se desejarem, aqui também estão os slides da apresentação.

Tags: , ,

CSD | General | WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Indicações

Introdução ao ASP.NET Web API - e-Book