Sin embargo, a veces olvidamos que estas mismas técnicas podemos utilizarlas para simplificar código en la implementación de convenciones o funciones más ligadas al negocio o a funcionalidades de nuestra aplicación. Por ejemplo, ¿no estáis aburridos de escribir acciones como las siguientes?
public class FriendsController: Controller
{
public IActionResult View(int id)
{
var friend = _friendServices.GetById(id);
if(friend == null)
return NotFound();
... // Prepare and show "View" view
}
public IActionResult Edit(int id)
{
var friend = _friendServices.GetById(id);
if(friend == null)
return NotFound();
... // Prepare and show "Edit" view
}
public IActionResult Delete(int id)
{
var friend = _friendServices.GetById(id);
if(friend == null)
return NotFound();
... // Prepare and show "Delete" view
}
...
}
Pues bien, vamos a aplicar el mismo principio para simplificar este código y eliminar duplicidades extrayendo las porciones comunes a un filtro, resultando en algo así de bonito:
[Autoload(typeof(Friend))] // ¡Magia!
public class FriendsController: Controller
{
public Task<IActionResult> View(Friend friend)
{
... // Prepare and show "View" view
}
public Task<IActionResult> Edit(Friend friend)
{
... // Prepare and show "Edit" view
}
public Task<IActionResult> Delete(Friend friend)
{
... // Prepare and show "Delete" view
}
...
}
No sé lo útil que podrá resultar en la práctica pero, como mínimo, nos ayudará a conocer mejor cómo funciona por dentro el framework ASP.NET Core MVC.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, trucos
Básicamente, la duda era la siguiente:
¿Hay alguna forma sencilla de añadir el atributo [Authorize]
, pero sólo a los controladores que se encuentren en una carpeta determinada del proyecto, sin tener que aplicarlo uno a uno?
La respuesta rápida es: sí. Bueno, con matices ;) Incluso hay varias opciones, así que vamos a ver algunas de ellas, y de paso repasamos conceptos de ASP.NET Core MVC :)Lo habitual es echar mano de los status code de HTTP para indicar problemas en el proceso de una petición; de hecho, este protocolo dispone de un rico conjunto de códigos que en principio parecen cubrir todas nuestras necesidades.
Pero no siempre es así. Por ejemplo, si tenemos un servicio que permite a los clientes de una empresa formalizar un pedido a través de un API y una llamada a este servicio retorna un error HTTP 403 (forbidden), claramente estamos indicando que el solicitante no tiene permisos para hacer un pedido. Sin embargo, no tenemos una forma clara de indicar cuál es la causa de esta prohibición (¿quizás las credenciales no son correctas? ¿o quizás el cliente no tiene crédito en la empresa? ¿o puede ser que el administrador lo haya denegado expresamente?)
Para aportar más detalles sobre el problema, normalmente necesitaremos retornar en el cuerpo de la respuesta información extra usando estructuras o formatos personalizados, probablemente distintos de una aplicación a otra, y documentarlos apropiadamente para que los clientes puedan entenderlos. Y aquí es donde entra en juego el estándar “Problem details”.
ASP.NET Core 2.1 continúa profundizando en esa línea e incluye entre sus novedades el nuevo atributo
[ApiController]
, un decorador aplicable a controladores que los identifica como puntos de entrada de APIS, aplicando de forma automática una serie de convenciones bastante útiles a la hora de crear este tipo de componentes:[ApiController]
[Route("api/[controller]")]
public class ValuesController : ControllerBase
{
...
}
Fijaos que, a diferencia de ASP.NET Web API (.NET Framework), se ha optado por utilizar un atributo en lugar de emplear herencia (en aquél framework existía la clase ApiController
).
A continuación veremos qué debemos tener en cuenta a la hora de aplicar este atributo a nuestros controladores y qué convenciones son las que estaremos asumiendo al utilizarlo.
Publicado por José M. Aguilar a las 8:55 a. m.
Etiquetas: aspnetcore, aspnetcoremvc, novedades, webapi
@helper
para crear porciones de HTML reutilizables en el interior de nuestras vistas, tanto para conseguir un código más limpio como para abrazar el principio DRY (Don’t Repeat Yourself), tan frecuentemente apaleado en la capa de presentación.Recordaréis que esta directiva permitía crear “funciones” en cuyo interior podíamos escribir código Razor (mezclando marcado y C#) que podía ser invocado desde distintos puntos:
@* File: Test.cshtml *|
@Multiplication(2)
@Multiplication(3)
@helper Multiplication(int x)
{
<h2>Multiplication table of @x</h2>
<ul>
@for (var i = 1; i <= 10; i++)
{
<li>@x * @i = @(x * i)</li>
}
</ul>
}
Además de definirlos y consumirlos desde la misma vista, también era posible crear helpers globales introduciéndolos en App_Code
, aunque esta opción me gustaba menos y creo que no la utilicé jamás en aplicaciones reales.
Pues bien, por algunas extrañas razones, en ASP.NET Core la directiva @helper
no está disponible, lo que puede complicarnos un poco a la hora de portar vistas antiguas o, simplemente, cuando queramos usarla para mejorar la legibilidad de nuestro código.
IActionResult
(o un Task<IActionResult>
en caso de ser asíncronas). Este interfaz, cuya única misión es definir el método de ejecución asíncrona del resultado, ExecuteResultAsync()
, es implementado por la gran variedad de tipos de resultado ofrecidos de serie por el framework, como ViewResult
, RedirectResult
o FileResult
.Sin embargo, no es esta la única opción disponible a la hora de especificar el resultado de una acción, como veremos a continuación.
Veamos muy rápidamente en qué consiste.
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.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.
@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
Aunque a muchos nos pueda parecer raro, aún hay bastantes empresas y desarrolladores trabajando en Visual Basic .NET. En ocasiones sólo es para mantener código antiguo, pero otras veces (más de lo que puede parecer) incluso para crear nuevos sistemas aprovechando la experiencia de los desarrolladores y bases de código existente.
Si estáis en esta situación y vais a comenzar a trabajar con ASP.NET Core, mi recomendación siempre es que aprovechéis el momento de cambios para dar el salto a C#. Spoiler: no os arrepentiréis ;)Bien, respecto a la pregunta motivo del post, en teoría debería poderse, y de hecho ya difundimos por aquí hace tiempo la noticia oficial de que sería soportado, pero la verdad es que desde entonces no he vuelto a oir hablar mucho del tema, ni he visto ejemplos, ni movimiento en la comunidad ni nada parecido, lo cual me hizo dudar de la situación actual de este tema.
En este post vamos a hacer un tour rápido para ver, en la práctica, cómo está el soporte para VB.NET en la versión actual de ASP.NET Core (2.0).
JSON Web Tokens, o JWT para los amigos, es sin duda una de las fórmulas más utilizadas para autenticación en servicios o APIs HTTP gracias a su sencillez de uso y a la seguridad que aportan en determinados escenarios frente a otras opciones como las populares cookies.
En este post vamos a ver cómo implementar funcionalidades básicas de generación de tokens JWT en ASP.NET Core MVC, y cómo asegurar nuestros APIs utilizándolos para autenticar a los usuarios.
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.