Por ejemplo, supongamos que tenemos una aplicación MVC con un controlador que expone un servicio como el siguiente:
[Route("api/contacts")] public class ContactsController : Controller { private readonly IContactRepository _repository; public ContactsController(IContactRepository repository) { _repository = repository; } [HttpGet("{id}")] public IActionResult Get(int id) { var contact = _repository.Get(id); if (contact == null) return NotFound(); return Ok(contact); } }Esta acción, que retornaría un objeto de tipo
Contact
del modelo de nuestra aplicación, podría ser fácilmente utilizada desde otras capas o sistemas, por ejemplo como sigue:// Petición: GET http://localhost:2805/api/contacts/1 HTTP/1.1 Accept: application/json // Respuesta: HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 Content-Length: 51 {"id":1,"name":"John Smith","phone":"998-12-32-12"}
Publicado por José M. Aguilar a las 8:58 a. m.
Etiquetas: apis, aspnetcoremvc
@Component.Invoke()
o su alternativa asíncrona @Component.InvokeAsync()
, por ejemplo de la forma que vemos a continuación:<div class="cart"> @await Component.InvokeAsync( "ShoppingCart", new { showImages = false, showButtons = false } ) </div>
@Component.Invoke()
es el mismo para todos los view components, ni el entorno ni el compilador pueden ofrecernos ayuda en nombres o parámetros, ni validar que la llamada sea correcta. Cualquier fallo se detectará exclusivamente en tiempo de ejecución.Asimismo, está claro que el código de las vistas es más legible y fluido si en lugar de utilizar sintaxis imperativa, como son las llamadas a helpers, se utiliza un enfoque declarativo, esto es, si utilizamos etiquetas como los célebres tag helpers.
Aunque esta tarea es algo que pueden (y suelen) realizar servidores frontales como IIS o Apache, a veces puede ser interesante que sea nuestra aplicación la que se encargue de ello, por ejemplo si estamos ofreciendo servicios directamente desde los servidores Kestrel o WebListener, o si deseamos aplicar algún tipo de compresión personalizada.
Conceptualmente, la tarea de compresión del resultado de una acción es una buena candidata para ser implementada como filtro. Los filtros son capaces de tomar el control antes y después de la ejecución de las acciones, por lo que, al menos en teoría, serían un buen lugar para implementar la lógica de compresión y retorno al cliente de los resultados, y su uso tendría la siguiente pinta:
public class HomeController : Controller { [Compress] public IActionResult Test() { var text = new string('x', 1000000); return Content(text); } }En este post vamos a ver cómo implementar ese filtro
[Compress]
. Y bueno, aunque implementarlo a mano y desde cero podría tener su gracia, lo que vamos a hacer es aprovechar dos de las últimas e interesantes novedades introducidas recientemente en ASP.NET Core 1.1: el middleware de compresión y la capacidad de utilizar middlewares como filtros de MVC.EnumDropDownListFor()
para simplificar la creación de cuadros desplegables cuyos elementos eran valores de una enumeración, un escenario relativamente frecuente al desarrollar aplicaciones.Su uso era bastante sencillo, como se puede observar en el código mostrado a continuación:
// Model: public enum Language { CSharp, VbNet, FSharp, NodeJs } public class CodeGenerator { public string ApplicationName { get; set; } public Language Language { get; set; } ... } // View: @model CodeGenerator ... @Html.LabelFor(m => m.Language) @Html.EnumDropDownListFor(m => m.Language)Pues bien,
EnumDropDownListFor()
ha dejado de existir en ASP.NET Core MVC, aunque por supuesto se ha incluido una alternativa, que incluso diría que es bastante más acertada, para conseguir el mismo objetivo, pues se ha separado de forma clara la generación del desplegable de la obtención de datos para poblarlo.A la hora de registrar las trazas es posible indicar uno de los niveles definidos en la enumeración
LogLevel
, cuyos miembros son Trace
, Debug
, Information
, Warning
, Error
y Critical
. Estos miembros definen jerárquicamente la "importancia" de los acontecimientos que registramos, de forma que luego podemos configurar los proveedores para que sólo muestren o persistan mensajes a partir de un nivel determinado.A continuación desgranaremos un poco qué se incluye en esta entrega, pero gracias al uso de versionado semántico, sin entrar en más detalles ya podemos saber que esta nueva versión añade características adicionales y mejoras compatibles hacia atrás, por lo que no tendríamos que preocuparnos por romper algo si actualizamos a ella (¡al menos en teoría, claro! ;)).
maxlength
en los cuadros de texto, máxime cuando esta información solíamos incluirla en anotaciones de las clases del modelo con atributos como StringLength
o MaxLength
.Y ha llovido bastante desde entonces, incluso el framework MVC se ha "reseteado" y ahora es ASP.NET Core MVC, pero seguimos sin disponer de esa posibilidad, que cubre un escenario muy frecuente al desarrollar aplicaciones con este framework.
En este post vamos a ver, paso a paso, cómo utilizar los maravillosos tag helpers para incluir este atributo de forma automática en los tags
<input>
vinculados a propiedades del modelo cuyos metadatos indiquen un tamaño máximo para el campo.Me consta que muchos lo estabais esperando con impaciencia, así que me complace anunciaros que desde hace unos días tenéis disponible mi nuevo curso sobre ASP.NET Core MVC en CampusMVP.
Su creación sido un proceso realmente largo y laborioso porque, a diferencia de ediciones anteriores, los cambios que trae consigo el nuevo marco de trabajo han obligado a replantear desde el principio los contenidos, pero creo que el resultado ha valido la pena: un curso potente y completo en el que recorremos el marco de trabajo desde las bases que lo sustentan (ASP.NET Core) hasta la programación de aplicaciones y servicios con el framework ASP.NET Core MVC.
En este post intentaremos responder a las siguientes preguntas:
- ¿En qué consiste el curso de ASP.NET Core MVC?
- ¿Cuáles son los contenidos del curso?
- ¿Es este curso para mi?
- ¿Qué conocimientos previos necesito para seguir el curso?
- ¿Y qué ocurre con MVC 5 o los demás frameworks basados en ASP.NET 4.x?
- Me convence, ¿cuándo empezamos?
El framework nos ofrece distintas fórmulas para generar direcciones, aunque la más básica es utilizar extensores de
IUrlHelper
como Action()
, Link()
o RouteUrl()
. Tenemos instancias de dicho interfaz disponibles en la propiedad Url
de controladores y vistas, pero también podemos reclamarlas directamente al sistema de inyección de dependencias en otros contextos. Asimismo, en las vistas podemos utilizar helpers específicos para generar hipervínculos, como el clásico Html.Action()
, o el más reciente tag helper <a>
. Startup
es donde introducimos el código de inicialización de nuestras aplicaciones. En sus métodos Configure()
y ConfigureServices()
encontraremos aspectos de todo tipo, como los siguientes, por citar sólo algunos:- La configuración del sistema de settings
- Definición de destinos de logs y niveles de trazas
- Configuración de componentes de tratamiento de errores y sistemas de depuración y profiling adicionales
- Configuración de servicios como caching o estado de sesión
- Inserción y configuración de servicios y middlewares del framework, como los destinados al proceso de contenidos estáticos, internacionalización, CORS, u otros
- Middlewares personalizados
- Registro de servicios en el sistema de inyección de dependencias
- Inicializaciones específicas de la aplicación, como seeds de bases de datos o configuración de mapeos entre objetos
- Configuración de rutas de la aplicación
- …
En más de una ocasión me he topado con artículos que sugerían que ASP.NET Core MVC es un middleware y de hecho, incluso en algunos momentos yo también he tenido la sensación de que podía serlo.
Sin embargo, y esta es la respuesta corta si quieres ahorrar tiempo de lectura, estrictamente hablando no lo es.
Hoy vamos a hablar de las variables de sesión, un tema que ya tratamos por aquí hace bastante tiempo, pero que conforme evolucionaba el producto ha cambiado lo suficiente como para dedicarle un nuevo post.
Uno de los requisitos más habituales que tenemos en cualquier aplicación medianamente compleja es la creación de logs donde guardar información cuando ocurran determinados eventos.
En ASP.NET "clásico" teníamos varias opciones, como utilizar las propias herramientas de tracing del marco de trabajo, o bien frameworks especializados de terceros como los célebres Log4Net, NLog o muchos otros, pero al final la idea era la misma, poder disponer de un componente en el que poder registrar sucesos como el siguiente:
public ActionResult RemoveInvoice(string id) { ... // Remove the invoice _logger.LogInformation($"Invoice {id} was removed by {User.Identity?.Name}"); return RedirectToAction("Index"); }El nuevo ASP.NET Core viene equipado de serie con un sistema de logging interno, integrado en el propio marco de trabajo, que proporciona algunas funcionalidades pero, sobre todo, los mimbres básicos sobre los que montar sistemas de trazas más complejos y personalizables.
Dedicaremos este post a ver cómo se configura y utiliza este mecanismo que, como otros aspectos del framework, está construido pensando en su extensibilidad y hace uso intensivo del sistema de inyección de dependencias.
Nota: aunque la RTM se acerca, sigue siendo bueno recordar que todavía estamos utilizando una release candidate de ASP.NET Core y, por tanto, algunos detalles aún podrían variar antes de que aparezca la versión final del producto.
En ASP.NET "clásico" teníamos varias opciones, como utilizar las propias herramientas de tracing del framework o frameworks especializados de terceros como los célebres Log4Net, NLog o muchos otros, pero al final la idea era la misma, poder disponer de un componente en el que poder registrar sucesos como el siguiente:
public ActionResult RemoveInvoice(string id) { ... // Remove the invoice _logger.LogInformation($"Invoice {id} was removed by {User.Identity?.Name}"); return RedirectToAction("Index"); }El nuevo ASP.NET Core viene equipado de serie con un sistema de logging interno, integrado en el propio marco de trabajo, que proporciona funcionalidades básicas pero, sobre todo, los mimbres básicos sobre los que montar sistemas de trazas más complejos y personalizables.
Dedicaremos este post a ver cómo se configura y utiliza este mecanismo que, como otros aspectos del framework, está construido pensando en su extensibilidad, y hace uso intensivo del sistema de inyección de dependencias.
Nota: aunque este componente no es un gran candidato a cambiar dada las pocas actualizaciones que ha sufrido en los últimos meses, es bueno recordar que todavía estamos utilizando una release candidate de ASP.NET Core y, por tanto, algunos detalles aún podrían variar antes de que aparezca la versión final del producto.
Al hilo de lo tratado en el último post sobre la ramificación del pipeline de ASP.NET Core utilizando los extensores
Map()
y MapWhen()
, el amigo Fernando V. dejaba una pregunta interesante en los comentarios del blog:"[…] Igual que hemos visto cómo ramificar el pipeline, sería posible volver a unir las distintas ramas en una sola?"Tampoco creo que sea muy habitual crear este tipo de estructuras en el pipeline, pero la verdad es que me ha parecido un reto muy interesante, lo suficiente como para dedicarle un post ;D
Al representar gráficamente el pipeline de ASP.NET Core, lo habitual es hacerlo como una tubería única en las que colocamos estratégicamente los middlewares que procesarán las peticiones que entrarán por ella.
Incluso a nivel de código, la inserción de middlewares se realiza normalmente invocando métodos del tipo
UseXXX()
sobre la instancia de IApplicationBuilder
que recibimos en el método Configure()
de la clase Startup
de nuestras aplicaciones ASP.NET Core.Esto parece dar a entender que el pipeline es lineal, y que todas las peticiones serán procesadas de la misma forma y por los mismos middlewares, salvo que alguno de ellos decida cortocircuitar la petición.
Sin embargo, aunque eso será lo más frecuente, no es obligatoriamente así. Ya desde los tiempos de Katana tenemos la posibilidad de dividir el pipeline en branches o ramas en función de determinados criterios, permitiéndonos crear configuraciones mucho más complejas y potentes que las que habitualmente suelen mostrarse.
Como ejemplo, en el diagrama lateral podemos observar un pipeline en cuya entrada se encuentra un middleware de gestión de errores, y que a continuación se divide en función de la ruta de la petición de entrada. Así, las peticiones cuyas rutas comiencen por "/SignalR" serán procesadas por los middlewares configurados en la rama de la izquierda; las que comiencen por "/API" serán procesadas a lo largo de la rama derecha, y el resto continuará su proceso por la rama central.
Vamos a ver cómo conseguir crear estas estructuras, pero, como siempre, tened en cuenta que estamos aún trabajando con una versión preliminar de ASP.NET Core y hay detalles que podrían cambiar en la versión definitiva.
StaticFilesMiddleware
y cómo permitir la navegación (o browsing) sobre directorios presentes en el sistema de archivos mediante DirectoryBrowerMiddleware
.Sin embargo, el paquete Nuget Microsoft.AspNetCore.StaticFiles aún contiene algunas perlas adicionales que es conveniente conocer para afinar aún más el comportamiento de nuestro servidor de archivos, e incluso para aumentar la productividad a la hora de implementarlo en nuestras aplicaciones.
Hey, pero como siempre, recordad que aún estamos jugando con una versión preliminar de ASP.NET Core, y hay detalles que podrían cambiar antes de alcanzar la versión final del producto.
Para entrar en materia, en dicho post presentamos
StaticFilesMiddleware
, el componente proporcionado por ASP.NET Core que, insertado convenientemente en el pipeline, es capaz de capturar las peticiones cuya ruta coincide con un recurso estático presente en el sistema de archivos y retornar su contenido al cliente.Sin embargo, el paquete Nuget Microsoft.AspNetCore.StaticFiles contiene otros middlewares que cubren necesidades que podemos encontrar con frecuencia cuando en nuestras aplicaciones necesitemos gestionar contenidos estáticos.
Y aún a riesgo de resultar cansino, antes de continuar os recuerdo que seguimos trabajando con versiones preliminares de ASP.NET Core, por lo que todavía podrían introducirse cambios en el producto.
Publicado por José M. Aguilar a las 9:10 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, middlewares, static files
Una de las principales características del nuevo ASP.NET Core es su diseño modular. El framework no es ahora un mastodonte monolítico sino un gran conjunto de pequeños componentes que incluiremos en nuestras aplicaciones exclusivamente cuando sea necesario.
Esto, que a priori se muestra como una ventaja en términos de rendimiento y flexibilidad de nuestros sistemas, presenta también algunos pequeños inconvenientes que debemos tener en cuenta a la hora de desarrollar aplicaciones.
Por ejemplo, en versiones anterior de ASP.NET no teníamos que preocuparnos demasiado de la gestión de los archivos estáticos (JS, CSS, HTML, fuentes...) porque teníamos por abajo a IIS que se encargaba de procesar las peticiones a este tipo de recursos. Ahora, tras la llegada de ASP.NET Core, las aplicaciones que utilicen archivos estáticos deberán incluir expresamente código para procesarlos.
Pero antes de continuar, recordaros que aún estamos jugando con una release candidate de ASP.NET Core, por lo que parte de lo que veamos a continuación podría no ser totalmente cierto en unas semanas.
Publicado por José M. Aguilar a las 9:10 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, desarrollo, static files
Seguro que estamos todos de acuerdo en que el nuevo sistema de configuración proporcionado por ASP.NET Core mejora bastante lo que teníamos en las versiones más tradicionales de ASP.NET.
Y la posibilidad de almacenar settings en formatos como .INI o .XML resulta interesante, el formato JSON me parece de lo más cómodo y apropiado para muchos escenarios en los que antes no lo teníamos tan sencillo, como definir configuraciones con un cierto nivel de jerarquía como la siguiente: