Agregando un controlador a una aplicación ASP.NET Core MVC

El patrón arquitectural Modelo-Vista-Controlador (MVC) separa la aplicación en tres componentes principales: Modelo, Vista y Controlador. El patrón MVC te ayuda a crear aplicaciones que son mas testeables y más faciles de actualizar que las aplicaciones tradicionales monolíticas.

Las aplicaciones MVC contienen:

  • Modelos: Clases que representan datos en nuestra aplicación. Las clases de modelo utilizan lógica de validación para hacer cumplir las reglas de negocio para esos datos. De manera típica, los objetos de modelo toman y guardan datos del estado del modelo desde y hacia una base de datos. En este tutorial, la clase de modelo Movie obtiene datos de péliculas desde la base de datos, los provee a la vista o los actualiza. Los datos actualizados se guardan nuevamente en la base de datos.
  • Vistas: Las vistas son los componentes que muestran la interfaz de usuario de la aplicación (UI). Generalmente esta UI muestra los datos del modelo.
  • Controladores: Clases que manejan las peticiones de los navegadores web. Ellos obtienen los datos del modelo y llaman a las plantillas de vista para devolver una respuesta. En una aplicación MVC, las vistas solo muestran la información; el controlador maneja y responde a las entradas e interacción del usuario. El controlador maneja los datos de ruta y las cadenas de conexión y pasa estos valores al modelo. El modelo puede utilizar estos valores para consultar a la base de datos. Por ejemplo, http://localhost:1234/Home/About tiene datos de ruta de Home(el controlador) y About (el método de accion que se va a llamar en el HomeController). http://localhost:1234/Movies/Edit/5 es una solicitud para editar la pélicula con el ID=5 utilizando el controlador Movie. Hablaremos de los datos de ruta más adelante en este tutorial.

El patrón MVC te ayuda a crear aplicaciones que separan los diferentes aspectos de la aplicación (la lógica de interacción, la lógica de negocio y la lógica de UI), mientras provee un bajo acoplamiento entre esos elementos. El patrón especifica a donde deberíamos ubicar cada tipo de lógica en la aplicación. La lógica de UI pertenece a la Vista. La lógica de interacción al Controlador. La lógica de negocio pertenece al Modelo (aunque podemos tener el modelo en capas también). Esta separación nos ayuda a manejar la complejidad cuando hacemos nuestras aplicaciones porque nos permite trabajar en un aspecto de la implementación a la vez sin impactar en el código de otro. Por ejemplo, podemos trabajar en la vista sin depender de la lógica de negocio.

Vamos a cubrir estos conceptos en esta serie de tutoriales y veremos como usarlos para construir una aplicación de peliculas. El proyecto MVC contiene carpetas para los controladores y las vistas.

  • En el Explorador de soluciones, hacemos click derecho en Controllers > Agregar > Nuevo elemento…

Agregar nuevo controlador

  • En el dialogo de Agregar nuevo elemento seleccionamos Clase de Controlador
  • Como nombre le ponemos HelloWorldController

Nombre del nuevo controlador

Reemplazamos los contenidos de Controllers/HomeWorldController.cs con los siguientes:

using Microsoft.AspNetCore.Mvc;
using System.Text.Encodings.Web;

namespace MvcMovie.Controllers
{
    public class HelloWorldController : Controller
    {
        // 
        // GET: /HelloWorld/

        public string Index()
        {
            return "Esta es mi acción por defecto...";
        }

        // 
        // GET: /HelloWorld/Welcome/ 

        public string Welcome()
        {
            return "Este es mi método de acción Welcome...";
        }
    }
}

Cada método public en un controlador se puede llamar como un endpoint HTTP. En el ejemplo de arriba, ambos métodos retornan una cadena de texto. Notemos los comentarios que preceden a cada uno de los métodos.

Un endpoint HTTP es una URL a la que puedo acceder en una aplicación web, tal como http://localhost:1234/HelloWorld, y combina el protocolo usado: HTTP, la dirección de red del servidor web (incluyendo el puerto TCP): localhost:1234 y la dirección de URI de destino HelloWorld.

El primer comentario dice que este es un método HTTP GET que se invoca agregando “/HelloWorld/” a la URL de base. Más adelante en el tutorial vamos a usar el motor de scaffolding para generar métodos HTTP POST.

Ejecutemos la aplicación en modo sin depurar y agreguemos “HelloWorld” a la dirección en la barra de direcciones. El método Index devuelve un string.

Acción Default

MVC invoca a las clases de controlador (y los métodos de acción en ellas) dependiendo de la URL que esté entrando. La lógica de ruteo por defecto usada por MVC usa un formato como este para determinar que código va a invocar:

/[Controller]/[ActionName]/[Parameters]

Podemos configurar el formato de las rutas en el método Configure del archivo Startup.cs.

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Cuando ejecutamos la aplicación y no proveemos ningun segmento de la URL, estos toman los valores por defecto de “Home” para el controlador y de “Index” para el método del especificado en la línea de template.

La primera parte del segmento de URL determina que clase de controlador ejecutar. Entonces localhost:xxxx/HelloWorld se mapea a la clase HomeWorldController. La segunda parte del segmento de la URL determina el método de acción en la clase. Entonces localhost:xxxx/HelloWorld/Index causaría que se ejecute el método Index de la clase HomeWorldController. Notemos que solo tuvimos que navegar a localhost:xxxx/HelloWorld y el método Index fue llamado por defecto. Esto se debe a que Index es el método por defecto que se va a llamar en un controlador si el nombre del método no es especificado de manera explícita. La tercera parte del segmento de URL (id) es para los datos de ruta. Vamos a ver los datos de ruta más adelante en este tutorial.

Naveguemos a http://localhost:xxxx/HelloWorld/Welcome. El método Welcome se ejecuta y retorna la cadena “Este es mi método de acción Welcome…". Para esta URL el controlador es Hello World y el método de acción Welcome. No hemos usado aún la parte de la url para el resto de los parámetros.

Acción Welcome

Modifiquemos el código para pasar alguna información de parámetros. Por ejemplo, /HelloWorld/Welcome?name=Rick&numtimes=4. Cambiemos el método Welcome para incluir dos parámetros como se muestra en el siguiente código.

// GET: /HelloWorld/Welcome/ 
// Requiere un using System.Text.Encodings.Web;
public string Welcome(string name, int numTimes = 1)
{
    return HtmlEncoder.Default.Encode($"Hello {name}, NumTimes is: {numTimes}");
}

El código anterior:

  • Usa la característica de C# de parámetros opciones para indicar que el parámetro numTimes toma el valor por defecto 1 si no se pasa ningún valor para ese parámetro.
  • Usa la función HtmlEncoder.Default.Encode para proteger la aplicación de entradas maliciosas (por ejemplo JavaScript).
  • Usa cadenas interpoladas

Ejecutemos la app y vayamos a:

http://localhost:xxxx/HelloWorld/Welcome?name=Rick&numtimes=4

(Reemplacemos xxxx con tu número de puerto). Puedes probar diferentes valores para name y numTimes en la URL. El sistema de bindings de MVC mapea los parámetros con nombre de la query string en la barra de direcciones a los parámetros en nuestro método.

Navegador con query string

En la imagen de arriba, el segmento de la URL de parametros no se usa, los parámetros name y numTimes se pasan como query strings. El caracter ? (signo de pregunta) es un separador, lo que sigue es la queyr string. El caracter & separa cada parámetro de la query string.

Reemplacemos el método Welcome con el código siguiente

public string Welcome(string name, int ID = 1)
{
    return HtmlEncoder.Default.Encode($"Hello {name}, ID: {ID}");
}

Ejecutemos la app y entremos la siguiente URL:

http://localhost:xxx/HelloWorld/Welcome/3?name=Rick

Navegador con parámetro ID

Esta vez el tercer segmento de la URL coincide con el parámetro de ruta id. El método Welcome contiene un parámetro id que coincide con la plantilla de URL que está en el método MapRoute. El signo de pregunta ? (en id?) indica que el parámetro id es opcional.

app.UseMvc(routes =>
{
    routes.MapRoute(
        name: "default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

En estos ejemplos el controlladores ha estado haciendo la porción “VC” de MVC, o sea el trabajo de la vista y del controlador. El controlador está retornando el HTML de manera directa. Generalmente no queremos que el controlador retorne el HTML de manera directa, dado que puede dar lugar a código muy complejo y dificil de mantener. En lugar de eso, lo más normal es que usemos una plantilla de vista Razor para ayudar a generar la respuesta HTML. Eso es lo que vamos a hacer en la próxima parte del tutorial.

En Visual Studio, en modo sin depurar (Ctrl+F5), no necesitas compilar la aplicación después de cambiar el código. Simplemente guardamos el archivo, actualizamos el navegador y podemos ver los cambios.