Skip to content

3. Controladores, Ações, Roteamento

Consideremos a arquitetura de uma aplicação ASP.NET MVC:

Neste capítulo, examinamos o processo que leva a solicitação [1] ao controlador e à ação [2a] que irão processá-la, um mecanismo chamado roteamento. Além disso, apresentamos as diferentes respostas [3] que uma ação pode enviar ao navegador. Pode ser algo diferente de uma visualização V [4b].

3.1. A estrutura de um projeto ASP.NET MVC

Vamos criar um primeiro projeto ASP.NET MVC com o Visual Studio Express 2012. Vamos adicioná-lo [1] à solução utilizada no capítulo anterior:

  • em [2], o nome do novo projeto;
  • em [3, 4], escolhemos um projeto base ASP.NET MVC. Esse modelo nos fornece um aplicativo web vazio, mas com todos os recursos (DLL, bibliotecas JavaScript, etc.) necessários para trabalhar.

O projeto resultante é apresentado em [5]. Faremos dele [6] o projeto inicial da solução:

Observem-se em [5] os seguintes pontos:

  • a arquitetura do projeto reflete seu modelo MVC:
  • os controladores C serão colocados na pasta [Controllers],
  • os modelos de dados M serão colocados na pasta [Models],
  • as visualizações V serão colocadas na pasta [Views],
  • na pasta [1], o arquivo [Site.css] será o arquivo CSS por padrão do aplicativo;
  • em [2], várias bibliotecas JavaScript estão à nossa disposição;
  • em [3], encontramos três visualizações específicas: _ViewStart, _Layout e Error.

O arquivo [_ViewStart] é o seguinte:


@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}
  • linha 1: o caractere @ indica uma sequência de C# na visualização. De fato, é possível incluir código C# em uma visualização;
  • linha 2: define uma variável Layout que define a vista pai de todas as vistas. Ela corresponde à página mestre do framework ASP.NET clássico.

O arquivo [_Layout] é o seguinte:


<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    @RenderBody()

    @Scripts.Render("~/bundles/jquery")
    @RenderSection("scripts", required: false)
</body>
</html>

Quando uma visualização da pasta [Views] for renderizada, seu corpo será renderizado pela linha 11 acima. Isso significa que a visualização não precisa incluir as tags <html>, <head> e <body>. Elas são fornecidas pelo arquivo acima. Por enquanto, esse arquivo é um tanto obscuro. Vamos simplificá-lo:


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Tutoriel ASP.NET MVC</title>
</head>
<body>
  <h2>Tutoriel ASP.NET MVC</h2>
  @RenderBody()
</body>
</html>
  • linha 6: o título comum a todas as visualizações;
  • linha 9: o cabeçalho comum a todas as visualizações;
  • linha 10: o conteúdo específico da visualização exibida.
  
  • [Web.config] é o arquivo de configuração do aplicativo web. Ele é complexo. Será necessário modificá-lo ao utilizar o framework [Spring.net] em uma arquitetura multicamadas.
  • [Global.asax] contém o código executado no início da aplicação. Geralmente, esse código utiliza os diversos arquivos de configuração da aplicação, incluindo o [Web.config].

3.2. O roteamento padrão do URL

O código de [Global.asax] é, por enquanto, o seguinte:


using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Exemple_01
{
 
  public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
   ...
    }
  }
}
  • linha 6, o namespace da classe. Ele deriva diretamente do nome do projeto e está presente nas propriedades do projeto:

Image

Definimos [1] como o namespace padrão. Ele passa a ser usado por padrão para todas as classes que forem criadas no projeto.

Voltemos ao código de [Global.asax]:


using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace Exemple_01
{
 
  public class MvcApplication : System.Web.HttpApplication
  {
    protected void Application_Start()
    {
   ...
    }
  }
}
  • linha 9: a classe [MvcApplication] deriva da classe [HttpApplication]. O nome [MvcApplication] pode ser alterado;
  • linha 11: o método [Application_Start] é o método executado no início da aplicação web. Ele é executado apenas uma vez. É aqui que a aplicação é inicializada.

O código de [Application_Start] é atualmente o seguinte:


    protected void Application_Start()
    {
      AreaRegistration.RegisterAllAreas();

      WebApiConfig.Register(GlobalConfiguration.Configuration);
      FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
      RouteConfig.RegisterRoutes(RouteTable.Routes);
      BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Por enquanto, não é necessário compreender todo esse código. As linhas 5 a 8 definem as rotas aceitas pela aplicação web. Voltemos à arquitetura de uma aplicação ASP.NET MVC:

Já explicamos que o [Front Controller] deve encaminhar um URL para a ação responsável por processá-lo. Uma rota serve para estabelecer a ligação entre um modelo de URL e uma ação. Essas rotas são definidas na pasta [App_Start] do projeto pelas classes [WebApiConfig, FilterConfig, RouteConfig, BundleConfig]:

 

Por enquanto, apenas a classe [RouteConfig] nos interessa:


using System.Web.Mvc;
using System.Web.Routing;

namespace Exemple_01
{
  public class RouteConfig
  {
    public static void RegisterRoutes(RouteCollection routes)
    {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
      );
    }
  }
}

As linhas 12 a 16 definem o formato dos URL aceitos pelo aplicativo. Isso é chamado de rota. Pode haver várias rotas possíveis (= vários modelos possíveis de URL). Elas se distinguem entre si pelo nome (linha 13). O formato dos URL da rota é definido na linha 14. Aqui, o URL terá três componentes:

  • {controller}: o nome de uma classe derivada de [Controller]. Ela será procurada na pasta [Controllers] do projeto. Por convenção, se o URL for /X/Y/Z, o controlador responsável por processar esse URL será a classe XController. O sufixo Controller é adicionado ao nome do controlador presente no URL;
  • {action}: o nome de um método no controlador designado acima. É ele que receberá os parâmetros que acompanham o URL e os processará. Esse método pode retornar diversos resultados:
    • void: a ação irá construir ela mesma a resposta para o navegador do cliente
    • String: a ação retorna uma sequência de caracteres ao cliente;
    • ViewResult: retorna uma visualização ao cliente;
    • PartialViewResult: retorna uma visualização parcial;
    • EmptyResult: uma resposta vazia é enviada ao cliente;
    • RedirectResult: solicita que o cliente seja redirecionado para um URL
    • RedirectToRouteResult: o mesmo, mas a URL é construída a partir das rotas do aplicativo;
    • JsonResult: envia uma resposta JSON
    • JavaScriptResult: retorna um código JavaScript ao cliente;
    • ContentResult: retorna um fluxo HTML ao cliente sem passar por uma visualização;
    • FileContentResult: retorna um arquivo ao cliente;
    • FileStreamResult: o mesmo, mas por outro caminho;
    • FilePathResult: ...
  • {id}: um parâmetro que será transmitido à ação. Para isso, a ação deverá ter um parâmetro chamado id.

A linha 15 define valores padrão para o caso de o URL não ter o formato esperado /{controller}/{action}/{id}. Ela também indica que o parâmetro {id} no URL é opcional. Segue uma lista de URL incompletas e a URL preenchida com os valores padrão:

URL original
URL preenchido
/
/Home/Índice
/Do
/Fazer/Índice
/Do/Something
/Fazer/Algo
/Fazer/Algo/4
/Fazer/Algo/4
/Fazer/Algo/x/y/z
URL sem rota

3.3. Criação de um controlador e de uma primeira ação

Vamos criar um primeiro controlador:

 
  • em [1], especifique o nome do controlador com o sufixo [Controller];
  • em [2], crie um controlador vazio MVC;
  • em [3], ele foi criado.

O código de [FirstController] é o seguinte:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
    public class FirstController : Controller
    {
        //
        // GET: /First/

        public ActionResult Index()
        {
            return View();
        }

    }
}
  • linha 7: um namespace padrão foi gerado;
  • linha 9: a classe [FirstController] deriva da classe [System.Web.Mvc.Controller];
  • linhas 14-17: uma ação [Index] foi gerada por padrão. Ela é pública. Isso é importante, caso contrário, ela não será encontrada. Ela retorna um tipo [ActionResult], que é uma classe abstrata da qual derivam a maioria dos resultados que uma ação normalmente retorna. Trata-se de um tipo “genérico” que pode ser especificado substituindo-o pelo nome real do tipo retornado;
  • linha 16: o método não faz nada. Ele se limita a retornar uma visualização, ou seja, um tipo [ViewResult]. O nome da visualização não é especificado. Nesse caso, o framework procura na pasta [Views / First] uma visualização com o nome da ação, ou seja, neste caso: [Index.cshtml].

Vamos criar a vista [Index.cshtml]:

  • em [1], clica-se com o botão direito do mouse no código da ação e seleciona-se a opção [Ajouter une vue];
  • em [2], o assistente sugere uma visualização com o nome da ação. É isso que queremos aqui;
  • em [3], por padrão, é sugerido o uso da página mestre [_Layout.cshtml];
  • em [4], uma vez validado, o assistente cria a visualização em uma subpasta da pasta [Views] com o nome do controlador (First).

O código gerado para a visualização [Index] é o seguinte:


@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>
  • linhas 1-3: um código C# que define uma variável;
  • linha 5: uma tag HTML.

Substitua todo o código anterior por este:


<strong>Vue [Index]...</strong>

Resumindo:

  • temos um controlador C chamado [First];
  • temos uma ação chamada [Index] que solicita a exibição de uma visualização chamada [Index];
  • temos a vista V [Index].

Podemos chamar a ação [Index] de duas maneiras:

  • /First/Index;
  • /First, pois [Index] também é a ação padrão nas rotas.

Vamos executar o aplicativo (CTRL-F5). Obtemos a seguinte página:

1

Image

Em [1], a ação solicitada foi http://localhost:49302. Não há caminho. Sabemos que nosso roteador espera ações no formato /{controller}/{action}/{id}. Como esses elementos estão ausentes, os valores padrão são utilizados. A solicitação URL passa a ser http://localhost:49302/Home/Index. O controlador [Home] não existe. Portanto, a solicitação URL é rejeitada.

Vamos agora tentar o URL http://localhost:49302/First/Index digitando-o diretamente no navegador:

A página acima foi gerada pela ação [Index] do controlador [First]. A página gerada por essa ação é a visualização [Index], cujo código era o seguinte:


<strong>Vue [Index]...</strong>

Ela gera a parte [1]. Já a parte [2] provém da página mestre [_Layout] que definimos um pouco antes:


<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Tutoriel ASP.NET MVC</title>
</head>
<body>
  <h2>Tutoriel ASP.NET MVC</h2>
  @RenderBody()
</body>
</html>

A linha 9 gerou a parte [2] da página. A visualização [Index], por sua vez, só aparece na linha 10.

Se exibirmos o código-fonte da página recebida, vemos que a página [Index] está de fato incluída (linha 10 abaixo) na página [Layout]:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width" />
  <title>Tutoriel ASP.NET MVC</title>
</head>
<body>
  <h2>Tutoriel ASP.NET MVC</h2>
  <strong>Vue [Index]...</strong>
</body>
</html>

Vamos agora testar o URL e o [/First]:

  

O URL [/First] estava incompleto. Ele foi preenchido com os valores padrão da rota e passou a ser [/First/Index]. Assim, obtemos o mesmo resultado de antes.

3.4. Ação com um resultado do tipo [ContentResult] - 1

Vamos criar uma nova ação no controlador [First]:


using System.Text;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
  public class FirstController : Controller
  {
    // Índice
    public ViewResult Index()
    {
      return View();
    }
    // Ação01
    public ContentResult Action01()
    {
      return Content("<h1>Action [Action01]</h1>", "text/plain", Encoding.UTF8);
    }
  }
}

A nova ação está definida nas linhas 14 a 18. Ela se limita a retornar uma sequência de caracteres por meio do método [Content] (linha 16) da classe [Controller] (linha 6). Os parâmetros do método são:

  1. a sequência de caracteres da resposta;
  2. um indicador da natureza do texto enviado: “text/plain”, “text/html”, “text/xml”, ... Esse indicador é chamado de tipo MIME (http://fr.wikipedia.org/wiki/Type_MIME);
  3. o terceiro parâmetro permite especificar o tipo de codificação utilizado para o texto.

Em vez de usar o tipo abstrato [ActionResult], nossos métodos especificam o tipo real renderizado (linhas 9 e 14).

Vamos solicitar o URL [/First/Action01]. Obtemos a seguinte página:

  

Vamos examinar o código-fonte da página recebida:

<h1>Action [Action01]</h1>

O navegador recebeu apenas o texto enviado pela ação e nada mais. Esse modo é interessante quando se deseja solicitar ao servidor web dados puros, sem a estrutura HTML ao redor. Observe-se acima que o navegador não interpretou a tag <h1>. Para entender o motivo, vamos examinar no Chrome as trocas de dados:

O navegador enviou os seguintes cabeçalhos:

1
2
3
4
5
6
7
8
GET /First/Action01 HTTP/1.1
Host: localhost:49302
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0,8
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

O servidor respondeu com os seguintes cabeçalhos:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wMQ==?=
X-Powered-By: ASP.NET
Date: Mon, 23 Sep 2013 15:22:33 GMT
Content-Length: 141
  • linha 3: define a natureza do documento. Encontramos os atributos definidos no método [Action01]. Foi porque foi informado de que o documento era do tipo “text/plain” e não “text/html” que o navegador não interpretou a tag <h1> que estava no documento recebido.

3.5. Ação com resultado do tipo [ContentResult] - 2

Consideremos a seguinte terceira ação:


using System.Text;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
  public class FirstController : Controller
  {
   ...
    // Ação02
    public ContentResult Action02()
    {
      string data = "<action><name>Action02</name><description>renvoie un texte XML</description></action>";
      return Content(data, "text/xml", Encoding.UTF8);
    }
  }
}
  • linha 12: define-se um texto XML;
  • linha 13: ele é enviado ao navegador, especificando que se trata de XML com o tipo MIME “text/xml”.

No navegador, obtém-se a seguinte página:

 

Vamos examinar no Chrome a resposta HTTP do servidor:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/xml; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wMg==?=
X-Powered-By: ASP.NET
Date: Mon, 23 Sep 2013 15:29:34 GMT
Content-Length: 176
  • linha 3: define a natureza do documento. Encontramos os atributos definidos no método [Action02];
  • linha 12: o tamanho em bytes do documento enviado pelo servidor.

O documento enviado pelo servidor é este (cópia do Chrome):

 

3.6. Ação com resultado do tipo [JsonResult]

Vamos adicionar a seguinte ação ao controlador [First]:


    // Ação03
    public JsonResult Action03()
    {
      dynamic personne = new ExpandoObject();
      personne.nom = "someone";
      personne.age = 20;
      return Json(personne,JsonRequestBehavior.AllowGet);
}
  • linha 4: uma variável do tipo dynamic. Durante a execução, é possível adicionar livremente propriedades a essa variável. A propriedade é criada ao mesmo tempo em que é inicializada;
  • linhas 5-6: inicializam-se duas propriedades nom e age;
  • linha 7: retorna a representação JSON (Javascript Object Notation) do objeto. O JSON permite serializar um objeto em uma cadeia de caracteres e, inversamente, deserializar uma cadeia em um objeto. É uma alternativa à serialização/deserialização XML;
  • linha 2: a ação retorna um tipo [JsonResult]. Esse tipo só pode ser retornado para uma solicitação POST. Se quisermos retorná-lo para um método GET, é preciso atribuir ao segundo parâmetro do construtor da classe Json (linha 7) o valor JsonRequestBehavior.AllowGet.

Ao solicitar o URL [/First/Action03], o navegador exibe o seguinte:

 

A resposta HTTP do servidor é a seguinte:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wMw==?=
X-Powered-By: ASP.NET
Date: Mon, 23 Sep 2013 15:48:53 GMT
Content-Length: 58
  • linha 3: indica que o documento enviado é o JSON;
  • linha 10: o documento enviado tem 58 bytes. É o seguinte:
[{"Key":"nom","Value":"someone"},{"Key":"age","Value":20}]

O elemento dinâmico [personne] é visto pelo JSON como uma matriz de dicionários, em que cada dicionário:

  • corresponde a um campo da variável [personne];
  • possui duas chaves: “Key” e “Value”. “Key” tem como valor associado o nome do campo e “Value” tem como valor o valor do campo.

3.7. Ação com um resultado do tipo [string]

Vamos adicionar a seguinte ação ao controlador [First]:


    // Ação04
    public string Action04()
    {
      return "<h3>Contrôleur=First, Action=Action04</h3>";
}

Quando solicitamos o URL [/First/Action04] pelo Chrome, obtemos a seguinte resposta:

 

Percebe-se que a tag <h3> foi interpretada. Vejamos a resposta HTTP do servidor:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wNA==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 07:49:00 GMT
Content-Length: 156

e o documento a seguir:

<h3>Contrôleur=First, Action=Action04</h3>

Na linha 3, vemos que o servidor indicou o envio de texto no formato HTML. É por isso que o navegador interpretou a tag <h3>. Quando se deseja enviar texto simples, é preferível, portanto, retornar um [ContentResult] em vez de um [string]. O [ContentResult] permite, de fato, especificar um tipo MIME “text/plain” para indicar que estamos enviando texto sem formatação, portanto, não passível de interpretação por parte do navegador.

3.8. Ação com um resultado do tipo [EmptyResult]

Considere a seguinte nova ação:


    // Ação05
    public EmptyResult Action05()
    {
      return new EmptyResult();
}

A ação se limita a retornar um tipo [EmptyResult]. Nesse caso, o servidor envia uma resposta vazia ao cliente, conforme mostra sua resposta HTTP:

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Cache-Control: private
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wNQ==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 08:11:12 GMT
Content-Length: 0
  • linha 9: o servidor informa ao cliente que está enviando um documento vazio.

3.9. Ação com um resultado do tipo [RedirectResult] - 1

Considere a seguinte nova ação:


    // Ação06
    public RedirectResult Action06()
    {
      return new RedirectResult("/First/Action05");
}

A ação retorna um tipo [RedirectResult]. Esse tipo permite enviar ao cliente uma ordem de redirecionamento para o parâmetro URL do construtor (linha 4). O cliente, então, enviará uma nova solicitação GET para [/First/Action05]. Assim, o cliente faz duas solicitações no total.

O navegador exibe o resultado da segunda solicitação:

 

Vamos examinar a resposta HTTP do servidor no Chrome:

 

Vemos acima as duas solicitações do navegador. Vamos examinar a primeira solicitação, [Action06]. A resposta do servidor, HTTP, é a seguinte:

HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /First/Action05
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wNg==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 08:16:59 GMT
Content-Length: 132
  • linha 1: o servidor respondeu com o código 302 Found. Até então, o código era 200 OK, o que significa que o documento solicitado foi encontrado. O código 302 indica que foi solicitado um redirecionamento. O endereço de redirecionamento é fornecido pela linha 4. Encontramos ali o endereço de redirecionamento que havíamos especificado no código da ação;
  • linha 11: o servidor indica que, com sua resposta HTTP, está enviando um documento do tipo text/html (linha 3) com 132 bytes (linha 11). Quando examinamos no Chrome a resposta à solicitação [Action06], ela está vazia, como era de se esperar. Provavelmente há uma explicação, mas eu não a conheço.

Devido ao redirecionamento, o navegador faz uma nova solicitação GET para o URL especificado na linha 4 acima, como pode ser visto no Chrome na linha 1 abaixo:

1
2
3
4
5
6
7
GET /First/Action05 HTTP/1.1
Host: localhost:49302
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0,8
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4

3.10. Ação com um resultado do tipo [RedirectResult] - 2

Ou seja, a seguinte nova ação:


    // Ação07
    public RedirectResult Action07()
    {
      return new RedirectResult("/First/Action05",true);
}

Na linha 4, adicionamos um segundo parâmetro ao construtor do tipo [RedirectResult]. Trata-se de um booleano cujo valor padrão é false. Quando ele é definido como true, a resposta HTTP enviada ao cliente é alterada. Ela passa a ser:

HTTP/1.1 301 Moved Permanently

Assim, o código de resposta enviado ao cliente agora é 301 Moved Permanently. O redirecionamento ocorre como antes, mas indica-se que esse redirecionamento é permanente. Isso permite que os mecanismos de busca substituam, em seus resultados, o antigo URL pelo novo.

3.11. Ação com um resultado do tipo [RedirectToRouteResult]

Considere a seguinte nova ação:


    // Ação08
    public RedirectToRouteResult Action08()
    {
      return new RedirectToRouteResult("Default",new RouteValueDictionary(new {controller="First",action="Action05"}));
}
  • linha 2: a ação retorna um tipo [RedirectToRouteResult]. Esse tipo permite redirecionar um cliente para um URL especificado não por uma sequência de caracteres, como anteriormente, mas por uma rota.

As rotas são definidas em [App_Start/RouteConfig]. Atualmente, há apenas uma:


      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
  • na linha 4, solicita-se ao cliente que se redirecione para a rota denominada [Default] com a variável controller=First e a variável action=Action05. O sistema de roteamento irá, então, gerar a rota de redirecionamento URL a partir de /First/Action05. É isso que mostra a resposta HTTP do servidor:
HTTP/1.1 302 Found
Cache-Control: private
Content-Type: text/html; charset=utf-8
Location: /First/Action05
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxGaXJzdFxBY3Rpb24wOA==?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 08:58:19 GMT
Content-Length: 132
  • linha 1: o redirecionamento;
  • linha 4: o endereço de redirecionamento gerado pelo sistema de roteamento do URL.

3.12. Ação com um resultado do tipo [void]

Ou seja, a seguinte nova ação:


    // Ação09
    public void Action09()
    {
      string nom = Request.QueryString["nom"] ?? "inconnu";
      Response.AddHeader("Content-Type", "text/plain");
      Response.Write(string.Format("<h3>Action09</h3>nom={0}", nom));
}
  • linha 2: a ação não retorna nenhum resultado. Ela mesma grava no fluxo da resposta enviada ao cliente;
  • linha 4: recupera-se um eventual parâmetro chamado [nom] na solicitação. Este parâmetro é acessível por meio da propriedade [Request] do controlador [Controller], do qual o controlador [First] herda. O parâmetro [nom], passado na forma [/First/Action09?nom=quelquechose], está disponível em Request.QueryString["nom"]. A sintaxe da linha 4 é equivalente a:
string nom=Request.QueryString["nom"];
if(nom==null){
    nom="inconnu";
}
  • linha 5: a resposta enviada ao cliente pode ser acessada por meio da propriedade [Response] do controlador [Controller], do qual o controlador [First] herda;
  • linha 5: define-se o cabeçalho HTTP [Content-Type], que indica a natureza do documento que o servidor está prestes a enviar ao cliente. Aqui, “text/plain” indica que o documento é texto simples que não deve ser interpretado pelo navegador;
  • linha 6: insere-se no fluxo da resposta uma sequência de caracteres. Nela foram incluídas tags HTML que não devem ser interpretadas pelo navegador, uma vez que este já terá recebido anteriormente o cabeçalho HTTP [Content-Type : text/plain"]. É isso que queremos verificar.

Vamos compilar o projeto e solicitar o URL [/First/Action09?nom=someone ][1] e, em seguida, oURL [/First/Action09 ] [2]:

Vamos agora verificar no Chrome a resposta HTTP do servidor:

1
2
3
4
5
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
...
Content-Length: 144
  • linha 3: encontramos o cabeçalho HTTP que nós mesmos definimos no código da ação.

3.13. Um segundo controlador

Vamos criar um segundo controlador no projeto. Seguiremos o método descrito no parágrafo 3.1, página 40. Vamos chamá-lo de [Second].

  

Seu código gerado é o seguinte:


namespace Exemple_01.Controllers
{
  public class SecondController : Controller
  {
    //
    // GET: /Segundo/

    public ActionResult Index()
    {
      return View();
    }

  }
}

Vamos modificá-lo da seguinte maneira:


using System.Text;
using System.Web.Mvc;

namespace Exemple_01.Controllers
{
  public class SecondController : Controller
  {
    // /Segundo/Ação01
    public ContentResult Action01()
    {
      return Content("Contrôleur=Second, Action=Action01", "text/plain", Encoding.UTF8);
    }

  }
}

Em seguida, acessemos o URL [/Second/Action01] em um navegador. Obtemos a seguinte resposta:

 

Este URL foi solicitado com um comando HTTP GET, conforme mostram os logs HTTP da solicitação no Chrome:

GET /Second/Action01 HTTP/1.1

O URL também pode ser solicitado com os comandos HTTP e POST. Para demonstrar isso, vamos usar novamente o aplicativo [Advanced Rest Client]:

  • em [1], iniciamos o aplicativo (na aba [Applications] de uma nova aba do Chrome);
  • em [2], selecionamos a opção [Request];
  • em [3], especifica-se a URL solicitada;
  • em [4], indique que o URL deve ser solicitado com um POST;

Ativamos os logs do Chrome por meio de (CTRL-I) para obter a resposta HTTP do servidor. Ao executar o [Send], a solicitação anterior, as trocas de dados HTTP são as seguintes:

O navegador envia a seguinte solicitação:

POST /Second/Action01 HTTP/1.1
Host: localhost:49302
Connection: keep-alive
Content-Length: 0
Origin: chrome-extension://hgmloofddffdnphfgcellkdfbfbjeloo
User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: */*
Accept-Encoding: gzip,deflate,sdch
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
  • linha 1: o URL é efetivamente solicitado com um POST;
  • linha 4: o tamanho em bytes dos elementos enviados. Não há nenhum aqui.

A resposta HTTP do servidor é a seguinte:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/plain; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 4.0
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcZGF0YVxpc3RpYS0xMzE0XGFzcG5ldFxkdnBcRXhlbXBsZXNcRXhlbXBsZS0wMVxTZWNvbmRcQWN0aW9uMDE=?=
X-Powered-By: ASP.NET
Date: Tue, 24 Sep 2013 10:47:59 GMT
Content-Length: 148
  • linha 3: o servidor envia um documento de texto sem formatação (plain);
  • linha 12: com 148 caracteres.

O documento enviado é o seguinte:

 

Obtém-se o mesmo documento que com o GET.

3.14. Ação filtrada por um atributo

Vamos criar a seguinte nova ação:


    // /Segundo/Ação02
    [HttpPost]
    public ContentResult Action02()
    {
      return Content("Contrôleur=Second, Action=Action02", "text/plain", Encoding.UTF8);
}

A ação [Action02] é análoga à ação [Action01], mas especifica-se que ela só é acessível por meio do comando HTTP POST (linha 2). Outros atributos podem ser utilizados:

HttpGet
serve apenas para o comando GET
HttpHead
serve apenas para o comando HEAD
HttpOptions
serve apenas para o pedido OPTIONS
HttpPut
serve apenas para o pedido PUT
HttpDelete
serve apenas para o comando DELETE

Vamos solicitar o URL [/Second/Action02] diretamente no navegador. Ele é então solicitado por um GET. O navegador exibe então a seguinte resposta:

 

A resposta HTTP do servidor foi a seguinte:

1
2
3
HTTP/1.1 404 Not Found
...
Content-Length: 3807
  • linha 1: o código HTTP 404 Not Found indica que o servidor não encontrou o documento solicitado. Nesse caso, a ação [Action02] não pôde atender à solicitação GET, pois ela atende apenas aos comandos POST;
  • linha 3: o tamanho do documento retornado. Essa é a página que foi exibida pelo navegador:
 

3.15. Recuperar os elementos de uma rota

Nas duas ações descritas anteriormente, escrevíamos algo como:


public ContentResult Action02()
    {
      return Content("Contrôleur=Second, Action=Action02", "text/plain", Encoding.UTF8);
}

Os nomes do controlador e da ação estavam definidos diretamente no código. Se alterarmos esses nomes, o código deixa de funcionar corretamente. Podemos acessar o controlador e a ação da seguinte maneira:


    // /Segundo/Ação03
    public ContentResult Action03()
    {
      string texte = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
      return Content(texte, "text/plain", Encoding.UTF8);
}

A rota definida em [App_Start/RouteConfig] é a seguinte:


      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Na linha 3, os três elementos da rota podem ser obtidos por meio de RouteData.Values["élément"], com o elemento em [controller, action, id].

Vamos solicitar o URL e o [http://localhost:49302/Second/Action03]:

 

Conseguimos recuperar tanto o nome do controlador quanto o nome da ação.