Mais acessados

quarta-feira, 20 de fevereiro de 2013

ASP.NET WebForms - Urls simplificadas usando o FriendlyUrls

No dia 18/02/2012 saiu mais um patch de atualização ASP.NET and Web Tools 2012.2 update. Dentre as várias melhorias contidas nesse patch a que mais chamou minha atenção foi o suporte nativo a FriendlyUrls, que torna possível usar o sistema de rotas introduzido no ASP.NET v4.0 de maneira fácil com o WebForms.

Conceito


Para aqueles que já programaram para a plataforma ASP.NET MVC/WebApi notaram a estrutura bem definida das Urls organizadas por diretórios, actions e controllers. Com o lançamento deste novo patch ficou simples disponibilizar em sua aplicação Urls que independem da extensão de seu arquivo WebForm. Isso, graças a uma nova classe helper, FriendlyUrls que disponibiliza uma série de métodos que fazem o "trabalho sujo por trás dos panos".

Implementação


Se você estiver criando um novo projeto ASP.NET WebForms depois de instalar o ASP.NET and Web Tools 2012.2 update as configurações do FriendlyUrls já vem habilitadas por padrão. Caso contrário basta adicionar via NuGet, abrindo o Pack Mananger Console, usando o comando "Install-Package Microsoft.AspNet.FriendlyUrls -Pre".
Com o FriendlyUrls instalado no seu projeto resta somente programação. Criei uma classe chamada RouteConfig para habilitar o FriendlyUrls e no Global.asax usei essa classe para registrar as rotas na tabela.
    public static class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.EnableFriendlyUrls();
        }
    }
 
    public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterOpenAuth();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
O método EnableFriendlyUrls() é um extension method da classe FriendlyUrls que faz todo o trabalho duro. Agora basta digitar o endereço de página sem se preocupar com a extensão .aspx. Uma observação é que se a página estiver continda dentro de um diretório com o mesmo nome desta, ele não consegue resolver a rota.
A classe FriendlyUrls trás alguns métodos interessantes. Na imagem abaixo em amarelo estão a Url que acessei e o caminho virtual obtido com a chamada do método.
 //Recupera o caminho virtual da página
 Request.GetFriendlyUrlFileVirtualPath();
 //Recupera os segmentos da url
 Request.GetFriendlyUrlSegments();
Com esse método é possível recuperar os segmentos da Url. Veja que tudo que vem após o nome da página é considerado como segmento.
 //Ajuda a criar os links para as páginas
 FriendlyUrl.Href("~/Pages/QueryStringPage", "ID", "1001");
Grifado na imagem abaixo está uma Url criada com um método utilitário da classe.
Espero que a dica seja útil! Não deixem de baixar o ASP.NET and Web Tools 2012.2 update. O exemplo que criei pode ser baixado aqui.



segunda-feira, 18 de fevereiro de 2013

ASP.NET Web API / HTML5 - Streaming de áudio e vídeo assincronamente

Muita gente acha que a WebApi é apenas um Framework para desenvolver outros Frameworks. Mas na verdade é uma maneira de usar o máximo de desempenho e controle sobre aplicativos que utilizam HTTP. Neste breve texto irei mostrar como fazer streaming de áudio e vídeo assincronamente usando ASP.NET WebApi.

Conceito básico


Para enviarmos dados assincronamente tiraremos proveito da nova classe PushStreamContent contida no namespace System.Net.Http. Essa classe permite que possamos enviar pacotes por demanda para o cliente. A referência que tive veio do blog Andru's WebLog. Em nosso caso leremos o arquivo do disco do servidor e o enviaremos pouco a pouco para o cliente que visualizará ou escutará a mídia por meio de controles HTML5 de áudio e vídeo.

Implementação


//A classe recebe como construtor uma Action
public PushStreamContent(Action onStreamAvailable);

Criei uma classe de ajuda que lê a mídia do disco do servidor e assincronamente a salva em um buffer e libera para o cliente de acordo com a demanda. Usei as novas técnicas de programação contidas no C# 5.0 async/await para realizar a tarefa.
    public class MediaStream
    {
        private readonly string _filename;

        public MediaStream(string filename)
        {
            this._filename = filename;
        }

        public async void WriteToStream(Stream outputStream, HttpContent content, TransportContext context)
        {
            try
            {
                var buffer = new byte[65536];

                using (var media = File.Open(_filename, FileMode.Open, FileAccess.Read))
                {
                    var length = (int)media.Length;
                    var bytesRead = 1;

                    while (length > 0 && bytesRead > 0)
                    {
                        bytesRead = media.Read(buffer, 0, Math.Min(length, buffer.Length));
                        await outputStream.WriteAsync(buffer, 0, bytesRead);
                        length -= bytesRead;
                    }
                }
            }
            catch (HttpException ex)
            {
                return;
            }
            finally
            {
                outputStream.Close();
            }
        }
    }
Note que a assinatura do método WriteToStream corresponde a assinatura do métodos PushStreamContent. Logo depois criei duas classes, uma para Vídeo e outra para Áudio ambas filhas da classe MediaStream. Fiz dessa maneira para promover uma certa extensibilidade caso seja necessário fazer algo mais complexo mais tarde.
    public class VideoStream : MediaStream
    {
        public VideoStream(string filename) : base(filename)
        {

        }
    }
    public class AudioStream : MediaStream
    {
        public AudioStream(string filename) : base(filename)
        {

        }
    }
Agora vamos para a classe Controller para áudio e vídeo, que ficaram bastante simples após a separação nas classes anteriores.
    public class AudiosController : ApiController
    {
        // GET api/music/5
        public HttpResponseMessage Get(string filename, string ext)
        {
            filename = @"\Content\Audio\" + filename + "." + ext;

            var audio = new AudioStream(filename);

            var response = Request.CreateResponse();
            response.Content = new PushStreamContent(audio.WriteToStream, new MediaTypeHeaderValue("audio/" + ext));

            return response;
        }
    }

    public class VideosController : ApiController
    {
        // GET api/music/5
        public HttpResponseMessage Get(string filename, string ext)
        {
            filename = @"\Content\Audio\" + filename + "." + ext;

            var video = new VideoStream(filename);

            var response = Request.CreateResponse();
            response.Content = new PushStreamContent(video.WriteToStream, new MediaTypeHeaderValue("video/" + ext));

            return response;
        }
    }
O nome do arquivo deve conter o caminho completo do mesmo em seu servidor, logo quando for fazer o seu teste lembre-se disso. Agora para que o cliente possa passar um endereço e consumir os nossos serviços é necessário que este registre uma rota com a padrão correspondendo, neste exemplo estou usando api/extensão/nomeDoArquivo:
config.Routes.MapHttpRoute(
    name: "DefaultVideo",
    routeTemplate: "api/{controller}/{ext}/{filename}"
);

Consumindo os serviços


Basta criar uma página e adicionar nossos controles HTML5:
    

Música

Video

Agora você pode abrir alguma ferramenta como firebug e ver que o conteúdo está sendo transmitido por streaming e não sendo carregado como um todo. Uma observação é que você deve usar o Chrome para fazer seus testes e brincar com este exemplo, eu não consegui fazer funcionar em nenhum outro =). Espero que tenham gostado da dica, o código fonte se encontra abaixo: