Veamos muy rápidamente en qué consiste.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, netcore, trucos
Centrándonos en ASP.NET Core, la versión 2.1 incluye numerosas mejoras sobre su predecesora. Muchos componentes internos han sido reescritos y se han añadido características que, sin duda, continúan haciendo de ASP.NET Core un framework cada vez más completo y eficiente.
Pero eso sí, no rompe nada y todo lo que sabemos de ASP.NET Core 2.0 sigue siendo válido (de ahí que sea simplemente una revisión 2.x: no hay breaking changes).
En este post vamos a ver por encima las novedades que creo más destacadas de esta entrega.
IHostedService
y registrar dicha implementación en el inyector de dependencias.Sin embargo, aunque podía parecer lo contrario, la implementación correcta del interfaz
IHostedService
no era una tarea sencilla. De hecho, como se comenta en este issue de Github, IHostedService
era un interfaz de muy bajo nivel y, al no quedar claro cómo debía utilizarse en escenarios reales, podría dar lugar a bastantes confusiones y problemas.- Procesar la plantilla CSHTML y obtener código C# que permita renderizarla.
- A continuación, compilar dicho código para obtener un componente ejecutable.
- Por último, ejecutar el código para renderizar la plantilla.
¡Seguimos! ;)
ImprPant
, o a lo sumo Alt+ImprPant
si sólo necesito el contenido de una ventana; tras ello, acudo a una herramienta de edición como Paint.net, pego la imagen desde el portapapeles y desde ahí recorto la sección que me interesa. Pues sí, todo muy manual, como en los viejos tiempos ;) Creo que estas funciones deben andar por ahí desde los tiempos de Windows 95 o quizás antes, así que simplemente sigo haciendo lo mismo que he hecho toda la vida.
Pero como ha ocurrido en otras ocasiones, creo que debo ser de los últimos en saber que Windows incluye desde hace ya bastante tiempo herramientas más sofisticadas para conseguir capturas de pantalla de forma sencilla, así que aprovecharemos este post para hacer una pequeña puesta al día.
El problema que tiene intentar usar Razor de esta forma es que estamos muy malacostumbrados ;) ASP.NET Core hace mucho trabajo por nosotros y puede hacernos ver que renderizar una vista es algo trivial, pero no lo es; la vista debe ser parseada para obtener de ella un código C# que más adelante será compilado al vuelo para generar un ensamblado que será anexado a nuestra aplicación de forma dinámica y que será utilizado en cada renderización. Y todo ello, de forma rápida y eficiente en recursos.
La renderización de una vista Razor desde una aplicación de consola “pura” consiste en seguir estos mismos pasos, pero de forma manual. Lo que veremos a lo largo de un par de posts es:
- Cómo generar código C# parseando una plantilla Razor, es decir, un archivo
.cshtml
. - Cómo compilar el código C# obtenido y generar un ensamblado con Roslyn.
- Cómo cargar dinámicamente dicho ensamblado en memoria.
- Cómo ejecutar una vista presente en dicho ensamblado y obtener el resultado.
Nota: el objetivo de estos posts es puramente didáctico, y su única intención es aprender algo sobre las tripas de ASP.NET Core. No nos meteremos en optimizar estas operaciones o introducir mejoras como cacheado o similares, ni en ofrecer una solución funcionalmente completa. Por tanto, lo que veremos aquí no será production ready, pero sí un buen punto de partida para que podáis crear vuestras propias soluciones.
Hay varios algoritmos para conseguirlo, pero el llamado Fisher-Yates shuffle es muy eficiente (O(N)), no necesita almacenamiento extra, es fácil de implementar y ofrece unos resultados más que razonables. Este algoritmo permite generar una permutación aleatoria de un conjunto finito de elementos o, en otras palabras, desordenar los elementos de un array.
Pues bien, al hilo de esto, en este post veremos muy rápidamente cómo podemos implementar estas redirecciones en ASP.NET Core MVC que, como veréis, nos lo pone bastante fácil.
Y es que el problema con las costumbres es que son difíciles de quitar; por ejemplo, probablemente muchos estamos acostumbrados a obtener el nombre de los miembros de un enum de la siguiente forma:
public enum Color { Red, Green, Blue }
static void Main(string[] args)
{
Console.WriteLine(Color.Red.ToString());
// Output: Red
}
A priori es correcto: llevamos años haciéndolo así y no nos ha ido mal del todo. Bueno, o sí, pero probablemente no por este motivo ;)@section
que más adelante se incluyen en el Layout para formar parte de la información enviada al lado cliente.Sin embargo, ya sabemos que no es posible utilizar secciones en vistas parciales o cualquier otro tipo de componentes, lo cual complica algunos escenarios.
Por ejemplo, es relativamente frecuente que en el lado servidor generemos código HTML utilizando algún mecanismo de reutilización de marcado, como vistas parciales, templated helpers, tag helpers, etc., y que éste requiera recursos extra como scripts o CSS que obligatoriamente deberíamos incluir en las vistas principales donde se utilicen.
Más en el terreno práctico, imaginad que creamos una vista parcial que requiere que las páginas donde vayamos a usarla incluyan una referencia a un archivo de scripts y un bloque de inicialización como el siguiente:
@* Partial view: _MyComponent.cshtml *@
<div class="my-component">
<!-- UI goes here -->
</div>
<!-- And now, add dependencies and initialization code -->
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" ></script>
<script>
// Initialization code goes here
$(".my-component").setup();
</script>
Aquí es donde comienzan los problemas. Si lo enfocamos como hemos mostrado anteriormente y resulta que en una misma página podemos tener más de una instancia de dicha vista parcial, estaremos incluyendo en la página las referencias y código de inicialización más de una vez, lo cual podría traernos bastantes dolores de cabeza:<html>
<body>
...
<!-- My Component, instance #1 -->
@Html.Partial("_MyComponent.cshtml")
...
<!-- My Component, instance #2 -->
@Html.Partial("_MyComponent.cshtml")
...
</body>
</html>
Además de problemas de rendimiento y mal funcionamiento de la aplicación, también podría ocurrir que al insertar la parcial en la página, los scripts quizás aparecerían antes de referenciar los componentes o frameworks utilizados, pues normalmente esto suele hacerse al cerrarse el <body>
. Por ejemplo, en el código anterior nuestro componente utiliza JQuery, por lo que necesitamos que este framework se cargue antes de que nuestro código aparezca en la página, cosa que no siempre podremos asegurar. Así, el código enviado a la página podría ser el siguiente:
<html>
<body>
...
<!-- My Component, instance #1 -->
<div class="my-component">
<!-- UI goes here -->
</div>
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" ></script>
<script>
// Initialization code goes here
$(".my-component").setup();
</script>
...
<!-- My Component, instance #2 -->
<div class="my-component">
<!-- UI goes here -->
</div>
<script src="path/to/my-component.js" ></script>
<link rel="stylesheet" href="path/to/my-component.css" ></script>
<script>
// Initialization code goes here
$(".my-component").setup();
</script>
...
<script src="/scripts/jquery.min.js"></script>
</body>
</html>
Obviamente, esto provocaría un error en la página.En este post vamos a ver un posible enfoque para solucionar este escenario. La idea es bastante sencilla, pues simplemente vamos a hacer posible que cada vista parcial o componente pueda registrar código HTML que luego sea renderizado en un lugar específico de la página. El objetivo sería algo similar a lo que conseguimos con la definición de secciones
@section
y @RenderSection()
, pero aplicable a cualquier tipo de vista parcial.Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, componentes, dynamicsections, trucos
Desde el punto de vista práctico, es una solución fantástica y muy cómoda de utilizar, y de hecho seguro que en más de una ocasión os habréis visto abusando de ella, es decir, habréis introducido en claims más información de la cuenta sólo por lo cómodo que resulta utilizarlo.
Pero claro, esto tiene su coste. Cada dato extra que añadimos a esos tokens supone transferencia adicional a la hora de recibir peticiones y retornar resultados, lo que puede repercutir en costes de operación si nuestro sistema tiene mucho tráfico, y en cualquier caso, aumentar los tiempos de respuesta de la aplicación.
Para solucionar estos problemas, ASP.NET Core incorpora el concepto de claims transformation, que son componentes ejecutados durante la fase de autenticación que toman el
ClaimsPrincipal
generado a partir de la información recibida en el token y permiten extenderlo o modificarlo para añadirle claims o atributos adicionales.De esta forma, podríamos introducir en los tokens que viajan por la red un único claim con el nombre o identificador único del usuario, y mediante una transformación posterior añadirle atributos adicionales, como su nombre completo, roles de seguridad y otros que nos hagan falta a nivel de aplicación.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, autenticación, jwt, trucos
En las versiones "clásicas" de ASP.NET MVC era algo que se podía resolver con relativa facilidad gracias a proyectos como RazorGenerator o RazorEngine, pero, como en ASP.NET Core las cosas han cambiado bastante, he pensado que quizás sería interesante comentar por aquí cómo podríamos conseguirlo en este nuevo framework.
Resulta que en mitad de una sesión de trabajo normal, de repente todo lo que tenía en pantalla comenzó a mostrarse en escala de grises. Para entendernos, lo que tenía por delante era más o menos lo siguiente, que no puedo negar que tenía su encanto:
O también se os habrá dado el caso de que necesitéis hacer cambios en un script y os da miedillo subirlo directamente a producción sin haberlo probado antes suficientemente en el entorno real. Esto es un tema especialmente peliagudo si estáis desarrollando una plataforma SaaS en la que cientos de clientes usen en sus páginas web una referencia a vuestro script y cualquier cambio en el mismo puede traer consecuencias graves para el servicio.
Por ejemplo, imaginad que prestamos un servicio similar a Google Analytics, y todos nuestros clientes tienen una referencia en sus páginas a nuestro archivo "analytics.js" para trackear sus visitas. ¿Cómo podríamos introducir cambios en dicho archivo de script para hacer pruebas en sitios donde nos interese, sin que afectara al resto de clientes ni a los visitantes de sus páginas?
A priori no parece haber una respuesta sencilla, pero sí que la hay. Sólo necesitamos Fiddler y tres minutos de nuestro tiempo para conseguirlo :)
Pero dado que siempre hablábamos de Visual Studio, es lógico preguntarse si posible conseguir exactamente lo mismo desde la línea de comandos o, llevándolo al extremo, en entornos no Windows como Linux o Mac, así que en este post veremos cómo conseguirlo.
Tras seleccionar el tipo de elemento a crear, aparecerá un segundo cuadro de diálogo solicitándonos información sobre el mismo. Por ejemplo, en la siguiente captura de pantalla se muestra el diálogo de creación de vistas MVC, donde debemos introducir el nombre de la vista, la plantilla a utilizar, la clase del Modelo para vistas tipadas, etc:
Después de cumplimentar estos datos, se generará automáticamente el código fuente del elemento indicado. Hasta aquí bien, pero, ¿qué ocurre si ese código generado no se ajusta exactamente a nuestras necesidades? O preguntándolo de otra forma, ¿es posible modificar las plantillas de generación de código utilizadas en estos casos para adaptarlas a nuestras preferencias?
Pues sí, y vamos a ver cómo :)
Resulta que varias veces he publicado un proyecto ASP.NET Core y, tras finalizar y probar un poco la aplicación, he visto que me había dejado por detrás alguna chorradilla en una vista que tenía que corregir rápidamente. En lugar de volver a publicar el proyecto completo, cuando me ocurre esto suelo a retocar la vista y actualizar sólo ese archivo en el servidor, por ejemplo desde el menú contextual del archivo en Visual Studio:
Pero en este caso, una vez finalizó la subida del archivo al servidor, pulsé F5 en el navegador para comprobar que ya estaba todo correcto y… ¡vaya, todo seguía igual que antes! No pasa nada, pensé que no había publicado bien, por lo que volví a repetir el proceso y pocos segundos después pude comprobar que los cambios seguían sin ser aplicados en el servidor. ¿Qué podía estar ocurriendo?
Ah, claro, ¡el caché! La vista era retornada por una acción MVC decorada con el filtro
ResponseCache
, por lo que podría ser normal que continuara llegando al navegador la versión anterior. Eliminé caché, incluso probé desde otro navegador y ¡todo seguía igual que antes!Ya lo único que se me ocurría es que la publicación hubiera fallado por algún siniestro bug de Visual Studio que no dejara rastro en las trazas, así que decidí ignorar al intermediario. Fui directamente al servidor para editar manualmente el archivo de la vista y… maldición, ¡no la encuentro! :-/
¿Qué está ocurriendo aquí?
https://www.variablenotfound.com/2018/05/implementando-mas-facilmente-background.html
Es relativamente frecuente encontrar aplicaciones en las que necesitamos disponer de un proceso en background ejecutándose de forma continua. Hace poco hablábamos de IApplicationLifetime, un servicio del framework que nos permitía introducir código personalizado al iniciar y detener las aplicaciones, y probablemente habréis pensado que éste sería un buen sitio para gestionar el inicio y finalización de estas tareas en segundo plano.
Y aunque es posible, ASP.NET Core proporciona otras fórmulas más apropiadas para conseguirlo: los hosted services. Mediante este mecanismo, podemos crear servicios que serán gestionados por el host, y que serán iniciados y finalizados automáticamente junto con la aplicación.
public async Task<IActionResult> GetTheAnswerToLifeUniverseAndEverything()
{
await Task.Delay(30000); // Simulando un proceso costoso...
return Content("42!");
}
Cuando nuestros usuarios pulsen dicho botón, necesariamente habrán de esperar varios segundos para obtener una respuesta. Pero como suelen ser Obviamente, esto no hace sino empeorar las cosas. El servidor, que ya estaba ocupado intentando responder la primera petición, no tiene ya que atender a una, sino a todas las que se han generado tras este ataque, cuando en realidad no tiene sentido: para tranquilizar al usuario basta con entregarle el resultado de una de ellas, por lo que todos los hilos sobrantes simplemente están malgastando recursos del servidor realizando operaciones para obtener resultados que nadie va a consumir.
Estaría bien poder cancelar esas peticiones largas si tenemos la seguridad de que ningún cliente está esperándolas, ¿verdad?
Otro ejemplo muy habitual lo encontramos con
MapPath()
, un método perteneciente a la clase HttpServerUtility
de ASP.NET "clásico" que utilizábamos para obtener una ruta física a partir de la ruta virtual o relativa de un recurso. Por ejemplo, en el siguiente código mostramos cómo averiguar la ruta en disco de una imagen utilizando este método:var path = HttpContext.Current.Server.MapPath("~/images/image.jpg"); // path = C:\inetpub\wwwroot\mysite\images\image.jpgPues bien, ni en ASP.NET Core ni en MVC tenemos disponible la clase
System.Web.HttpContext
, ni por tanto una propiedad Server
de tipo HttpServerUtility
que usábamos para invocar al método MapPath()
. Sin embargo, disponemos de herramientas alternativas que nos permiten conseguir lo mismo, aunque, eso sí, de forma algo menos directa.