Acessando ações diretamente

by Israel Aece 29. June 2010 16:06

Em algumas situações dentro de uma aplicação ASP.NET MVC, há ações dentro dos controllers que não podem ser invocadas diretamente a partir da URL, ou seja, somente estarão acessíveis através de métodos que geram o resultado (HTML) "in-line", ou seja, em algum ponto da página. Um exemplo disso é o uso dos métodos Action e RenderAction.

Como exemplo, há métodos que recebem como parâmetro o nome de uma ação e, eventualmente, o nome do controller, e o resultado irá variar de acordo com o método que você utiliza. Um desses métodos, chamado de Action, retornará um objeto do tipo MvcHtmlString, que colocará o resultado diretamente no local especificado dentro do HTML da página; enquanto o método RenderAction adiciona o conteúdo diretamente no objeto HttpResponse, que em alguns cenários, pode ser mais performático.

Muitas vezes, estas ações devem ser acessadas somente através destes métodos, e com isso um usuário não deveria acessá-la diretamente através do navegador. Para evitar que isso aconteça, você pode recorrer a um novo atributo, incluído na versão 2.0 do ASP.NET MVC, chamado ChildActionOnlyAttribute. Este atributo pode ser aplicado tanto em classes quanto em métodos, e quando o runtime do ASP.NET encontrá-lo, irá negar o acesso à mesma caso esteja sendo acessada diretamente através navegador. Para exemplificar, considere o controller abaixo, que gerencia os usuários do sistema:

public class UsuariosController : Controller
{
    public ActionResult Cadastrar()
    {
        return View();
    }

    [ChildActionOnly]
    public ActionResult VerificarExistenciaDoUsuario(string nome)
    {
        return PartialView();
    }
}

Agora, no código HTML da página ASPX da ação Cadastrar, podemos utilizar um dos métodos que falamos acima, especificando o nome da ação marcada com o atributo tema deste artigo, e com isso, o resultado gerado será colocado no mesmo local onde as tags <% %> estão localizadas. Abaixo podemos visualizar a sua utilização:

<div>
    <% Html.RenderAction("VerificarExistenciaDoUsuario"); %>
</div>

Note que ao navegar pela aplicação através do endereço Usuarios/Cadastrar, visualizamos o conteúdo normalmente, mas se tentarmos acessar o endereço Usuarios/VerificarExistenciaDoUsuario, a seguinte mensagem é exibida: The action 'VerificarExistenciaDoUsuario' is accessible only by child request. Mensagem essa, que não aconteceria se você omitir o atributo ChildActionOnlyAttribute.

Além da forma declarativa que vimos acima, você pode controlar se a chamada está ou não sendo realizada diretamente através do navegador. Para isso, basta recorrermos à propriedade IsChildAction da classe ControllerContext, que retorna um valor boleano indicando se ela está ou não sendo acessada diretamente. Agora, podemos tomar alguma decisão em cima desta propriedade, algo que é impossível de realizar quando utilizamos o modo declarativo.

Tags:

ASP.NET

Utilizando jQuery para invocar Actions

by Israel Aece 5. May 2010 16:51

Com jQuery podemos acessar serviços WCF para tornar a experiência do usuário muito melhor, evitando a atualização completa da página. Neste artigo vimos como proceder para preparar um serviço WCF para ser exposto e ser consumido via AJAX, e além disso, exploramos a API do jQuery para efetuar a comunicação com esse serviço.

Só que muitas vezes, para ter a riqueza necessário do lado do cliente, tudo o que precisamos fazer é consumir métodos que estão no código C#/VB.NET, dentro da própria aplicação. Se a aplicação está baseada no ASP.NET MVC, talvez tenhamos a necessidade de invocar uma ação (action) de um determinado controller. Felizmente o jQuery não está limitado à executar apenas métodos expostos por serviços WCF, mas também podemos acessar os métodos criados nos controllers, utilizando a mesma API. A finalidade deste artigo é mostrar como proceder para atingir esse objetivo.

Como todos sabemos, todas as actions de um controller sempre devem retornar a instância de uma das classes derivadas de ActionResult, e como o próprio nome diz, é responsável por receber, armazenar e devolver o resultado da respectiva ação. Uma das classes derivadas dela é a JsonResult, que representa o resultado em formato JSON, que é o formato mais conveniente para o jQuery trabalhar.

Sendo assim, para aquelas actions que desejamos consumir através do jQuery, precisamos retornar uma instância da classe JsonResult, abastecendo a propriedade Data com algum objeto que desejamos visualizar do lado do cliente. Essa propriedade recebe um System.Object, o que nos permite definirmos qualquer objeto, incluindo tipos anônimos. A partir da versão 2.0 do ASP.NET MVC, a Microsoft adicionou nesta classe uma nova propriedade chamada de JsonRequestBehavior, que recebe uma das duas opções fornecidas pelo enumerador com o mesmo nome. Basicamente, a ideia desta propriedade é controlar se o acesso via GET é permitido ou não. Por questões de segurança, essa propriedade é definida com a opção DenyGet, o que nega a possibilidade de conseguir invocar através de GET. Você pode trocar para a opção AllowGet, mas é importante que você conheça os eventuais problemas que podem acontecer se isso estiver habilitado. Com esse conhecimento da classe JsonResult, podemos ter a seguinte action:

public class UsuariosController : Controller
{
    public ActionResult RecuperarUsuario()
    {
        return new JsonResult()
        {
            Data = new Usuario() { Codigo = 123, Nome = "Israel" }
        };
    }
}

Depois do controller e da action criados, vamos recorrer ao jQuery para consumir este método. O jQuery fornece alguns atalhos, que evita a configuração total da função $.ajax. Um desses atalhos é a função $.getJSON, que dado uma URL e um callback, podemos referenciar o método criado anteriormente, e com a função de callback processar o resultado. O problema é que a função $.getJSON efetua a solicitação via GET, e como vimos acima, não estamos permitindo isso (JsonRequestBehavior). Sendo assim, vamos recorrer a função de baixo nível $.ajax, configurando apenas o que é necessário para executar o método:

function RecuperarUsuario() {
    $.ajax(
    {
        type: "POST",
        url: "/Usuarios/RecuperarUsuario",
        contentType: "application/json",
        success:
            function (resultado) {
                alert(resultado.Codigo);
                alert(resultado.Nome);
            },
    }

Como percebemos no código acima, ele está invocando o método RecuperarUsuario no controller Usuarios, através de POST, e quando o resultado é devolvido, exibimos a mensagem na tela. Note que há uma barra (/) antes do nome do controller. Isso é necessário porque a view corrente, que possui o código jQuery, não faz parte deste controller, o que nos obriga a mencionar a partir no nível raiz. Se quisermos acessar uma action que está no controller da view, então tudo o que é necessário é informar o nome da mesma, sem mencionar o controller.

Já quando a action exigir parâmetros, é necessário informá-los durante a chamada. Se modificarmos a action para ela passar a receber dois parâmetros, sendo o código e o nome do usuário, precisamos informar isso na chamada para o método, ficando da seguinte forma:

function RecuperarUsuario() {
    $.ajax(
    {
        type: "POST",
        url: "/Usuarios/RecuperarUsuario",
        data: { "codigo": 123, "nome": "Israel Aece" },
        processData: true,
        success:
            function (resultado) {
                alert(resultado.Codigo);
                alert(resultado.Nome);
            },
    }

Como a opção processData está definida como True, ele converterá o JSON que informamos no parâmetro data em uma espécie de coleção de chave/valor, e como estamos efetuando a requisição através de POST, as parâmetros serão colocados no corpo da mensagem, separando cada par de chave com o caracter &. Note que na chamada acima, não definimos a propriedade contentType para application/json, já que o conteúdo a ser enviado trata-se de parâmetros em suas formas tradicionais.

O problema maior é quando a action recebe um parâmetro complexo, como por exemplo, a instância da classe Usuario. Neste caso, temos que recorrer à biblioteca JSON2, que fornecerá recursos para serializar a instância de um objeto em formato JSON, para que assim possa trafegar até a action correspondente. Com isso, o código para a chamada à ela será da seguinte forma:

function Adicionar() {
    var usuario = { "Codigo": 123, "Nome": "Israel" };

    $.ajax(
    {
        type: "POST",
        url: "/User/Adicionar",
        contentType: "application/json",
        data: JSON.stringify(usuario),
        processData: false,
        success:
            function (resultado) {
                alert(resultado);
            },
    });
}

Do lado do servidor, teremos um método chamado Adicionar, que recebe a instância da classe Usuario. O código abaixo ilustra como devemos configurar a action com este parâmetro, e note que a propriedade Data retorna uma mensagem, informando que o usuário foi cadastrado com sucesso.

public class UsuariosController : Controller
{
    public ActionResult Adicionar(Usuario usuario)
    {
        return new JsonResult()
        {
            Data = "Usuario cadastrado com sucesso."
        };
    }
}

Só que do lado da action também será necessário efetuar um código adicional. Como estamos mandando o conteúdo em formato JSON, o ASP.NET MVC não conseguirá extrair o objeto diretamente dele, o que nos obriga a criar um binder exclusivo para isso, onde podemos utilizar algum serializador JSON que faça esse trabalho.

Para atingir esse objetivo, podemos utilizar um ponto de estensibilidade do ASP.NET MVC, que nos permite criar um atributo herdando da classe CustomModelBinderAttribute, onde podemos definir como vamos efetuar a "tradução" do(s) parâmetro(s) que estão no corpo da requisição, em um objeto conhecido pela aplicação, e que no nosso exemplo será a classe Usuario. Esta classe possui um método chamado GetBinder, que retorna a instância de uma classe que implementa a interface IModelBinder, qual utilizaremos o serializador DataContractJsonSerializer, que é o mesmo serializador utilizado pelo WCF. O código abaixo ilustra como ele deve ficar:

public class JsonBinderAttribute : CustomModelBinderAttribute
{
    public override IModelBinder GetBinder()
    {
        return new JsonModelBinder();
    }

    public class JsonModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext cc, ModelBindingContext bc)
        {
            return new DataContractJsonSerializer(bc.ModelType)
                .ReadObject(cc.HttpContext.Request.InputStream);
        }
    }
}

Agora, tudo o que precisamos fazer é decorar o parâmetro da action com o atributo que criamos acima. Isso dirá ao runtime do ASP.NET MVC que deverá utilizar este binder para preencher o conteúdo do parâmetro usuario. Abaixo temos a mesma action Adicionar, mas com a alteração necessária para que funcione devidamente, utilizando o binder recém criado:

public ActionResult Adicionar([JsonBinder]Usuario usuario)
{
    //...
}

Conclusão: Este artigo mostrou mais uma das possibilidades que temos ao utilizar o jQuery, onde conseguimos acessar métodos escritos em C#/VB.NET, sem a necessidade de uma atualização geral da página. Como já sabemos, a experiência do usuário aumenta, e nós como desenvolvedores, podemos fazer uso de uma API consistente, independente se estamos consumindo métodos locais ou remotos.

Tags: , , ,

ASP.NET

Autenticação via Claims no ASP.NET MVC

by Israel Aece 11. April 2010 20:54

Anteriormente eu comentei como podemos utilizar o WIF em uma aplicação ASP.NET WebForms, analisando detalhadamente os passos necessários para efetuar a configuração dos módulos utilizados pelo WIF. Mas o WIF também pode ser utilizado em conjunto com aplicações ASP.NET MVC. A finalidade deste artigo é mostrar algumas dicas importantes para fazer com que o WIF seja acoplado ao pipeline de uma relying party construída com o ASP.NET MVC, pois ao contrário do que vimos no artigo anterior, não há templates de projetos com o WIF pré-configurado para o ASP.NET MVC.

O primeiro passo é importante para ser dito, é que para utilizar o WIF com o MVC, também devemos recorrer aos módulos WSFederationAuthenticationModule e SessionAuthenticationModule, que são acoplados ao pipeline do ASP.NET e são os responsáveis pelo processamento e gerenciamento do processo de autenticação do usuário. Sendo assim, é necessário adicioná-los manualmente na seção <httpModules /> do arquivo Web.config, ou se estiver utilizando o assistente Federation Utility, que está disponível a partir da IDE do Visual Studio, ele já fará essa configuração automaticamente.

Como sabemos, a infraestrutura de segurança do o ASP.NET MVC é igual ao do ASP.NET WebForms, e com isso devemos respeitar as mesmas imposições feitas quando utilizamos o WIF no WebForms, ou seja, configurar o modelo de autenticação como indefinido (None), pois essa tarefa não compete mais à esta aplicação. Além disso, temos também que criar e configurar adequadamente a seção <microsoft.identityModel />, que como já sabemos, isso acaba sendo realizado automaticamente quando utilizamos o Federation Utility.

O primeiro passo é você se preocupar em proteger as seções da aplicação MVC que não permitem o acesso anônimo. Ao contrário do WebForms, no MVC definimos quais são as actions (métodos) que são restritas. Para definir isso, podemos utilizar o arquivo Web.config ou através do atributo AuthorizeAttribute. O código abaixo negará o acesso anônimo aos métodos (actions) VisualizarItens e RecuperarIndice da classe (controller) Monitor. Para maiores detalhes, você pode consultar este artigo.

[Authorize]
public class MonitorController : Controller
{
    public ActionResult VisualizarItens()
    {
        //...
    }

    public ActionResult RecuperarIndice()
    {
        //...
    }
}

Pelo fato de termos os módulos do WIF acoplados ao MVC, ao detectar que o usuário não tem permissão de acesso a um destes métodos, automaticamente o módulo WSFederationAuthenticationModule entrará em ação e redirecionará o usuário para o STS que ele confia, incluindo na requisição a action e o controller que ele tentou acessar, para que quando o STS validá-lo, o WIF consiga redirecionar o usuário para o mesmo local.

Outro detalhe importante quando configuramos o WIF no ASP.NET MVC, é o uso do atributo passiveRedirectEnabled do elemento wsFederation. Quando ele é definido como False, o desenvolvedor da relying party deverá explicitamente efetuar o redirecionamento para o STS quando necessário. No ASP.NET WebForms, podemos fazer uso do controle FederatedPassiveSignIn, que tem a finalidade de encapsular toda a complexidade deste processo. Mas como sabemos, no MVC não utilizamos esses tipos de controles, o que nos obriga a fazer o redirecionamento manual, lembrando que isso somente é necessário quando o atributo passiveRedirectEnabled for definido como False.

Quando necessitamos desligar o auto-redirecionamento, precisamos saber como proceder para conseguir interagir com o STS. Para exemplificar isso, temos que criar um controller que irá gerenciar o processo de login, logout e o processamento do token gerado. Esse controller utilizará alguns dos membros que são expostos pela API do WIF, que vimos em um artigo anterior. O controller irá definir actions que determina cada uma das tarefas que ele vimos acima, podendo criar nas views, links que direcionam para cada uma delas.

public class SecurityController : Controller
{
    public ActionResult EfetuarLogin()
    {
        if (!User.Identity.IsAuthenticated)
        {
            var fam = FederatedAuthentication.WSFederationAuthenticationModule;
            var requestMessage = new SignInRequestMessage(new Uri(fam.Issuer), fam.Realm)
            {
                Context = "EndereçoParaOndeUsuarioSeraDirecionadoDepoisDoLogin"
            };

            return new RedirectResult(requestMessage.WriteQueryString());
        }

        return this.View();
    }

    [ValidateInput(false)]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult ProcessarToken()
    {
        var fam = FederatedAuthentication.WSFederationAuthenticationModule;

        if (fam.CanReadSignInResponse(HttpContext.Current.Request, true))
        {
            string url = HttpContext.Current.Request.Form["wctx"];
            return new RedirectResult(url);
        }

        return this.View();
    }

    public ActionResult EfetuarLogout()
    {
        if (User.Identity.IsAuthenticated)
        {
            var fam = FederatedAuthentication.WSFederationAuthenticationModule;
            var signOutMessage = new SignOutRequestMessage(new Uri(fam.Issuer));

            fam.SignOut(false);
            return new RedirectResult(signOutMessage.WriteQueryString());
        }

        return this.View();
    }
}

O primeiro método, chamado de EfetuarLogin, extrai alguns parâmetros do módulo WSFederationAuthenticationModule e cria uma mensagem de login para o STS. A mensagem de login é representada pela instância da classe SignInRequestMessage, que fornece uma propriedade chamada Context, que recebe uma URL para qual o usuário será direcionado depois do login ter sido realizado. Finalmente, utilizando o método WriteQueryString, que retorna uma string representando o endereço completo até o STS, incluindo os parâmetros necessários para que o STS faça toda a validação necessária, e passamos essa string através de um objeto RedirectResult, que faz com que o MVC redirecione o usuário ao STS.

Na sequência temos o método ProcessarToken, que é responsável por verificar se a requisição trata-se de um token gerado pelo STS, e isso é realizado através do método CanReadSignInResponse da classe WSFederationAuthenticationModule. Se este método retornar True, então extraímos o parâmetro wctx da requisição e redirecionamos o usuário para este endereço. O wctx representa o endereço que o usuário tentou acessar antes de efetuar o login no STS, qual utilizaremos o RedirectResult para direcionar o usuário novamente para lá. Podemos notar que este método está decorado com dois atributos, onde o primeiro deles desligamos a validação para permitir que informações com caracteres < e > sejam postadas para ele. Isso é necessário porque o token gerado é baseado em XML. Em seguida, utilizamos o atributo AcceptVerbsAttribute para garantir que este método será acessado somente através do verbo POST.

Finalmente, temos o método EfetuarLogout, que coordena o processo de logout do usuário. Ele utiliza o método SignOut da classe WSFederationAuthenticationModule, e na sequência utilizamos uma instância da classe SignOutRequestMessage, que recebe em seu construtor o endereço do STS. Depois da instância criada, utilizamos novamente um objeto RedirectResult para redirecionar o usuário para o endereço gerado pelo método WriteQueryString da classe SignOutRequestMessage, que efetuará o logout no STS.

Depois deste controller criado, tudo o que precisamos fazer na aplicação, é mencionar as actions login e logout onde desejamos visualizar estes links. Para exemplificar, o código abaixo ilustra como criar estes links:

<%= Html.ActionLink("Login", "EfetuarLogin", "Security") %>
<%= Html.ActionLink("Logout", "EfetuarLogout", "Security") %>

Conclusão: Como percebemos neste artigo, apesar de não termos toda a facilidade de configuração e controles que existem no ASP.NET WebForms, é fácil acoplar o WIF à execução de uma aplicação ASP.NET MVC. A única exigência aqui é você tentar identificar se precisa ou não do auto-redirecionamento; caso não precisar, o controller que foi criado acima não será necessário.

Tags: , , ,

ASP.NET | WIF

Autenticação e Autorização no ASP.NET MVC

by Israel Aece 9. February 2010 08:58

Desde a primeira versão do ASP.NET, temos três formas de autenticação: Windows, Forms e Passport. A primeira opção consiste em permitir o acesso desde que o usuário faça parte do domínio/máquina. Essa opção torna o gerenciamento simples, facilita o Single Sign-On (SSO) interno, mas é útil em um ambiente controlado, como uma intranet. A opção Forms faz com que a aplicação se encarregue de gerenciar e autenticar seus usuários, baseando-se em um cookie que determinará quem é o usuário logado, e esta opção é a mais ideal para a internet. Finalmente, temos a Passport, que permitia autenticar o usuário utilizando o serviço de identidade da Microsoft, mas por haver um custo envolvido, acabou não evoluindo.

Com a vinda do ASP.NET MVC, grande parte dos serviços que são disponibilizados para o ASP.NET WebForms, já nasceram com o MVC ou estão aos poucos sendo adicionados à plataforma. Como já era de se esperar, a questão da autenticação e autorização já fazem parte desde a versão 1.0 do MVC, onde podemos utilizar a autenticação Windows ou Forms, mudando ligeiramente como devemos proceder para configurá-los e utilizá-los.

Para definir se vamos utilizar o modelo de autenticação Windows ou Forms, devemos recorrer ao elemento authentication no arquivo Web.config, como já fazíamos anteriormente. O atributo mode determina qual a opção de autenticação, e um sub-elemento chamado forms, nos permite customizar a autenticação baseada em Forms. O exemplo abaixo ilustra a configuração inicial:

<authentication mode="Forms">
    <forms loginUrl="Authentication/LogOn" />
</authentication>

Aqui há uma mudança radical aqui. O ASP.NET WebForms é baseado em arquivos, ou seja, todas as requisições, sejam elas da aplicação ou da infraestrutura (como é o caso aqui), o alvo sempre será um arquivo físico, com extensão *.aspx. Já no ASP.NET MVC, o alvo será sempre uma ação, que nada mais é que um método dentro de uma classe, conhecida como Controller. No exemplo acima, o controller é uma classe chamada AuthenticationController e dentro dele há um método chamado LogOn. É dentro deste método que você colocará todos os passos necessários para autenticar o usuário. Geralmente esse método deve receber o login e a senha, mas eventualmente poderá ter algo mais rebuscado.

Quando tentamos acessar um recurso protegido através do FormsAuthentication, ele automaticamente redireciona o usuário para a página de autenticação configurada acima, embutindo na URL uma query string chamada ReturnUrl, com o caminho (do arquivo ou da ação) que tentamos acessar. Esse parâmetro é útil para depois que validar o usuário, redirecioná-lo para a mesma seção que ele solicitou antes de efetuar a autenticação. Sendo assim, é necessário que o método que efetua a autenticação do usuário, também receba esse parâmetro.

Em tempo, a versão 2.0 do ASP.NET MVC fornece um atributo chamado HttpPostAttribute, que permite você decorar uma determinada ação, para que ela seja executada somente via POST, garantindo assim que ela seja executada somente através desta forma. Para ilustrar, o método LogOn fica da seguinte forma:

public class AuthenticationController : Controller
{
    [HttpPost]
    public ActionResult LogOn(string userName, string password, string returnUrl)
    {
        //...
    }
}

É importante dizer que se você estiver utilizando a autenticação Windows, nada disso é necessário, já que a autenticação acaba sendo feita automaticamente pelo IIS/ASP.NET.

Se precisar saber qual usuário está autenticado naquele momento, você pode recorrer à propriedade User, exposta pela classe Controller. Assim como já acontece com a classe Page do WebForms, essa propriedade retorna a instância de uma classe que implementa a interface IPrincipal, fornecendo acesso ao username do usuário atual, e um método chamado IsInRole, caso você precisa refinar ainda mais a autorização. Para maiores detalhes sobre essa interface, consulte este o capítulo 9 deste artigo.

Autorização

A autorização pode continuar sendo realizada da mesma forma que fazíamos antes, ou seja, recorrendo ao arquivo de configuração (Web.config) para especificar as regras de autorização. A diferença se dá também pelo fato de que o MVC é baseado em ações, e ao invés de determinar páginas e/ou diretórios nessas regras, utilizaremos as ações para refinar o acesso dos usuários. Se temos um controller chamado MonitorController e dentro dele uma ação (método) chamada VisualizarItens, e o acesso à ela somente pudesse ser feita por usuários autenticados, a regra ficaria da seguinte forma:

<location path="Monitor/VisualizarItens">
    <system.web>
        <authorization>
            <deny users="?" />
        </authorization>
    </system.web>
</location>

Caso você precise permitir o acesso à uma determinada ação para somente aqueles usuários que fazem parte de um determinado grupo (role), você pode configurar a regra da seguinte forma:

<location path="Monitor/VisualizarItens">
    <system.web>
        <authorization>
            <allow roles="Financeiro" />
        </authorization>
    </system.web>
</location>

Apesar desta técnica funcionar, é difícil de manter. Para cada novo controller ou ação que são criados, você precisa se preocupar em olhar para o arquivo de configuração e configurar quem poderá ou não ter acesso. O problema desta técnica é que se você esquecer, usuários que talvez não deveriam ter acesso, acabarão visualizando. No ASP.NET MVC há uma outra forma de se trabalhar, também declarativa, mas recorrendo à atributos dentro dos controllers/ações, facilitando a centralização e manutenção das regras de acesso.

Para isso, o ASP.NET MVC fornece um atributo chamado AuthorizeAttribute que podemos decorar em classes (controllers) ou em métodos (ações). Esse atributo fornece duas propriedades do tipo string: Users e Roles. Na primeira propriedade, especificamos os usuários (separados por vírgula) que podem ter acesso aquele membro (classe ou método) em que o atributo está sendo aplicado. Já a segunda propriedade, Roles, permite especificarmos os grupos (separados por vírgula), onde somente usuários que estão dentro de um deles é que podem ter acesso ao recurso. Utilizar a propriedade Users não é o mais ideal, porque usuários são muito "voláteis", e em pouco tempo, essa regra provavelmente não fará mais sentido.

Como dito acima, podemos aplicar esse atributo em nível de controller, e assim, as suas respectivas ações irão exigir que aquela regra seja atendida antes de permitir o acesso. No primeiro exemplo, aplicamos este atributo em nível de controller, e todas as ações exigirão que o usuário esteja autenticado:

[Authorize]
public class MonitorController : Controller
{
    public ActionResult VisualizarItens()
    {
        //...
    }

    public ActionResult RecuperarIndice()
    {
        //...
    }
}

Já o segundo exemplo, fará com que o controller seja acessado somente por usuários devidamente autenticados, mas isso não é o suficiente para o método VisualizarItens, que além disso, exige que o usuário esteja contido no grupo Financeiro:

[Authorize]
public class MonitorController : Controller
{
    [Authorize(Roles = "Financeiro")]
    public ActionResult VisualizarItens()
    {
        //...
    }

    public ActionResult RecuperarIndice()
    {
        //...
    }
}

Caso você tenha uma regra de validação muito mais complexa do que isso, nada impede você de criar um filtro para efetuar essa customização, fazendo que a autorização siga os passos necessários para atender essa regra predefinida. E para finalizar, lembre-se que o MVC trata os métodos como "opt-out", ou seja, por padrão, todos os métodos públicos estão acessíveis. Se mesmo com a autenticação e/ou autorização ele jamais deverá ser invocado, então você deve decorá-lo com o atributo NonActionAttribute.

Membership e Roles

A partir da versão 2.0 do ASP.NET, uma série de novos serviços foram adicionados, e entre eles temos o Memberhip Provider e Role Provider, algo que já falei extensivamente nestes outros artigos. Felizmente podemos continuar utilizando esses serviços, mas com um pouco mais de cautela.

Esses serviços seguem um padrão conhecido como Provider Model, e ao utilizar o Membership ou Roles, eu estou trabalhando de forma independente de repositório; posso trabalhar com SQL Server, Oracle ou Xml, apenas configurando os providers através do arquivo Web.config. Mas apesar desses serviços seguirem um padrão de extensibilidade bem definido, isso não quer dizer que é uma boa prática você pode utilizá-los diretamente dentro dos controllers.

É importante dizer que ao criar aplicações MVC, a testabilidade deve estar em foco, e utilizar as classes Membership ou Roles dentro dos controllers, o tornará dependente destas implementações, ou melhor, destes serviços, e que por sua vez, estão fortemente acoplados ao ASP.NET. Como disse anteriormente, a arquitetura do provider model é bem flexível, mas para criar uma versão "fake" é complexo demais, pois há uma infinidade de funcinalidades que as vezes serão desnecessárias. Além disso, utilizar essas classes torna meu controller dependente deste tipo de serviço mas, eventualmente, eu tenho o meu próprio repositório de usuários e suas respectivas permissões, e quero fazer uso deles ao invés daqueles que o ASP.NET fornece.

A classe AccountController

Quando criamos um novo projeto MVC, por padrão, uma série de recursos já são adicionados, e entre eles, temos a classe (controller) chamada AccountController. Essa classe foi desenhada para servir como um wrapper para os serviços de Membership e FormsAuthentication. Há vários problemas com essa classe, e o primeiro deles é aquele que descrevi acima, que é a forte dependência da API do Membership. Apesar da Microsoft ter refatorado o código e criado uma interface chamada IMembershipService, há ainda alguns tipos que estão sendo utilizados, como é o caso do enumerador MembershipCreateStatus. Além disso, essa classe ainda viola o princípio de SRP (Single Responsability Principle), que faz com que ela faça muito mais coisas do que realmente deveria.

Conclusão: O fato da Microsoft tem se preocupado em manter grande parte dos recursos do WebForms no MVC, é importante que você analise cuidadosamente como incorporá-lo ao MVC para não comprometer um dos maiores benefícios do MVC, que são os testes. É importante notar que grande parte do conhecimento adquirido no ASP.NET WebForms, acaba sendo reutilizado aqui, apenas com ligeiras mudanças devido à arquitetura do MVC.

Tags: , , ,

ASP.NET | Security

Programação Assíncrona no ASP.NET MVC

by Israel Aece 25. January 2010 10:38

A Microsoft introduziu na versão 2.0 do ASP.NET WebForms uma funcionalidade chamada de páginas assíncronas, assunto qual já comentei bastante por aqui, e também fiz uma palestra no TechEd 2008 à respeito desse mesmo assunto. Para recapitular, e resumidamente falando, quando uma requisição chega para o IIS, o mesmo entrega para o ThreadPool da CLR, que utilizará uma thread para efetivamente executar aquela requisição.

Por padrão, as páginas são sempre síncronas, ou seja, enquanto aquela requisição não for finalizada, a thread não será liberada. O problema disso é que muitas vezes, as páginas executam processos pesados, como por exemplo, acesso à serviços, consultas em base de dados, etc. Essas tarefas são consideradas I/O-bound, ou seja, são tarefas que não dependem da máquina local, mas sim do processamento do computador remoto que hospeda o serviço/banco de dados, da latência da rede, etc. Neste tempo que a thread fica aguardando esse processo de I/O finalizar, ela poderia estar servindo outras requisições, também ASP.NET, mas que não fazem necessariamente acesso à recursos deste tipo, como por exemplo, páginas institucionais. Dependendo da demanda, é comum o usuário receber no navegador erros com as seguintes mensagens de erro: Server Unavaliable ou Server Too Busy (503).

As páginas assíncronas resolvem esse tipo de problema, ou seja, quando encontrar uma tarefa deste tipo, a executam em uma thread de I/O, devolvendo a thread para o ThreadPool, e dando a chance dela atender outras requisições. Quando o processo remoto finalizar, a thread de I/O é retornada com o resultado e, novamente, uma thread é apanhada do ThreadPool para finalizar a requisição, e que na maioria das vezes, irá renderizar o resultado.

Por ser algo que aumenta consideravelmente a performance, a Microsoft está introduzindo este mesmo recurso no ASP.NET MVC. A partir da versão 2.0 do mesmo, teremos a possibilidade de criar ações assíncronas. Aplicações MVC que estão sujeitas à uma grande quantidade de requisições, podem fazer as execuções das ações de forma assíncrona, trabalhando de forma bem semelhante ao WebForms, e dando também ao MVC o mesmo benefício.

O primeiro passo para a criação de ações assíncronas, é fazer com o controller herde da classe abstrata AsyncController e não apenas de Controller. Essa classe fornecerá toda a infraestrutura para que as ações sejam executadas assincronamente, mas é importante dizer que mesmo que o controller herde de AsyncController, ele ainda pode continuar executando ações síncronas. Ao contrário do modelo síncrono, ações assíncronas dependem de um par de métodos, sendo um que inicia a tarefa custosa, e o segundo que será disparado quando o processo for finalizado. Atualmente é necessário sufixar o nome da ação com os respectivos sufixos: "Async" e "Completed". Abaixo temos um exemplo de como fica a estrutura de um controller assíncrono:

public class UsuariosController : AsyncController
{
    public void ListagemAsync(int quantidade)
    {
        //...
    }

    public ActionResult ListagemCompleted(Usuario[] usuarios)
    {
        //...
    }
}

Repare que o primeiro método é definido como void, pois quem retorna o resultado para a View é o método que é disparado quando o processo for finalizado. Um outro detalhe importante é que o método sempre será referenciado ou acessado no navegador como "Listagem" e nunca como "ListagemAsync", pois os sufixos são somentes utilizados pelo runtime do ASP.NET.

Ao herdar da classe AsyncController, novas propriedades estão a nossa disposição, e uma delas é a AsyncManager, que retorna a instância de uma classe com o mesmo nome. Como o próprio nome diz, ela é a responsável por gerenciar as operações assíncronas. Essa classe fornece três propriedades: OutstandingOperations, Parameters e Timeout. A primeira delas, OutstandingOperations, retorna a instância de uma classe chamada OperationCounter, onde essa classe controla a quantidade de operações que foram inicializadas pela respectiva ação. Já a propriedade Parameters, serve como um dicionário de dados, que permite passar informações para o método de finalização (como o resultado, por exemplo) e, finalmente, a propriedade Timeout, onde podemos definir um número inteiro que representa a quantidade de milisegundos (padrão de 45000 (45 segundos)) que o ASP.NET irá aguardar até que a operação seja finalizado.

Como sabemos, o .NET Framework fornece duas formas para trabalho assíncrono: modelo APM (métodos Begin/End) ou o modelo de eventos. O MVC suporta as duas formas de trabalho, mas a implementação para cada uma delas é ligeiramente diferente. Independentemente de qual técnica você utilize para invocar, o método que é disparado quando o processo assíncrono é finalizado não mudará em nada. Abaixo temos a sua implementação, e podemos notar que ele nada sabe sobre questões assíncronas.

public ActionResult ListagemCompleted(Usuario[] usuarios)
{
    ViewData["Usuarios"] = usuarios;
    return this.View();
}

O próximo passo é codificar o método ListagemAsync, e vamos utilizar inicialmente o modelo de programação assíncrona do .NET (APM). Como exemplo, vamos consumir um serviço WCF que foi referenciado na aplicação. Lembre-se que ao referenciar um serviço WCF, por padrão, ele não traz as versões assíncronas das operações; para poder habilitá-las, consulte este artigo.

Vamos então instanciar o proxy para estabelecer o canal de comunicação entre a aplicação e o serviço WCF. Podemos notar que temos que obrigatoriamente invocar os métodos Increment e Decrement, expostos pela propriedade OutstandingOperations, para especificar a quantidade de operações assíncronas que estão em andamento. Depois disso, devemos inicializar a operação assíncrona, através do método BeginRecuperarUsuarios. De acordo com o modelo APM, além dos parâmetros exigidos pelo método em si, temos que informar um callback, que nada mais é do que o método que será executado quando o processo assíncrono for finalizado.

Note no código abaixo que dentro do método de callback, estamos recuperando o resultado (EndRecuperarUsuarios) e armazenando dentro da propriedade Parameters a coleção de usuários. O valor colocado dentro deste dicionário será passado para o método ListagemCompleted, através do parâmetro "usuarios". Em seguida estamos também decrementando o contador de operações assíncronas. Note que tudo o que foi descrito neste parágrafo, está sendo executado dentro do método chamado Sync, também fornecido pela propriedade AsyncManager. Isso é necessário para garantir que este código e, um pouco mais tarde, a execução do método ListagemCompleted, sejam disparados em uma thread que o ASP.NET terá o controle. Se você não se atentar à isso e tentar executar esse código, ainda estará em uma thread de I/O, fazendo com que o contexto do HTTP (HttpContext.Current) esteja nulo e, consequentemente, não conseguirá acessar grande parte dos recursos que precisa para exibir o resultado.

public void ListagemAsync(int quantidade)
{
    ServicoDeUsuarios proxy = new ServicoDeUsuarios();
    this.AsyncManager.OutstandingOperations.Increment();

    proxy.BeginRecuperarUsuarios(quantidade, ar =>
    {
        AsyncManager.Sync(() =>
        {
            this.AsyncManager.Parameters["usuarios"] = proxy.EndRecuperarUsuarios(ar);
            this.AsyncManager.OutstandingOperations.Decrement();
        });
    }, null);
}

Depois de visualizar a implementação baseada no modelo APM, temos agora o modelo de eventos. Neste caso não precisamos envolver o método Sync, pois o evento que determina que o processo foi finalizado já acontece dentro da thread do próprio ASP.NET. Tudo o que precisamos fazer é se vincular à este evento, e dentro dele armazenar o resultado na propriedade Parameters e decrementar o contador, tudo de forma bem parecida ao que vimos acima. Apenas para iniciar o processo assíncrono, você deverá invocar a versão assíncrona do método que é gerado durante a criação do proxy do WCF, que terá sempre o nome da operação sufixada com a palavra "Async".

public void ListagemAsync(int quantidade)
{
    ServicoDeUsuarios proxy = new ServicoDeUsuarios();
    this.AsyncManager.OutstandingOperations.Increment();

    proxy.RecuperarUsuariosCompleted += (sender, e) =>
    {
        this.AsyncManager.Parameters["usuarios"] = e.Result;
        this.AsyncManager.OutstandingOperations.Decrement();
    };

    proxy.RecuperarUsuariosAsync(quantidade);
}

Observação: Qual dos dois modelos utilizar? Isso vai depender da API que está sendo chamada dentro do controller/ação assíncrono. Se ela suportar os dois modelos, então você pode escolher um deles. Mas há situações que não temos esse luxo, como por exemplo, quando queremos invocar uma consulta no SQL Server usando o ADO.NET tradicional, ou até mesmo ler o conteúdo de um arquivo no disco. Essas classes apenas fornece o modelo APM, com um par de métodos Begin/End.

A Microsoft ainda disponibilizou dois atributos: AsyncTimeoutAttribute e NoAsyncTimeoutAttribute. O primeiro deles disponibiliza uma propriedade chamada Duration, que como falamos acima, recebe a quantidade de milisegundos que determina o timeout. Já o segundo atributo deve ser aplicado quando você quer deixar isso indefinido. Independentemente de qual irá utilizar, eles devem ser aplicados sempre ao método que está sufixado com a palavra "Async", assim como vemos abaixo:

[AsyncTimeout(Duration = 30000)]
public void ListagemAsync(int quantidade)
{
    //...
}

Conclusão: É importante dizer que esse modelo de programação, apesar de tornar o código um pouco mais ilegível e poluído, traz um grande benefício em termos de performance e escalabilidade. Não pense que a página aparecerá no navegador do usuário enquanto a ação é processada, e quando ela for finalizada, aparecerá os dados na tela. Em termos visuais, você ainda terá o mesmo resultado, ou seja, enquanto a ação não for finalizada o navegador ficará bloqueado até que a requisição como um todo seja concluída, mas esta funcionalidade irá "desafogar" o ASP.NET.

Tags: ,

ASP.NET | Async

ASP.NET MVC - Preview 2

by Israel Aece 19. March 2008 12:52

Há algum tempo eu falei sobre algumas das funcionalidades disponibilizadas pelo ASP.NET MVC. Recentemente a Microsoft disponibilizou a versão 2, com algumas mudanças e melhorias. Abaixo estão listadas algumas delas:

  • Todos os tipos que compõem o Framework estavam dentro do assembly System.Web.Extensions.dll. Nesta versão a Microsoft desacoplou isso, "quebrando" em tres assemblies distintos (todos podendo ser rodados em partial trust):
    • System.Web.Mvc.dll: Este assembly é o core dos aplicativos MVC.
    • System.Web.Routing.dll: O roteamento consiste em ter URLs amigáveis ao invés de URLs que apontam para um recurso físico, como é o caso de uma página ASPX. Como essa característia não necessariamente é de aplicativos baseados em MVC, a Microsoft decidiu isolar isso em um assembly, permitindo que façamos o uso deste em aplicativos ASP.NET WebForms. O Phil Haack e o Luis Abreu escreveram sobre isso aqui e aqui.
    • System.Web.Abstractions.dll: Este assembly acomoda os tipos (para ambientes de testes) que serão utilizados para efetuar mocks dos objetos concretos.
  • Não há mais uma template de projeto para testes de aplicações baseadas em MVC. Agora, quando criamos um projeto MVC, uma pop-up é exibida, perguntando se voce deseja ou não criar um projeto de testes, dando suporte a qualquer runtime que queira utilizar (NUnit, xUnit, MSTest, etc.).
  • As rotas eram antes definidas como [ ]. Agora fazemos isso através de { }.
    • Ainda sobre rotas: na declaração das mesmas, é possível agora definirmos um "catchall" e, com isso, o runtime será capaz de pegar todos os parametros que são passados na URL. Este vídeo mostra a implementação desta técnica.
  • Os métodos que representam ações não precisam mais ser decorados com o atributo ControllerActionAttribute. Qualquer método público de um controller pode agora ser utilizado pelo runtime. Agora, o que voce precisa fazer é barrar o acesso, ou seja, se voce tem um método público mas não quer que este esteja disponível para que a aplicação possa consumí-lo, então voce deve decorar este método com o atributo NoActionAttribute.
  • Há um novo atributo chamado ActionFilterAttribute que voce poderá utilizar para interceptar a execução de uma determinada ação. Esse atributo, trata-se de uma classe base que deve ser herdada. Essa classe base fornece dois principais métodos: OnActionExecuting e OnActionExecuted que, de acordo com a nossa necessidade, sobrescrevemos cada um destes métodos e efetuamos a customização. Essa nova classe dará origem a um novo atributo que, por sua vez, deverá ser decorado nos métodos dos controllers que deverão invocá-los. Maiores informações e como proceder para criá-lo, consulte este vídeo.

Tags:

ASP.NET

Comparativo: MVC vs. WebForms

by Israel Aece 10. January 2008 15:36

Recentemente a Microsoft disponibilizou a implementação do padrão MVC. Esta versão encontra-se em CTP e está disponível para ser baixada neste link. A idéia deste post é mostrar uma comparação saudável e técnica do modelo tradicional do ASP.NET (WebForms) em comparação com o  MVC.

Introdução

Desde o lançamento da plataforma .NET até a versão mais recente – 3.5 – o ASP.NET é baseado em um modelo chamado de WebForms que, desde então, foi uma das grandes mudanças, talvez uma das mais impactantes, que a Microsoft fez em sua estrutura de desenvolvimento para aplicações Web. O ASP classico deixou de ser utilizado, devido a estrutura estável, performática, reutilizável e simples que é o ASP.NET.

Por sua vez, o ASP.NET possui uma série de características que o tornam simples de utilizar, mas não menos robusto. A quantidade de controles ricos que o mesmo fornece é extremamente grande, onde precisaríamos de uma série de linhas de código HTML + CSS para ter um resultado igual. Mesmo com essa facilidade e toda sua extensibilidade (como é caso dos Control Adapters), muitos desenvolvedores sentem a necessidade de ter um maior controle sobre como o conteúdo está sendo renderizado para o cliente e como os dados que o cliente informa são enviados para a aplicação.

Devido a estrutura do ASP.NET e seus controles intrínsicos, a renderização final acaba sendo delegada ao runtime do ASP.NET e é neste momento que desenvolvedores sentem a necessidade de uma maior flexibilidade. O padrão MVC (Model-View-Controller) garante isso. Este padrão não nasceu agora. Já existem vários Frameworks que você pode acoplar ao ASP.NET e desenvolver aplicações baseadas neste modelo. O que vemos neste momento é uma implementação deste padrão pela própria Microsoft, integrando fortemente à infraestrutura do ASP.NET.

Arquitetura

O ASP.NET WebForms é baseado em um padrão chamado Page Controller.  Já o modelo MVC baseia-se em um outro padrão, chamado de Front Controller. Cada um desses padrões tem suas vantagens e desvantagens que podem ser analisadas e comparadas através do link definido acima.

Além dos padrões, o modelo WebForms possui algumas características muito interessantes, que tornaram o ASP.NET muito fácil de desenvolver, acabando com aquele abismo que existia entre programadores Windows e Web. Para que isso fosse possível, funcionalidades como o ViewState, CodeBehind, Server Controls foram essenciais, mas que, no mundo MVC, acabam não sendo utilizados.

Páginas vs. Ações

Quando uma requisição chega para uma aplicação ASP.NET baseada no modelo tradicional, a página requisitada é mapeada para uma página (*.aspx). Esta página contém código HTML que representa a visualização da mesma e que será processada pelo ASP.NET e, em seguida, gerado um output e enviado ao usuário que a requisitou.

No modelo MVC, isso mudará drasticamente. Ao invés de você fazer requisição para uma página, você requisitará uma ação. Esta ação nada mais é do que um método que estará dentro de um determinado Controller. O Controller é responsável por capturar as informações fornecidas pelo protocolo HTTP ou pelo usuário, manipular essas informações, acessar o Model e, finalmente, renderizar o conteúdo, através de uma View, para o usuário.

Runtime

Quando uma requisição chega para uma aplicação ASP.NET tradicional, o runtime primeiramente executará vários de passos até que a página ASPX seja efetivamente executada. Muitos deles serão omitidos neste post ou serão comentados superficialmente (para maiores detalhes, consulte este artigo). Durante os passos que são omitidos nestes posts, estão a construção do AppDomain e a criação de objetos extremamente importantes para a execução da mesma, como o HttpContext, HttpApplication, etc. Depois que estes objetos estão devidamente criados, em algum momento, o ASP.NET determina qual será o handler responsável por executar a página; assim que este handler é determinado, o método ProcessRequest da página é executado e o ciclo de vida da mesma inicia, executando os métodos que criam os controles na página (baseando-se no HTML), os eventos da própria página, como Init, Load, etc., e eventos de controles que, possivelmente, podem acontecer.

No modelo MVC esse comportamento é radicalmente diferente. O motivo é porque as requisições para aplicações MVC são feitas para ações e não para páginas. Devido a isso, um módulo chamado UrlRoutingModule é adicionado a esta aplicação que intercepta a requisição no evento PostResolveRequestCache e PostMapRequestHandler. Este módulo é responsável por efetuar o parser da requisição e encontrar uma rota que se encaixa com a requisição. O mapeamento das requisições para o Controller/Ação é realizado no arquivo Global.asax e será tema de um futuro post/artigo.
 
Depois disso, um handler chamado MvcHandler é disparado. Ele é responsável por encontrar e instanciar o Controller correspondente e executar a respectiva ação requisitada pelo usuário. A idéia dentro desta ação é acessar o Model para extrair informações, definir possíveis regras de exibição e renderizar a visualização através do método RenderView. Este método recebe como parâmetro uma string, que representa a página a ser exibida; a responsabilidade deste método é justamente encontrar tal página ASPX e executá-la.

Todos os Controllers das aplicações herdam direta ou indiretamente da classe Controller que fornece toda essa infraestrutra. Não diferente, as páginas que servem como View, devem herdar da classe ViewPage ou ViewPage<TViewData>. Apesar de ser um Framework ainda bem novo, já existe a possibilidade de você customizar alguns funcionalidades, como a criação do Controller (ControllerBuilder) e também da geração da View (ViewFactory) e que podem ser facilmente plugáveis à infraestrutura do MVC, mas isso exige um post/artigo mais detalhado.

Segurança

Funcionalidades como o Membership e Roles continuam sendo suportadas normalmente, com apenas um detalhe com relação a restrição de páginas por grupos/usuários. Até então, quando queremos barrar o acesso a um determinado grupo/usuário à uma página, especificamos isso no Web.Config da aplicação, como pode ser visto através do trecho de código abaixo:

<location path="Contas/ContaCorrente.aspx">
  <system.web>
    <authorization>
      <allow roles="Gerentes" />
      <deny users="*" />
    </authorization>
  </system.web>
</location>

No modelo MVC isso não funcionará. Como falamos acima, o modelo MVC se baseia na execução de ações e não em páginas. Isso quer dizer que você terá que, ao invés de especificar a página, informar a ação e qual grupo/usuário poderá acessá-la. No modelo MVC deveríamos ter algo mais ou menos como:

<location path="Contas/ExibirContaCorrente">
  <system.web>
    <authorization>
      <allow roles="Gerentes" />
      <deny users="*" />
    </authorization>
  </system.web>
</location>

[ControllerAction]
public void ExibirContaCorrente()
{
     //regras
     RenderView(“ContaCorrente”);  //ContaCorrente.aspx
}

Em alternativa a isso, podemos recorrer a programação declarativa e utilizar atributos para especificar a autorização. Com a segurança declarativa, podemos decorar o método de ação com o atributo PrincipalPermissionAttribute e especificar o(s) grupo(s) que poderão ter acesso ao respectivo método. Fredrik Normén explica maiores detalhes desta implementação neste post.

Um outro exemplo é com relação a página de autenticação (Login) da aplicação. Ela também será uma ação de algum Controller que deverá ser executada quando o usuário desejar efetuar o login. Com isso, o atributo loginUrl do elemento <forms /> não apontará mais para uma página, mas sim para uma ação que se encarregará de exibir a página de autenticação para o usuário. Troy Goode mostra como aplicar essa técnica através deste post.

Testes

A partir do momento que você vê os testes como algo benéfico para o bom funcionamento das aplicações, você irá querer implementar isso em qualquer tipo de aplicação, mesmo em aplicações ASP.NET. Infelizmente, aplicações ASP.NET desenvolvidas em sua estrutura tradicional (WebForms), tornam a realização dos testes extremamente difícil, já que uma única classe/página é responsável por tratar da exibição dos dados, exibir os dados e capturar as informações fornecidas pelo usuário e enviar para a aplicação novamente. Esse modelo nos obrigará a instanciar essa classe e, para isso, será necessário que a mesma esteja sendo executada dentro do IIS, criando um forte acoplamento.

Isso já será bem mais flexível no modelo MVC, pois as três partes são independentes/desacopladas, permitindo que os testes sejam realizados de forma isolada ao resto do Framework. Somente um detalhe importante aqui é com relação a utilização de recursos específicos do protocolo HTTP (como QueryStrings, Forms, Headers, etc.); ao utilizar isso dentro da classe Controller você amarra isso ao ASP.NET e, segundo Phil Haack, eles estarão disponibilizando Interfaces que poderemos efetuar o mock destes objetos.

Server Controls (runat=”server”)

Acredito que aqueles que recorrem ao uso do modelo MVC provavelmente querem também ter o controle da criação dos controles e dificilmente deverão utilizar os controles fornecidos pelo ASP.NET.

Mesmo assim, felizmente a Microsoft se preocupou com a gama de controles que ela disponibiliza na ToolBox do Visual Studio .NET, e permite que utilizamos tais controles em aplicações que utilizam o modelo MVC, mas com algumas limitações:

• A forma que você popula os controles muda ligeiramente para se adequar a este modelo, mas ganhamos a facilidade do drag-and-drop e a geração automática do respectivo HTML. E além de tudo isso, a Microsoft garante que os Server Controls continuarão evoluindo com as futuras versões da plataforma e, conseqüentemente, suportados em ambos modelos.

• Apesar dos Server Controls serem suportados, o MVC não utiliza o conceito de Postback para se comunicar com o servidor, justamente porque tudo será baseado em ações, e o mesmo deverá ser redirecionado para uma ação específica dentro de algum Controller.

Outras Funcionalidades

Algumas funcionalidades do ASP.NET provavelmente perderão o sentido dentro deste modelo, como é o caso dos Control Adapters (já mencionado acima). Outras funcionalidades, que já utilizamos tradicionalmente, continuam sendo suportadas dentro deste modelo, como por exemplo ASCX, Master Pages, Session State, Caching, Url Authorization, etc..

Outras funcionalidades ainda não se sabe se serão suportadas dentro deste modelo, como é o caso das páginas assíncronas. Com este modelo, quem executa a parte “pesada” é o Controller e não mais a página. Sendo assim, não temos que fazer a página executar assincronamente já que, neste momento, o processo custoso já foi realizado. Imaginei que a Microsoft pudesse fazer algo como:

[ControllerAction]
[ExecuteInBackground]
public void CalcularPorcentagemDeComissao(int? gerenteId)
{
     //processo custoso
     RenderView(“Resultado”, null);
}

… e assim o processo seria executado em uma worker thread ao invés de utilizar uma thread retirada do ThreadPool que poderia servir páginas mais simples/leves.

Futuro

Este post foi baseado na versão CTP do ASP.NET MVC Framework. Provavelmente este Framework evoluirá bastante até sua versão final. Acredito que em breve, a Microsoft lançará uma nova versão, com possíveis melhorias no que diz respeito a execução e renderização dos controles. Até lá, muitas informações serão disponibilizadas pela comunidade, como é o caso do MVC Toolkit.

Para aqueles que pretendem estudar mais sobre este Framework, aconselho que assinem os blogs do Scott Guthrie, Fredrik Normém, Phil Haack, Rob Conery, Scott Hanselman e Luis Abreu que, sob-demanda, publicarão informações importantes sobre o assunto e o blog do Eduardo Miranda, que fala muito sobre testes.

Conclusão

Particularmente, gosto bastante do modelo WebForms e, até o presente momento, sempre consegui extrair bons resultados deste modelo. Vejo o MVC como uma alternativa bastante interessante ao modelo tradicional e que acredito que será bem aceito pela comunidade. Mesmo que o código C# ou VB.NET que consta no arquivo ASPX é referente a renderização, não gosto muito desta mistura (um pouco confuso?), e acho que isso é talvez por estar habituado ao code-behind. O Framework vai evoluir muito e, acredito que em pouco tempo, teremos várias novidades e, conseqüentemente, poderemos tirar maiores conclusões a respeito destes dois modelos.

Tags: ,

ASP.NET

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

Host