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!
martes, 31 de mayo de 2022
.NET

A veces, desde aplicaciones .NET de consola, escritorio, o incluso ASP.NET Core, puede resultar interesante conectarse con una hoja de Google Sheets para añadir filas de datos.

Hay varias formas de conseguirlo, pero aquí vamos a ver la que creo que es la más sencilla, pues permite evitar parte del engorroso workflow de OAuth y, lo que es mejor, podemos usarla sin necesitar credenciales de usuario desde, por ejemplo, un servidor o un proceso desasistido.

Ojo: las APIs de Google que vamos a ver son gratuitas, pero tienen limitaciones de uso que debéis conocer antes de utilizarlas.

A grandes rasgos, el proceso consta de los siguientes pasos, que seguiremos a lo largo del post:

  • Configuración del proyecto y credenciales en Google Developer Console.
  • Creación del documento Google Sheet en el que añadiremos las filas.
  • Consumo de las APIs de Google para añadir datos.

¡A por ello!

1. Configuración del proyecto en Google Developer Console

1.1. Creación del proyecto y habilitación de la API

Para usar muchas de las APIs de Google necesitamos entrar en la consola para desarrolladores y crear un proyecto o, si nos interesa, seleccionar uno ya existente, usando el menú situado en la barra superior.

En nuestro caso, vamos a crear un proyecto al que llamaremos "Sheets API example":

Pantalla de creación de proyecto en Google Cloud Platform

Tras aceptar y esperar unos segundos lo tendremos creado. El siguiente paso es añadir a este proyecto la API de Google Sheets, para lo que acudimos en el menú lateral a la opción "API y servicios > API y servicios habilitados" y pulsamos el botón superior "Habilitar API y servicios" para añadir la nueva API.

Usando el buscador, debemos localizar la API de Google Sheets:

Resultados de la búsqueda de la API de Google Sheets

Una vez localizada, al pulsar sobre la API accederemos a una página en la que encontraremos información general, documentación y condiciones generales de uso. Pulsando el botón "Habilitar", ya podremos utilizarla desde nuestro proyecto:

Habilitar la API de Google Sheets

1.2. Creación de credenciales

A continuación vamos a crear el conjunto de credenciales que nos permitirá desde nuestro código .NET utilizar la API de Google Sheets.

Una vez hemos pulsado el botón para habilitar la API, aparecerá en pantalla una página resumen del servicio en la que veremos opciones para crear las credenciales, tanto en un aviso en la parte superior como en la pestaña "Credenciales" algo más abajo:

Página de la API de Google Sheets habilitada, con opciones para crear credenciales

El primero paso será especificar el tipo de credenciales que vamos a crear, para lo que tendremos que cumplimentar un sencillo formulario con la siguiente información:

  • Seleccionar una API: Google Sheets API (aparecerá preseleccionado)
  • ¿A qué datos quieres acceder? Seleccionaremos "Datos de aplicaciones".
  • ¿Piensas usar esta API con Compute Engine, Kubernetes Engine, App Engine o Cloud Functions? Seleccionamos la opción "No".

Pantalla de creación de credenciales

Pulsando el botón "Siguiente", pasaremos ahora a crear el service account, un tipo especial de cuenta diseñada para ser utilizada por usuarios no humanos (p.e. aplicaciones) que se deben ser autorizadas para acceder a datos de las APIs de Google.

En su creación, debemos elegir un nombre para la cuenta; en nuestro caso usaremos por ejemplo "Sheets API Example account". Esto generará de forma automática un ID y una dirección de email, que más adelante usaremos para compartir la Google Sheet con esta cuenta:

Creación de la cuenta de servicio

Pulsando el botón "Crear y continuar" y luego el botón "Listo" tendremos ya la cuenta de servicio creada.

Por último, vamos ahora a generar las claves de acceso, necesarias para que nuestra aplicación .NET acceda a la API usando la cuenta de servicio creada anteriormente. Para ello, acudimos a la sección de credenciales de la API de Sheets, donde pulsamos, en la zona inferior, sobre la dirección de correo electrónico de la cuenta de servicio que acabamos de crear:

Cuenta de servicio recién creada

Esto nos llevará a una nueva página con propiedades de la cuenta, en la que hemos de abrir la pestaña "Claves", y luego usar el botón "Agregar clave" para crear una nueva clave:

Sección 'claves' de la cuenta de servicio

La aplicación nos preguntará el formato en el que deseamos obtener la clave, y elegiremos JSON. Tras ello, se descargará a nuestro ordenador un archivo .json con las claves de acceso. Es importante guardar este archivo en un lugar seguro, pues lo usaremos más adelante para comunicarnos con las hojas de cálculo.

2. Creación del documento Google Sheet en el que añadiremos las filas

Esta parte tiene poco misterio. En primer lugar, simplemente tendremos que crear un documento Google Sheets, que será sobre el que añadiremos filas desde código en el siguiente apartado.

Lo único adicional que hay que hacer es asegurar el acceso de la cuenta de servicio a la hoja de cálculo, compartiendo el documento con ella. Para conseguirlo, debemos usar el botón "Compartir" que encontraremos en la esquina superior derecha de Sheets y, en el cuadro de diálogo mostrado a continuación, introducir el email de la cuenta de servicio, que en nuestro caso es sheets-api-example-account@sheets-api-example-346615.iam.gserviceaccount.com:

Compartir documento con cuenta de servicio

Ojo, es importante que al compartir se especifiquen los permisos de "Editor", para que la cuenta pueda modificar datos.

Otra opción sería usar la opción "Obtener un enlace", indicando que cualquiera que tenga el enlace podrá editar el documento. Obviamente es menos seguro, pero hay escenarios en los que podría valernos.

Por último, debemos fijarnos en la URL del documento, que más o menos tiene una pinta como la siguiente:

https://docs.google.com/spreadsheets/d/1Yp2d9smO0IjfiLmtu_djzfU4qQ4hNonkasvlb3SOEbQ/edit#gid=0

Pues bien, de aquí debemos extraer la secuencia central aleatoria de letras y números, pues es el identificador del documento. Lo usaremos desde el código .NET para indicar qué documento queremos editar, por lo que conviene tenerlo a mano.

Y eso es todo, ¡ya podemos empezar a programar!

3. Consumir las APIs de Google para añadir datos

Vamos a implementar nuestro ejemplo en una aplicación de consola .NET 6, pero en realidad podría .NET Framework, o incluso una aplicación web MVC, Blazor o de cualquier otro tipo.

En todos los casos, lo primero que debemos hacer es instalar en el proyecto el paquete NuGet Google.Apis.Sheets.v4.

A continuación, vamos a crear un servicio, al que llamaremos SheetsClient, que podríamos usar como Singleton en nuestras aplicaciones.

Su esqueleto será el siguiente. Como podéis observar, la idea es que le pasemos en el constructor el identificador del documento a editar (que previamente hemos compartido con la cuenta de servicio) y la ruta hacia el archivo de claves (que, recordaréis, descargamos desde la consola de Google).

public class SheetsClient
{
    private readonly string _sheetId;
    private readonly string _keysFilePath;
    private SheetsService? _sheetsService;

    public SheetsClient(string sheetId, string keysFilePath)
    {
        ArgumentNullException.ThrowIfNull(sheetId);
        ArgumentNullException.ThrowIfNull(keysFilePath);
        _sheetId = sheetId;
        _keysFilePath = keysFilePath;
    }

    // Añade una fila de datos a la hoja por defecto
    public Task AddRowAsync(params object[] values) 
        => AddRowToSheetAsync(string.Empty, values);

    // Añade una fila de datos a la hoja especificada
    public async Task AddRowToSheetAsync(string sheetName, object[] values)
    {
        ArgumentNullException.ThrowIfNull(values);
        await EnsureInitializedAsync();
        await AddRowToSheetInternalAsync(sheetName, values);
    }

    private async Task EnsureInitializedAsync()
    {
        // Nos aseguramos de que el cliente de la API
        // está inicializado y almacenado en el campo _sheetsService
    }

    private async Task AddRowToSheetInternalAsync(string sheetName, object[] values)
    {
        // Añade una fila de datos a la hoja indicada, a través
        // del cliente _sheetsService previamente inicializado
    }
}

Vamos ahora con la implementación de los detalles. El método EnsureInitializedAsync() crea unas credenciales desde el archivo de claves suministrado e inicializa el servicio SheetsService de Google, dejándolo almacenado en _sheetsService, para usos posteriores. Su código es muy simple:

private async Task EnsureInitializedAsync()
{
    if (_sheetsService != null)
        return;

    using var stream = new FileStream(_keysFilePath, FileMode.Open, FileAccess.Read);
    var credential = (await GoogleCredential.FromStreamAsync(stream, CancellationToken.None))
        .CreateScoped(new[] { SheetsService.Scope.Spreadsheets });
    _sheetsService = new SheetsService(new BaseClientService.Initializer()
    {
        HttpClientInitializer = credential
    });
}

En este caso estamos usando un FileStream para acceder al contenido del archivo, pero podríamos usar cualquier tipo de stream de entrada para obtener las claves desde otros orígenes más seguros, como key vaults, datos de entorno o similares.

Por otra parte, el método AddRowToSheetInternalAsync() es el encargado de añadir efectivamente las filas a la hoja especificada (o a la hoja por defecto si no se indica ninguna). Para ello, se crea una petición de inserción de un conjunto de valores sobre un rango de datos, ejecutándose finalmente la orden:

private async Task AddRowToSheetInternalAsync(string sheetName, object[] values)
{
    if (_sheetsService == null)
        throw new InvalidOperationException();

    var range = string.IsNullOrEmpty(sheetName) ? $"A:A" : $"{sheetName}!A:A";
    var valueRange = new ValueRange
    {
        Values = new List<IList<object>> { values }
    };
    var appendRequest = _sheetsService.Spreadsheets.Values.Append(valueRange, _sheetId, range);
    appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
    await appendRequest.ExecuteAsync();
}

Una vez tenemos esta clase, ya podemos implementar código que la consuma. Si lo hacemos desde una aplicación de consola, podría ser algo tan simple como esto:

var sheetsClient = new SheetsClient("1Yp2d9smO0IjfiLmtu_djzfU4qQ4hNonkasvlb3SOEbQ", "sheets-api-example-keys.json");
await sheetsClient.AddRowAsync("Juan", "López", 23, "juan@server.com");
await sheetsClient.AddRowAsync("Luis", "Rodríguez", 45, "luis@server.com", "+34 673273623");
Console.ReadLine();

Recordad que:

  • El primer parámetro es el identificador del documento que queremos editar. Podíamos extraerlo de la URL del documento cuando lo editamos con el navegador.
  • El segundo parámetro es la ruta al archivo de claves. Para hacerlo sencillo, podéis simplemente incluir el archivo descargado en la raíz del proyecto .NET y, en sus propiedades, establecer la opción "Copiar al directorio de salida" como "Copiar si es más reciente".
  • Y, por último, recordad que introducir esas claves directamente en el código suele ser una mala idea, mucho mejor si optáis por valores de configuración, user secrets o similar.

Y el resultado en ejecución es el siguiente. Podéis ver que la primera vez tarda algo más porque tiene que realizarse la autenticación, pero después es bastante inmediato:

Aplicación .NET en ejecución, añadiendo filas a la hoja de cálculo

Publicado en Variable not found.

1 comentario:

Eber H dijo...

Muchas gracias por compartir. Con la ayuda de este articulo y un poco de otro, lo pude desarrollar para .net Framework 4.6. Saludos!