Adviento C# 2020: Diario de ASP.NET

Este año volví tiempo completo al desarrollo Web, primero con .NET Core y ahora con .NET 5. Quiero compartir 3 cosas que aprendí y me parecen relevantes. Si logro mostrarte al menos una que no conocías, habré cumplido con el propósito de este post.

1. Inyectar el usuario

Seguramente tienes varios servicios que agregas a tus controladores usando inyección de dependencias y es probable que en ellos tengas métodos como:

IEnumerable<Product> GetProducts(string userId);

En donde userId lo pasas desde el controlador usando algo como:

var userId = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
var products = _service.GetProducts(userId);


Yo creé una extensión para usar:

User.GetId();

Eso me funcionó en muchos proyectos, hasta que me topé con la implementación de un IEditableDashboardStorage para un control de DevExpress. En resumen, esa interfaz la implementas para almacenar la configuración de un dashboard que es editable por el usuario… Pero no tienes acceso al contexto Web, así que no puedes obtener el userId. ¿Cómo guardas la configuración del usuario, sin tener su Id?

Pues lo inyectas agregando esta línea en el método ConfigureServices de Startup:

services.AddTransient(s => s.GetService().HttpContext.User);

Ahora puedes pasar una instancia de ClaimsPrincipal en el constructor de tus servicios.

2. Políticas antes que condicionales o roles de usuario

Usar roles está bien para manejar la autorización de forma general, pero cuando necesitas tener más control sobre lo que puede o no acceder un usuario, se quedan cortos. Allí caemos en el error de mezclar roles y lógica de negocio.

Lo ideal es crear políticas implementando IAuthorizationRequirement, creando un AuthorizationHandler y dejando que el framework se encargue del resto. Al final tendrás en tu Startup algo como:

services.AddAuthorization(options =>
{
  options.AddPolicy("IsCustomer", policy =>
    policy.Requirements.Add(new
     HasAccountRequirement(HasAccountRequirement.CUSTOMER_ACCOUNT)));

  options.AddPolicy("IsPartner", policy =>
    policy.Requirements.Add(new
      HasAccountRequirement(HasAccountRequirement.PARTNER_ACCOUNT)));
});

En este ejemplo, HasAccountRequirement accede a la base de datos -a través de servicios inyectados- y busca si el usuario tiene un perfil activo de customer o partner. A diferencia del punto anterior, el AuthorizationHandler sí accede al contexto Web. Luego puedes filtrar los recursos usando la etiqueta/atributo:

[Authorize(Policy="IsCustomer")]

U otras estrategias que ofrece el framework.

3. EF Core Power Tools

Casi toda la documentación que vas a encontrar sobre Entity Framework está orientada a Model First: generar la base de datos a partir del modelo. Ese es el escenario más común cuando estás aprendiendo, pero es muy raro en proyectos medianos o grandes.

EF Core Power Tools tiene una opción de ingeniería inversa muy buena, que te ayuda en el escenario Database First. La única recomendación que tengo es que esos elementos generados automáticamente los dejes en un proyecto de librería de clases -Classlib- separado, de forma que puedas ajustarlo conforme el proyecto avanza.

Comentarios

Entradas más populares de este blog

Ampliar el sistema de archivos de una máquina virtual Ubuntu

Historia de una App: un mes en la Tienda Windows y en Google Play