Módulos y rutas en NancyFx

Este artículo continúa la serie de introducción a Nancy, un framework para aplicaciones web escrito en C#. En el artículo anterior (Primer contacto con NancyFX) hacíamos una breve introducción a la sintaxis, y veíamos un ejemplo muy básico de rutas. En este veremos con un poco más de detalle qué es un módulo, cómo funciona el sistema de rutas, cómo definirlas, y qué tipo de respuestas podemos dar.

Módulos

using System;

namespace NancyFxSinatra
{
	public class SampleModule : Nancy.NancyModule
	{
		public SampleModule()
		{
			Get["/"] = _ => "Hello World!";
		}
	}
}

En el artículo anterior mostrábamos el siguiente código. Las rutas están definidas en el constructor de un módulo, que es a su vez la pieza clave que define el comportamiento de nuestra aplicación.

Un módulo hereda de la clase NancyModule y se puede definir en cualquier parte de la aplicación. No es necesario declararlos explícitamente ya que Nancy se encargará de buscarlos por nosotros. Esta búsqueda se realiza al iniciar la aplicación, quedando en caché una vez encontrados.

Esto nos permitiría, en una hipotética aplicación de gestión tener nuestro ProductModule, que gestionara productos, y nuestro CategoriesModule que gestionara categorías. Los módulos pueden encontrarse en diferentes carpetas, o incluso en diferentes ensamblados.

Rutas

Todos los caminos conducen a Roma

Una ruta se compone por:

Método + Patrón + Acción.

Método

El método es el verbo HTTP por el que accedemos a un recurso. A continuación podemos ver un ejemplo de acceso a diferentes recursos con el mismo patrón.

using System;

namespace NancyFxSinatra
{
	public class Routes : Nancy.NancyModule
	{
		public Routes ()
		{
			Get["/routes"] = _ => "Respuesta usando GET\n";
			Post["/routes"] = _ => "Respuesta usando POST\n";
		}
	}
}

Para probarlo en este caso usaremos la línea de comandos y la utilidad curl, que permite hacer peticiones a una pagina, y devuelve el resultado. Se encuentra disponible para Mac y Linux de manera nativa, y para Windows lo podemos obtener de su página oficial.

Si queremos probar la llamada por GET, usamos el comando:

curl -X GET http://127.0.0.1:8080/routes

Y la salida que obtendremos será:

Respuesta usando GET

Por otra parte, si queremos probar la llamada por POST, el comando será:

curl -X POST http://127.0.0.1:8080/routes

Con la correspondiente salida:

Respuesta usando POST

Así podemos probar el resto de métodos HTTP soportados, que son GET, POST, PUT, DELETE, PATCH, HEAD y OPTIONS.

Patrón

El patrón, por su parte, nos permite detectar una url específica, así como capturar elementos. Podemos destacar los siguiente patrones:

  • «/users»: Detecta la coincidencia exacta.
  • «/users/{id}»: Al caso anterior, captura además cualquier elemento que se pase a continuación. En el caso de «/users/roberto», id tendría el valor de roberto.
  • «/users/{id}/{format?}»: Además de lo anterior, detecta una ruta que puede contener, o no ese valor. En el caso de «/users/roberto/json», format tendría el valor de json. Si por lo contrario usamos una ruta de «/users/roberto» también será capturada, ya que el parámetro es opcional.
  • «users/{cur*}»: A diferencia del anterior, solamente detecta y captura entradas que comiencen por cur, por lo tanto «/users/roberto» no sería detectado, pero «/users/cur1239» sí.

Podemos encadenar patrones como hemos visto antes para crear rutas más complejas, y usar expresiones regulares para una captura más específica.

Acción

Las acciones son el contenido de nuestra llamada, es decir, el resultado de la misma, es de tipo Response, sin embargo, según la respuesta del método se puede devolver:

  • entero: Se interpreta entonces como código de estado HTTP (ejemplo, 404).
  • HttpStatusCode: Código de respuesta incluido en Nancy, que corresponde también a estados HTTP.
  • texto: Interpretado como el cuerpo de la respuesta.

Para demostrarlo, veamos 4 tipos de respuesta, las 3 primeras son las que hemos mostrado, mientras que la última devuelve un error y además de una respuesta en el cuerpo.

Get ["/actions/ok"] = _ => 202;
Get ["/actions/auth"] = _ => HttpStatusCode.Forbidden;
Get ["/actions/text"] = _ => "Hola Mundo";Get["/actions/error"] = _ => {
    Response res =  ((Response)"Error con mensaje\n");
    res.StatusCode = HttpStatusCode.InternalServerError;
    return res;
};

Usando curl (con la opción -v para que muestre también las cabeceras) podemos comprobar que el resultado de la petición corresponde con los códigos que hemos mostrado.

Resumen

En este artículo hemos visto qué son los módulos, cómo, usando comandos HTTP, podemos especificar la manera para acceder a un recurso. Hemos visto también, cómo capturar rutas específicas o que cumplan con algún criterio, y finalmente, hemos devuelto mensajes HTTP usando números y enumerados. En el próximo artículo veremos cómo usar ViewEngines y plantillas HTML para contenido.

Más información:

Creando un servidor HTTP básico con C#

Una de las prácticas de la carrera, de la asignatura Redes II, consistía en hacer un servidor HTTP básico, que manejara comandos GET, POST, y mostrara páginas y ficheros de cualquier tipo. Esto en C es un auténtico coñazo, ya que tardas más tiempo pegándote con los punteros que trabajando en la solución, así que casi que además voy a hacer una copia en C#, y lo dejo por escrito ;).

Continuar leyendo «Creando un servidor HTTP básico con C#»