Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
Mostrando entradas con la etiqueta aspnetcoremvc. Mostrar todas las entradas
Mostrando entradas con la etiqueta aspnetcoremvc. Mostrar todas las entradas
martes, 11 de diciembre de 2018
La pasada semana, en el contexto del evento Microsoft Connect(); 2018, se lanzaron las últimas versiones de la familia de productos "Core": .NET Core 2.2, ASP.NET Core 2.2 y Entity Framework Core 2.2.

En todos los casos son revisiones pequeñas y que no rompen nada de lo anterior, pero en cada uno de estos productos se han introducido mejoras que vale la pena conocer, por lo que, antes que nada, os recomiendo que echéis un vistazo a los artículos anteriores, que son los anuncios oficiales.

En este post vamos a ver rápidamente las novedades más destacables de ASP.NET Core 2.2.
martes, 27 de noviembre de 2018
Desarrolladores Como sabemos, la inyección de dependencias está grabada a fuego en los genes de ASP.NET Core. La mayoría de sus componentes la usan internamente para obtener acceso a otros componentes que necesitan para cumplir sus funciones y, además, es una práctica recomendada en la parte que nos toca a nosotros como desarrolladores de aplicaciones.

Por ello, en ASP.NET Core MVC, lo habitual es que implementemos nuestros controladores atendiendo a este principio, y para ello utilicemos la técnica de inyección de dependencias en el constructor:
public class InvoiceController: Controller
{
    private readonly IInvoiceServices _invoiceServices;
    private readonly IMapper _mapper;
    private readonly ILogger<InvoiceController> _logger;

    public InvoiceController(
        IInvoiceServices invoiceServices, 
        ILogger<InvoiceController> logger,
        IMapper mapper)
    {
        _invoiceServices = invoiceServices;
        _logger = logger;
        _mapper = mapper;
    }
    ...
}
Nota: aunque en este post estemos hablando de controladores ASP.NET Core MVC, las ideas que vamos a comentar aquí son también aplicables a ASP.NET MVC "clásico" e incluso a otro tipo de frameworks que comparten los mismos conceptos.
martes, 20 de noviembre de 2018
ASP.NET Core MVC Seguramente habréis notado que en proyectos ASP.NET Core 2.1 la compilación de las vistas funciona de forma ligeramente diferente a como se hacía en versiones anteriores. Hasta ahora las vistas sólo eran compiladas al publicar el proyecto pero, a partir de esta versión, este proceso se realizará antes, durante la compilación.

En este artículo vamos a ver cómo aprovechar las ventajas de la precompilación, y al mismo tiempo mantener la flexibilidad que nos ofrece la compilación en tiempo de ejecución que tradicionalmente hemos disfrutado en proyectos ASP.NET y ASP.NET Core.

martes, 13 de noviembre de 2018
ASP.NET Core MVCEn el post anterior veíamos cómo personalizar los mensajes de error generados durante el proceso de binding, y cómo aprovechar las posibilidades que nos brinda el framework para introducir textos localizados en esos puntos.

Hoy seguiremos profundizando en este tema, pero esta vez nos centraremos en modificar los textos por defecto de las anotaciones de datos y hacer que simplemente decorando una propiedad con un atributo como Required consigamos obtener mensajes de error localizados y a nuestro gusto, sin necesidad de establecer el ErrorMessage de forma explícita y, en algunos casos, ni siquiera tener que indicar dicho atributo.
martes, 6 de noviembre de 2018
ASP.NET Core MVCEn el framework ASP.NET Core MVC es muy sencillo establecer los mensajes de error de validación de campos utilizando propiedades de las data annotations, como en el siguiente ejemplo:
namespace LocalizationDemo.ViewModels
{
    public class PersonViewModel
    {
        [Required(ErrorMessage ="The name is required")]
        public string Name { get; set; }
    }
}
Incluso es bastante fácil hacer que este texto aparezca traducido atendiendo a la cultura del hilo actual. Para ello, simplemente debemos configurar los servicios de localización apropiadamente, e indicar en la propiedad ErrorMessage la clave del mensaje de error en el archivo RESX asociado a la clase:
// En Startup.cs:
public void ConfigureServices()
{
    ...
    services.AddLocalization(opt=>opt.ResourcesPath="Resources");
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
        .AddViewLocalization()
        .AddDataAnnotationsLocalization(); // Esta línea añade la localización 
                                           // de data annotations 
    }

// En el view model:
namespace LocalizationDemo.ViewModels
{
    public class PersonViewModel
    {
        // El mensaje de error está definido 
        // en Resources/ViewModels.PersonViewModel.resx
        [Required(ErrorMessage ="NameIsRequired")] 
        public string Name { get; set; }
    }
}
Esto es así de fácil para las validaciones que declaramos de forma explícita en nuestro código, mediante atributos de anotaciones de datos. Sin embargo, existen otro tipo de validaciones que se realizan de forma implícita (por ejemplo, cuando en un campo entero introducimos un valor no numérico) y cuyos mensajes de error no son tan sencillos de establecer.
martes, 23 de octubre de 2018
ASP.NET Core MVC Desde que comenzamos a programar con ASP.NET MVC tenemos interiorizado que si en nuestras acciones vemos código repetido, suele ser un candidato a ser implementado en forma de filtro. Así, durante años hemos implementado en forma de action filters asuntos transversales de nuestras aplicaciones, como la seguridad, logging, caching y otros aspectos.

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.
martes, 25 de septiembre de 2018
ASP.NET Core MVC Hace unos días, un alumno del curso online de ASP.NET Core en CampusMVP (por cierto, desde hace unas semanas actualizado a la versión 2.1) me planteaba una cuestión que quizás podría interesar a alguien más, por lo que vamos a comentarla por aquí.

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 :)
martes, 3 de julio de 2018
ASP.NET Core MVC Uno de los objetivos de ASP.NET Core ha sido siempre servir como infraestructura para la creación de servicios HTTP o APIs web, por lo que ya desde las primeras versiones se incluían funcionalidades específicamente diseñadas para facilitar esta tarea.

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.
martes, 19 de junio de 2018
ASP.NET Core MVC Como sabéis, por defecto las acciones MVC y Web API en ASP.NET Core retornan un 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.
martes, 27 de febrero de 2018
ASP.NET Core MVC Cuando en nuestras aplicaciones o APIs web usamos autenticación basada en tokens, ya sean JWT o simples cookies, normalmente aprovechamos para introducir en ellos información extendida sobre el usuario que realiza la petición, básicamente para tenerla más a mano a la hora de procesarla. Por ejemplo, suele ser habitual guardar su nombre, el identificador en base de datos, roles de seguridad y cosas de este estilo.

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.
martes, 20 de febrero de 2018
ASP.NET Core MVC En ocasiones puede resultar interesante renderizar una vista de ASP.NET Core MVC a una cadena de caracteres. Probablemente el escenario clásico de uso sea utilizar este tipo de vistas como motores de plantillas, por ejemplo, a la hora de generar el cuerpo de un email, pero puede haber muchos otros casos en los que nos vendría bien saber hacerlo.

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.
martes, 16 de enero de 2018
ASP.NET CoreYa, sé que puede parecer una pregunta extraña a estas alturas, pero el caso es que un alumno del curso de ASP.NET Core en CampusMVP me la envió hace unos días y me ha parecido interesante comentar el asunto por aquí, por si puede interesar a alguien más.

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).
martes, 19 de diciembre de 2017
ASP.NET Core MVC
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.
martes, 21 de noviembre de 2017
ASP.NET Core MVC Hace unos días veíamos lo sencillo que resultaba personalizar las plantillas de generación de código para proyectos MVC desde Visual Studio, e incluso cómo crear plantillas específicas para proyectos.

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.
martes, 7 de noviembre de 2017
ASP.NET Core MVCTrabajando con ASP.NET Core MVC desde Visual Studio, seguro que alguna vez habéis creado vistas, controladores u otros elementos utilizando las plantillas disponibles en el IDE, seleccionando la opción del menú Add > New Scaffolded Item, por lo que os habréis encontrado con un cuadro de diálogo como el siguiente:

Cuadro de diálogo de adición de scaffolded item

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:

Cuadro de diálogo de creación de vista MVC

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 :)
martes, 24 de octubre de 2017
ASP.NET Core MVC Pues os voy a contar una historia que me ocurrió más de una vez al comenzar a utilizar ASP.NET Core 2. Y como supongo que no seré el único, creo que puede ser interesante compartirla por aquí para evitar pérdidas de tiempo innecesarias al resto de la humanidad ;)

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í?
martes, 17 de octubre de 2017
Puedes encontrar una versión actualizada de este post, que describe una forma mejor de hacerlo:
https://www.variablenotfound.com/2018/05/implementando-mas-facilmente-background.html

ASP.NET Core 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.
martes, 10 de octubre de 2017
ASP.NET CoreImaginad una aplicación ASP.NET Core MVC en la que insertamos un enlace o botón que dirige el navegador hacia la siguiente acción, que realiza una operación compleja y retorna un resultado:
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 seres poseídos por una entidad demoníaca impacientes, lo normal es que se lancen en un feroz ataque contra el sistema, refrescando la página o pulsando repetidamente el botón de envío como si no hubiera un mañana. Todos hemos escuchado y sufrido en nuestras carnes una agresión de este tipo: “espera, esto parece que no va: click-click-click-click-click-click-click…

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?
martes, 3 de octubre de 2017
ASP.NET CoreAl dar el salto a una nueva tecnología como ASP.NET Core, en muchas ocasiones nos encontramos con el problema de no saber cómo hacer cosas que con las tecnologías tradicionales teníamos completamente controladas. Ya hemos comentado por aquí varios casos (como el Application_Start(), los custom errors o las variables de sesión) y hemos visto cómo se mapean estas funciones al nuevo framework, pero hay muchos más.

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.jpg
Pues 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.
martes, 26 de septiembre de 2017
ASP.NET CoreEn las versiones clásicas de ASP.NET, el archivo Global.asax proporcionaba vías para implementar lógica personalizada cuando la aplicación arrancaba y era detenida, lo que podía resultar bastante útil, por ejemplo, para registrar estos eventos en un log, precargar cachés, inicializar bases de datos, “engancharnos” a servicios externos, etc.

Por ejemplo, en el siguiente código vemos cómo podíamos aprovechar los eventos Application_Start() y Application_End() para guardar un registro básico de estos sucesos:
public class MvcApplication : System.Web.HttpApplication
{
    private static string _logFile;
    protected void Application_Start()
    {
        ...
        _logFile = Server.MapPath("log.txt");
        File.AppendAllText(_logFile, DateTime.Now + ": Starting\n");
    }

    protected void Application_End()
    {
        File.AppendAllText(_logFile, DateTime.Now + ": Stopping\n");
    }
}
Sabemos que en ASP.NET Core no existe Global.asax, por lo esta fórmula ya no está disponible. Sin embargo, el nuevo framework ofrece una alternativa bastante razonable mediante el interfaz IApplicationLifetime, proporcionando, entre otras cosas, vías para suscribirnos a eventos relacionados con el ciclo de vida de una aplicación.