.NET Web API output cache con Azure Redis Cache

En esta publicación veremos como conectar una Web API de .NET a Azure Redis Cache. El objetivo es decorar nuestros métodos web para que las respuestas que son concurrentes se obtengan del servicio de cache de Azure Redis Cache y no tener que ir siempre a una base de datos.

La manera en la que vamos a implementar esta solución es codificando un "middleware" en nuestra Web API, interceptando únicamente los "request" de los métodos que nosotros deseamos implementar cache.

Una vez que interceptamos los request, verificamos en Redis mediante un Key, si no existe registro, dejamos que nuestro método haga su operación normal, despues, al final del proceso, obtenemos el "response" y lo guardamos en Redis con alguna Key que identifique el registro para luego obtenerlo de cache cuando se requiera.

Pre-requisitos
    • Visual Studio, obtenerlo aquí.
    • Tener una suscripción de Azure

    Descargar código aquí:

    Crear proyecto Web API

    En Visual Studio damos  clic en "Create new project" y elegimos:
    Templates > Visual C# > Web > ASP.NET Web Application (.NET Framework) > Empty (Seleccionando la opción de Web API)



    Una vez creado el proyecto, agregamos un controlador y creamos dos métodos web para probar nuestra API; un método Get y un método Post.




    Ahora podemos probar nuestra API. En lo personal me gusta utilizar un RestClient llamado Postman, lo puedes descargar de la tienda de Google, es gratis:
    https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop

    Iniciamos la Web API en debug desde Visual Studio y probamos desde nuestro Restclient:


    Bien! Ya tenemos funcionando la Web API, ahora es hora de crear el servicio de Azure Redis Cache en nuestra suscripción de Azure.

    Crear servicio de Redis Caches
    Ingresamos al portal de Azure y tecleamos en el buscador: "Redis Cache", luego agregamos un servidor de Redis y capturamos los datos del nuevo servidor:




    Por motivos de demostración, elegiremos un servidor standard, recordemos que si queremos configurar un cluster de Redis o bien realizar configuraciones mas avanzadas, necesitamos adquirir un servidor "Premium", para mas información puedes visitar el siguiente enlace:
    https://docs.microsoft.com/en-us/azure/redis-cache/cache-premium-tier-intro

    Crear proyecto Class Library para hacer caching en Azure Redis Cache
    Mientras se crea el servicio de cache de Redis, vamos de vuelta a la Web API para implementar nuestra capa de cache.

    Damos clic derecho a la solución de "WebAPI.Output.RedisCache" y agregamos un nuevo proyecto de tipo "Class Library"

    Después creamos una nueva clase, la cual se va a encargar de exponer un nuevo atributo al cual llamaremos "CacheOutput". Como expondremos un atributo, nombraremos a nuestra clase por convención: "CacheOutputAttribute.cs" y luego heredamos de "ActionFilterAttribute" para poder interceptar los request y response procesados en el servidor.

    Nota: Para poder heredar de la clase "ActionFilterAttribute" necesitamos tener acceso a la librería "System.Web.Http", la cual podemos descargar desde NuGet con el nombre de "Microsoft.AspNet.WebApi.Client y Microsoft.AspNet.WebApi.Core" los dos paquetes son necesarios.


    Ahora ya podemos decorar nuestros métodos de la Web API de la siguiente manera:

    Como podrán darse cuenta, aun no hacemos uso del servicio de Azure Redis Cache, para eso, me gustaría empezar por crear una "interface" de los métodos que vamos a utilizar para agregar y obtener datos de Redis.

    Primero que nada, bajaremos el NuGet de la librería "StackExchange.Redis" la cual nos facilitará la interacción con el servidor de Redis.

    Damos clic derecho en las referencias de "RedisCacheProvider" y agregamos el NuGet "StackExchange.Redis"

    Creamos la interface con los métodos que vamos a requerir para hacer el caching de las salidas de nuestra Web API:


    Nombré a la interface "IRedisCacheApi" ya que es la que expondrá  los métodos de interacción con Redis a nuestra Web API.

    Método "Add":
    Inserta registro en Redis en formato "key/value" con tiempo de caducidad.
    Método "Get":
    Obtiene registro mediante su "key".
    Método "SearchKeys":
    Busca en Redis por la "key" proporcionada, regresa "true" si la llave se encuentra viva en Redis, de lo contrario regresa "false".
    Método "Remove":
    Elimina registro de Redis mediante su "key"

    Ahora es tiempo de crear la implementación de cada uno de los métodos. Creamos una nueva clase y luego heredamos de "IRedisCacheApi"


    Para poder hacer conexiones a nuestro servicio de Redis, vamos agregar una propiedad estatica, la cual va a exponer la conexión a la base de datos de Redis con la cadena de conexión que le proporcionemos.

    La cadena de conexión la podemos obtener en el portal de Azure, accediendo al servicio de Redis que anteriormente creamos.

    Vamos al portal de Azure y vamos a la sección "Keys":



    Hacemos una copia del "Primary connection string" y lo ingresamos a nuestra variable estática de la clase RedisCacheApi:

    Es tiempo de codificar!!
    Vamos a implementar la lógica  de cada uno de los métodos.

    Método Add:
    public void Add(string key, string value, int expires = 30)
    {
       if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
       {
          throw new ArgumentException();
       }
       _redisCache.StringSet(key, value, TimeSpan.FromSeconds(expires));
    }
    Método Get:
    public string Get(string key)
    {
       if (string.IsNullOrEmpty(key))
       {
          throw new ArgumentException();
       }
       return _redisCache.StringGet(key);
    }
    Método Remove:
    public void Remove(string key)
    {
       if (string.IsNullOrEmpty(key))
       {
          throw new ArgumentException();
       }
       _redisCache.KeyDelete(key);
    }

    Método SearchKeys:
    public bool Get(string key)
    {
       if (string.IsNullOrEmpty(key))
       {
          throw new ArgumentException();
       }
       return _redisCache.KeyExists(key);
    }

    Y eso es todo lo que necesitamos para lograr la interacción con Azure Redis Cache.

    Codificar CacheOutputAttribute para consumir RedisCacheApi

    Ahora es tiempo de volver al filtro "CacheOutputAttribute" que creamos anteriormente y sobre escribimos el método: "OnActionExecuting" para interceptar los "Request" entrantes y sobre escribimos el método "OnActionExecuted" para interceptar los "Response" de salida de la API.

    También aprovechamos para agregar la referencia a nuestra API de Redis que acabamos de crear y una propiedad para poder pasar como argumento el tiempo de vida de nuestro response en cache del lado del servidor:
    Vamos a crear una implementación muy sencilla, sin mucha lógica para demostrar el funcionamiento de los filtros, guardado y consultando a Redis.

    OnActionExecuting:
    El método OnActionExecuting obtiene el "Request" del cliente, este método se ejecuta antes de que la petición llegue a la API. Lo que hacemos aquí es obtener la ruta de la api y los parámetros de entrada, para las peticiones "GET" se obtiene el QueryString, para las peticiones "POST" se obtiene el "Body". El motivo de por el cual obtenemos estos parámetros es para guardarlos como "Key" en Redis.

    Después verificamos si la "Key" existe en Redis, si no existe, aborta la ejecución, de lo contrario, el método irá a Redis para obtener el valor de esa llave. Una vez que obtenga el valor de Redis, creamos un "Response" en JSON directamente, así ya no ejecutaremos nada en nuestra API.


    OnActionExecuted:
    El método OnActionExecuted obtiene los response de nuestros métodos de la API. Este método se ejecuta al finalizar la ejecución de código de la API.
    Aquí lo que hacemos es crear la "Key" con la misma lógica con la que la guardamos anteriormente (ruta API+ parámetros), y luego guardamos en Redis el valor de "Response" de nuestros métodos a los cuales le especificamos que hicieran caching.
    GetParametersFromRequest:
    Método para obtener parámetros de los Request.
    ToDictionary
    Convierte un objeto "NameValueCollection" a un diccionario.

    Con esta simple implementación podemos guardar y obtener la información de nuestros response en cache.

    Hora de probar nuestra API
    Al final nuestra API luce de la siguiente manera:
    Vamos a correr nuestra aplicación en Debug y luego abrimos Postman para probar nuestros métodos implementando cache.

    El resultado esperado es que la primera petición vas obtener el response normal de tu API y si vuelves hacer una petición en un intervalo de 40 segundos, obtendrás el mismo response pero con el sufijo "FROM CACHE".

    Ahora verificamos en Redis si el registro realmente existe en cache:
    (usé la herramienta de Redis Desktop Manager, la puedes descargar del siguiente enlace: https://redisdesktop.com/)
    Vemos que el registró si existe, ahora vamos a probar el método POST:


    Genial!! vemos que nuestra implementación de output cache sí funciona!

    Por ultimo esperamos a que pasen 40 segundos (ya que en la API configuramos que el "ServerExpirationTime" es de 40 segundos) y luego verificamos Redis de nuevo para ver que los registros hayan expirado.

    Con estos sencillos pasos ya podemos disfrutar de las ventajas de Redis Caches en Azure.

    Descargar código aquí:

    Saludos a todos!

    Comments

    Popular posts from this blog

    Configurar y desplegar una Web API en Azure App Service Environment

    Patrones de diseño para aplicaciones de alta disponibilidad en Azure - Resilient Applications (Parte I: Retry Policy)

    Integrar Web API de .NET en proyecto de Xamarin.Forms utilizando Visual Studio y Azure API App

    Conectar .NET Web API con Azure API Management

    Publicar resultados de una consulta de Azure SQL Database a Azure Service Bus con Logic App