3. Controladores, acciones, enrutamiento
Consideremos la arquitectura de una aplicación ASP.NET MVC:
![]() |
En este capítulo, analizaremos el proceso que lleva la solicitud [1] al controlador y a la acción [2a] que la procesarán, un mecanismo que se conoce como enrutamiento. Además, presentamos las diferentes respuestas [3] que una acción puede enviar al navegador. Estas pueden ser algo distinto a una vista V [4b].
3.1. La estructura de un proyecto ASP.NET MVC
Creemos un primer proyecto ASP.NET MVC con Visual Studio Express 2012. Lo agregaremos [1] a la solución utilizada en el capítulo anterior:
![]() |
![]() |
- en [2], el nombre del nuevo proyecto;
- en [3, 4], elegimos un proyecto base ASP.NET MVC. Esta plantilla nos proporciona una aplicación web vacía, pero con todos los recursos (DLL, bibliotecas de JavaScript, etc.) necesarios para trabajar.
El proyecto resultante se muestra en [5]. Lo convertiremos en [6], el proyecto inicial de la solución:
![]() |
En [5] se observarán los siguientes puntos:
- la arquitectura del proyecto refleja su modelo MVC:
- los controladores C se colocarán en la carpeta [Controllers],
- los modelos de datos M se colocarán en la carpeta [Models],
- las vistas V se colocarán en la carpeta [Views],
![]() |
- en [1], el archivo [Site.css] será el archivo CSS por defecto de la aplicación;
- en [2], se nos ofrecen varias bibliotecas de JavaScript;
- en [3], encontramos tres vistas específicas: _ViewStart, _Layout y Error.
El archivo [_ViewStart] es el siguiente:
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
- línea 1: el carácter @ indica una secuencia de C# en la vista. De hecho, se puede incluir código C# en una vista;
- línea 2: define una variable Layout que establece la vista principal de todas las vistas. Corresponde a la página maestra del marco clásico ASP.NET.
El archivo [_Layout] es el siguiente:
<!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>
Cuando se genere una vista de la carpeta [Views], su cuerpo se generará mediante la línea 11 anterior. Esto significa que la vista no tiene que incluir las etiquetas <html>, <head> ni <body>. Estas son proporcionadas por el archivo anterior. Por el momento, este es bastante complejo. Simplifiquémoslo:
<!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>
- línea 6: el título común a todas las vistas;
- línea 9: el encabezado común a todas las vistas;
- línea 10: el contenido específico de la vista que se muestra.
![]() |
- [Web.config] es el archivo de configuración de la aplicación web. Es complejo. Será necesario modificarlo cuando se utilice el marco de trabajo [Spring.net] en una arquitectura de múltiples capas.
- [Global.asax] contiene el código que se ejecuta al iniciar la aplicación. Por lo general, este código utiliza los distintos archivos de configuración de la aplicación, entre ellos [Web.config].
3.2. El enrutamiento por defecto de URL
El código de [Global.asax] es, por el momento, el siguiente:
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()
{
...
}
}
}
- línea 6, el namespace de la clase. Proviene directamente del nombre del proyecto y se encuentra en las propiedades del proyecto:

![]() |
Establecemos en [1] el espacio de nombres por defecto. De esta manera, se utilizará de forma predeterminada para todas las clases que se creen en el proyecto.
Volvamos al 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()
{
...
}
}
}
- línea 9: la clase [MvcApplication] deriva de la clase [HttpApplication]. El nombre [MvcApplication] se puede cambiar;
- línea 11: el método [Application_Start] es el método que se ejecuta al iniciar la aplicación web. Solo se ejecuta una vez. Aquí es donde se inicializa la aplicación.
El código de [Application_Start] es actualmente el siguiente:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Por el momento, no es necesario comprender todo este código. Las líneas 5 a 8 definen las rutas aceptadas por la aplicación web. Volvamos a la arquitectura de una aplicación ASP.NET MVC:
![]() |
Ya explicamos que el [Front Controller] debe enrutar un URL hacia la acción encargada de procesarlo. Una ruta sirve para vincular un modelo de URL con una acción. Estas rutas se definen en la carpeta [App_Start] del proyecto mediante las clases [WebApiConfig, FilterConfig, RouteConfig, BundleConfig]:
![]() |
Por el momento, solo nos interesa la clase [RouteConfig]:
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 }
);
}
}
}
Las líneas 12 a 16 definen el formato de los URL que acepta la aplicación. A esto se le llama una ruta. Puede haber varias rutas posibles (= varios formatos de URL posibles). Se distinguen entre sí por su nombre (línea 13). El formato de los URL de la ruta se define en la línea 14. Aquí, el URL tendrá tres componentes:
- {controller}: el nombre de una clase derivada de [Controller]. Se buscará en la carpeta [Controllers] del proyecto. Por convención, si el URL es /X/Y/Z, el controlador encargado de procesar este URL será la clase XController. Se agrega el sufijo «Controller» al nombre del controlador presente en el URL;
- {action}: el nombre de un método en el controlador mencionado anteriormente. Este método recibirá los parámetros que acompañan al URL y los procesará. Este método puede devolver diversos resultados:
- void: la acción generará por sí misma la respuesta al navegador del cliente
- String: la acción devuelve al cliente una cadena de caracteres;
- ViewResult: devuelve una vista al cliente;
- PartialViewResult: devuelve una vista parcial;
- EmptyResult: se envía una respuesta vacía al cliente;
- RedirectResult: solicita al cliente que se redirija a una URL
- RedirectToRouteResult: lo mismo, pero la URL se construye a partir de rutas de la aplicación;
- JsonResult: envía una respuesta JSON
- JavaScriptResult: devuelve un código de JavaScript al cliente;
- ContentResult: devuelve un flujo HTML al cliente sin pasar por una vista;
- FileContentResult: devuelve un archivo al cliente;
- FileStreamResult: lo mismo, pero por otra vía;
- FilePathResult: ...
- {id}: un parámetro que se transmitirá a la acción. Para ello, la acción deberá tener un parámetro llamado id.
La línea 15 define valores por defecto cuando el URL no tiene el formato esperado /{controller}/{action}/{id}. También indica que el parámetro {id} en el URL es opcional. A continuación se muestra una lista de URL incompletas y la URL completada con los valores por defecto:
URL original | URL completado |
/ | /Inicio/Índice |
/Do | /Do/Índice |
/Do/Algo | /Hacer/Algo |
/Hacer/Algo/4 | /Hacer/Algo/4 |
/Hacer/Algo/x/y/z | URL sin enrutar |
3.3. Creación de un controlador y una primera acción
Creemos un primer controlador:
![]() |
![]() |
- en [1], asigne el nombre del controlador con su sufijo [Controller];
- en [2], cree un controlador MVC vacío;
- en [3], ya se ha creado.
El código de [FirstController] es el siguiente:
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();
}
}
}
- línea 7: se generó un namespace por defecto;
- línea 9: la clase [FirstController] deriva de la clase [System.Web.Mvc.Controller];
- líneas 14-17: se generó una acción [Index] por defecto. Es pública. Esto es importante, de lo contrario no se podrá encontrar. Devuelve un tipo [ActionResult], que es una clase abstracta de la que derivan la mayoría de los resultados que suele devolver una acción. Es un tipo «genérico» que se puede especificar reemplazándolo por el nombre real del tipo devuelto;
- línea 16: el método no hace nada. Simplemente devuelve una vista, es decir, un tipo [ViewResult]. No se especifica el nombre de la vista. En este caso, el marco de trabajo busca en la carpeta [Views / First] una vista con el nombre de la acción, es decir, en este caso: [Index.cshtml].
Creemos la vista [Index.cshtml]:
![]() |
- en [1], hacemos clic con el botón derecho en el código de la acción y seleccionamos la opción [Ajouter une vue];
- en [2], el asistente propone una vista con el nombre de la acción. Eso es lo que queremos aquí;
- en [3], por defecto se propone el uso de la página maestra [_Layout.cshtml];
- en [4], una vez validado, el asistente crea la vista en una subcarpeta de la carpeta [Views] con el nombre del controlador (First).
El código generado para la vista [Index] es el siguiente:
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
- líneas 1-3: código C# que define una variable;
- línea 5: una etiqueta HTML.
Reemplazamos todo el código anterior por este:
<strong>Vue [Index]...</strong>
Resumamos:
- tenemos un controlador C llamado [First];
- tenemos una acción llamada [Index] que solicita la visualización de una vista llamada [Index];
- tenemos la vista V [Index].
Podemos llamar a la acción [Index] de dos maneras:
- /First/Index;
- /First, ya que [Index] también es la acción predeterminada en las rutas.
Ejecutemos la aplicación (CTRL-F5). Obtenemos la siguiente página:
1

En [1], la acción solicitada fue http://localhost:49302. No hay ruta. Sabemos que nuestro enrutador espera acciones de la forma /{controller}/{action}/{id}. Al faltar estos elementos, se utilizan los valores predeterminados. La URL URL se convierte en http://localhost:49302/Home/Index. El controlador [Home] no existe. Por lo tanto, la URL URL es rechazada.
Ahora probemos el URL http://localhost:49302/First/Index escribiéndolo directamente en el navegador:
![]() |
La página anterior fue generada por la acción [Index] del controlador [First]. La página generada por esta acción es la vista [Index], cuyo código era el siguiente:
<strong>Vue [Index]...</strong>
Esta genera la parte [1]. La parte [2], por su parte, proviene de la página maestra [_Layout] que definimos hace un momento:
<!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>
La línea 9 generó la sección [2] de la página. La vista [Index], por su parte, solo aparece en la línea 10.
Si mostramos el código fuente de la página recibida, vemos que la página [Index] está incluida (línea 10 a continuación) en la página [Layout]:
Ahora probemos con URL y [/First]:
![]() |
El archivo URL [/First] estaba incompleto. Se completó con los valores predeterminados de la ruta y pasó a ser [/First/Index]. Por lo tanto, se obtiene el mismo resultado que anteriormente.
3.4. Acción con un resultado del tipo [ContentResult] - 1
Creemos una nueva acción en el controlador [First]:
using System.Text;
using System.Web.Mvc;
namespace Exemple_01.Controllers
{
public class FirstController : Controller
{
// Índice
public ViewResult Index()
{
return View();
}
// Acción01
public ContentResult Action01()
{
return Content("<h1>Action [Action01]</h1>", "text/plain", Encoding.UTF8);
}
}
}
La nueva acción se define en las líneas 14 a 18. Simplemente devuelve una cadena de caracteres utilizando el método [Content] (línea 16) de la clase [Controller] (línea 6). Los parámetros del método son:
- la cadena de caracteres de la respuesta;
- un indicador de la naturaleza del texto enviado: "text/plain", "text/html", "text/xml", ... Este indicador se denomina tipo MIME (http://fr.wikipedia.org/wiki/Type_MIME);
- el tercer parámetro permite especificar el tipo de codificación utilizado para el texto.
En lugar de utilizar el tipo abstracto [ActionResult], nuestros métodos especifican el tipo real que se genera (líneas 9 y 14).
Solicitemos el URL [/First/Action01]. Obtenemos la siguiente página:
![]() |
Veamos el código fuente de la página recibida:
El navegador solo recibió el texto enviado por la acción y nada más. Este modo es interesante cuando se desea solicitar al servidor web datos puros sin el formato HTML que los rodea. Cabe señalar que, en el ejemplo anterior, el navegador no interpretó la etiqueta <h1>. Para entender por qué, veamos en Chrome los intercambios:
El navegador envió los siguientes encabezados:
El servidor le respondió con los siguientes encabezados:
- línea 3: establece el tipo de documento. Aquí se ven los atributos definidos en el método [Action01]. Es precisamente porque se le indicó que el documento era de tipo «text/plain» y no «text/html» que el navegador no interpretó la etiqueta <h1> que se encontraba en el documento recibido.
3.5. Acción con un resultado de tipo [ContentResult] - 2
Consideremos la siguiente tercera acción:
using System.Text;
using System.Web.Mvc;
namespace Exemple_01.Controllers
{
public class FirstController : Controller
{
...
// Acción02
public ContentResult Action02()
{
string data = "<action><name>Action02</name><description>renvoie un texte XML</description></action>";
return Content(data, "text/xml", Encoding.UTF8);
}
}
}
- línea 12: se define un texto XML;
- línea 13: se envía al navegador especificando que es XML con el tipo MIME «text/xml».
En el navegador, se obtiene la siguiente página:
![]() |
Veamos en Chrome la respuesta HTTP del servidor:
- línea 3: establece la naturaleza del documento. Se encuentran los atributos definidos en el método [Action02];
- línea 12: el tamaño en bytes del documento enviado por el servidor.
El documento enviado por el servidor es este (copia de Chrome):
![]() |
3.6. Acción con un resultado de tipo [JsonResult]
Agreguemos la siguiente acción al controlador [First]:
// Acción03
public JsonResult Action03()
{
dynamic personne = new ExpandoObject();
personne.nom = "someone";
personne.age = 20;
return Json(personne,JsonRequestBehavior.AllowGet);
}
- línea 4: una variable de tipo dynamic. Durante la ejecución, se pueden agregar libremente propiedades a dicha variable. La propiedad se crea al mismo tiempo que se inicializa;
- líneas 5-6: se inicializan dos propiedades nom y age;
- línea 7: se devuelve la representación JSON (notación de objetos de JavaScript) del objeto. La función JSON permite serializar un objeto en una cadena de caracteres y, a la inversa, deserializar una cadena en un objeto. Es una alternativa a la serialización/deserialización XML;
- línea 2: la acción devuelve un tipo [JsonResult]. Este tipo solo puede devolverse para una solicitud POST. Si se desea devolverlo para un método GET, se debe asignar al segundo parámetro del constructor de la clase Json (línea 7) el valor JsonRequestBehavior.AllowGet.
Al solicitar el URL [/First/Action03], el navegador muestra lo siguiente:
![]() |
La respuesta del servidor es la siguiente:
- línea 3: indica que el documento enviado es JSON;
- línea 10: el documento enviado tiene 58 bytes. Es el siguiente:
El elemento dinámico [personne] es visto por JSON como una matriz de diccionarios donde cada diccionario:
- corresponde a un campo de la variable [personne];
- tiene dos claves: «Key» y «Value». «Key» tiene como valor asociado el nombre del campo y «Value» tiene como valor el valor del campo.
3.7. Acción con un resultado de tipo [string]
Agreguemos la siguiente acción al controlador [First]:
// Acción04
public string Action04()
{
return "<h3>Contrôleur=First, Action=Action04</h3>";
}
Cuando solicitamos el URL [/First/Action04] con Chrome, obtenemos la siguiente respuesta:
![]() |
Se observa que se ha interpretado la etiqueta <h3>. Veamos la respuesta HTTP del servidor:
y el siguiente documento:
En la línea 3, se observa que el servidor indicó que enviaba texto en formato HTML. Por eso el navegador interpretó la etiqueta <h3>. Cuando se quiere enviar texto sin formato, es preferible devolver un [ContentResult] en lugar de un [string]. De hecho, el [ContentResult] nos permite especificar un tipo MIME «text/plain» para indicar que se envía texto sin formato, por lo que el navegador no lo interpretará.
3.8. Acción con un resultado de tipo [EmptyResult]
Consideremos la siguiente acción nueva:
// Acción05
public EmptyResult Action05()
{
return new EmptyResult();
}
La acción simplemente devuelve un tipo [EmptyResult]. En este caso, el servidor envía una respuesta vacía al cliente, como lo muestra su respuesta HTTP:
- línea 9: el servidor le indica a su cliente que le está enviando un documento vacío.
3.9. Acción con un resultado de tipo [RedirectResult] - 1
Consideremos la siguiente acción nueva:
// Acción06
public RedirectResult Action06()
{
return new RedirectResult("/First/Action05");
}
La acción devuelve un tipo [RedirectResult]. Este tipo permite enviar al cliente una orden de redirección hacia el parámetro del constructor URL (línea 4). El cliente emitirá entonces una nueva solicitud GET hacia [/First/Action05]. Por lo tanto, el cliente realiza dos solicitudes en total.
El navegador muestra el resultado de la segunda solicitud:
![]() |
Analicemos la respuesta HTTP del servidor en Chrome:
![]() |
Como se ve arriba, estas son las dos solicitudes del navegador. Analicemos la primera solicitud, [Action06]. La respuesta del servidor, HTTP, es la siguiente:
- línea 1: el servidor respondió con un código 302 Found. Hasta ahora, era 200 OK, lo que significa que se encontró el documento solicitado. El código 302 indica que se solicita una redirección. La dirección de redirección se indica en la línea 4. Ahí encontramos la dirección de redirección que habíamos especificado en el código de la acción;
- línea 11: el servidor indica que, con su respuesta HTTP, envía un documento de tipo text/html (línea 3) de 132 bytes (línea 11). Al examinar en Chrome la respuesta a la solicitud [Action06], esta está vacía, como era de esperarse. Probablemente haya una explicación, pero no la conozco.
Debido a la redirección, el navegador realiza una nueva solicitud GET hacia la URL indicada en la línea 4 anterior, como se puede ver en Chrome en la línea 1 a continuación:
3.10. Acción con un resultado de tipo [RedirectResult] - 2
O bien, la siguiente acción nueva:
// Acción07
public RedirectResult Action07()
{
return new RedirectResult("/First/Action05",true);
}
En la línea 4, se ha agregado un segundo parámetro al constructor del tipo [RedirectResult]. Se trata de un valor booleano cuyo valor predeterminado es false. Al establecerlo en true, se modifica la respuesta HTTP enviada al cliente. Queda así:
De este modo, el código de respuesta enviado al cliente es ahora 301 Moved Permanently. La redirección se realiza como antes, pero se indica que esta redirección es permanente. Esto permite a los motores de búsqueda reemplazar en sus resultados el antiguo URL por el nuevo.
3.11. Acción con un resultado del tipo [RedirectToRouteResult]
Veamos la siguiente acción nueva:
// Acción08
public RedirectToRouteResult Action08()
{
return new RedirectToRouteResult("Default",new RouteValueDictionary(new {controller="First",action="Action05"}));
}
- línea 2: la acción devuelve un tipo [RedirectToRouteResult]. Este tipo permite redirigir a un cliente hacia un URL especificado no mediante una cadena de caracteres como antes, sino mediante una ruta.
Las rutas se definen en [App_Start/RouteConfig]. Actualmente solo hay una:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
- En la línea 4, se le pide al cliente que se redirija a la ruta denominada [Default] con la variable controller=First y la variable action=Action05. El sistema de enrutamiento generará entonces la ruta de redirección URL a partir de /First/Action05. Esto es lo que muestra la respuesta HTTP del servidor:
- línea 1: la redirección;
- línea 4: la dirección de redirección generada por el sistema de enrutamiento de URL.
3.12. Acción con un resultado de tipo [void]
Sea la siguiente acción nueva:
// Acción09
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));
}
- línea 2: la acción no devuelve ningún resultado. Escribe directamente en el flujo de la respuesta enviada al cliente;
- línea 4: se recupera un posible parámetro llamado [nom] en la solicitud. Se puede acceder a este parámetro a través de la propiedad [Request] del controlador [Controller], del cual hereda el controlador [First]. El parámetro [nom], pasado en la forma [/First/Action09?nom=quelquechose], está disponible en Request.QueryString["nom"]. La sintaxis de la línea 4 es equivalente a:
- línea 5: se puede acceder a la respuesta enviada al cliente a través de la propiedad [Response] del controlador [Controller], de la cual hereda el controlador [First];
- línea 5: se establece el encabezado HTTP [Content-Type], que indica la naturaleza del documento que el servidor está a punto de enviar al cliente. En este caso, «text/plain» indica que el documento es texto sin formato que el navegador no debe interpretar;
- línea 6: se escribe una cadena de caracteres en el flujo de la respuesta. En ella se han incluido etiquetas HTML que el navegador no debe interpretar, ya que este habrá recibido previamente el encabezado HTTP [Content-Type : text/plain"]. Esto es lo que queremos verificar.
Compilemos el proyecto y solicitemos el URL [/First/Action09?nom=someone ][1] y luego elURL [/First/Action09 ] [2]:
![]() |
Ahora veamos en Chrome la respuesta HTTP del servidor:
- línea 3: encontramos el encabezado HTTP que nosotros mismos habíamos establecido en el código de la acción.
3.13. Un segundo controlador
Creemos en el proyecto un segundo controlador. Seguiremos el método descrito en el párrafo 3.1, página 40. Lo llamaremos [Second].
![]() |
Su código generado es el siguiente:
namespace Exemple_01.Controllers
{
public class SecondController : Controller
{
//
// GET: /Segundo/
public ActionResult Index()
{
return View();
}
}
}
Modifiquémoslo de la siguiente manera:
using System.Text;
using System.Web.Mvc;
namespace Exemple_01.Controllers
{
public class SecondController : Controller
{
// /Segundo/Acción01
public ContentResult Action01()
{
return Content("Contrôleur=Second, Action=Action01", "text/plain", Encoding.UTF8);
}
}
}
Luego solicitemos el URL [/Second/Action01] con un navegador. Obtenemos la siguiente respuesta:
![]() |
Este URL se solicitó con un comando HTTP GET, tal como lo muestran los registros HTTP de la solicitud en Chrome:
El URL también se puede solicitar con un comando HTTP POST. Para demostrarlo, utilicemos nuevamente la aplicación [Advanced Rest Client]:
![]() |
- en [1], iniciamos la aplicación (en la pestaña [Applications] de una nueva pestaña de Chrome);
- en [2], seleccionamos la opción [Request];
- en [3], se especifica la URL solicitada;
- en [4], se indica que el URL debe solicitarse con un POST;
Se activan los registros de Chrome mediante (CTRL-I) para obtener la respuesta HTTP del servidor. Al ejecutar [Send], la solicitud anterior, los intercambios HTTP son los siguientes:
El navegador envía la siguiente solicitud:
- línea 1: se solicita correctamente el URL con un POST;
- línea 4: el tamaño en bytes de los elementos enviados. Aquí no hay ninguno.
La respuesta HTTP del servidor es la siguiente:
- línea 3: el servidor envía un documento de texto sin formato (plain);
- línea 12: de 148 caracteres.
El documento enviado es el siguiente:
![]() |
Se obtiene el mismo documento que con el GET.
3.14. Acción filtrada por un atributo
Creemos la siguiente acción nueva:
// /Segundo/Acción02
[HttpPost]
public ContentResult Action02()
{
return Content("Contrôleur=Second, Action=Action02", "text/plain", Encoding.UTF8);
}
La acción [Action02] es similar a la acción [Action01], pero se indica que solo se puede acceder a ella mediante el comando HTTP POST (línea 2). Se pueden utilizar otros atributos:
HttpGet | solo sirve para el comando GET |
HttpHead | solo sirve para el comando HEAD |
HttpOptions | solo sirve para el pedido OPTIONS |
HttpPut | solo sirve para el pedido PUT |
HttpDelete | solo sirve para el comando DELETE |
Solicitemos el URL [/Second/Action02] directamente en el navegador. Entonces se solicita mediante un GET. El navegador muestra entonces la siguiente respuesta:
![]() |
La respuesta HTTP del servidor fue la siguiente:
- línea 1: el código HTTP 404 Not Found indica que el servidor no encontró el documento solicitado. En este caso, la acción [Action02] no pudo atender la solicitud GET, ya que solo atiende los comandos POST;
- línea 3: el tamaño del documento devuelto. Esta es la página que mostró el navegador:
![]() |
3.15. Recuperar los elementos de una ruta
En las dos acciones descritas anteriormente, escribíamos algo como esto:
public ContentResult Action02()
{
return Content("Contrôleur=Second, Action=Action02", "text/plain", Encoding.UTF8);
}
Los nombres del controlador y de la acción estaban escritos de forma estática en el código. Si cambiamos estos nombres, el código ya no funciona correctamente. Podemos acceder al controlador y a la acción de la siguiente manera:
// /Segundo/Acción03
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);
}
La ruta definida en [App_Start/RouteConfig] es la siguiente:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
En la línea 3, los tres elementos de la ruta se pueden obtener mediante RouteData.Values["élément"] con el elemento en [controller, action, id].
Solicitemos el URL y el [http://localhost:49302/Second/Action03]:
![]() |
Hemos recuperado correctamente tanto el nombre del controlador como el nombre de la acción.




























