Eseguire processi di sistema in una Web API C#

Alcuni contesti applicativi richiedono l’esecuzione di processi di sistema o console application per assolvere alle funzioni richieste dall’utente, e talvolta è necessario impacchettare tali funzionalità all’interno di una REST API.

Ad esempio potremmo essere di fronte ad un revamping di funzionalità fornite da console application o da software on-premise, o potrebbero esserci contesti operativi in cui si vogliono utilizzare programmi di terze parti distribuite direttamente in formato eseguibile.

Qualsiasi sia il vostro contesto operativo, è molto semplice inglobare tali software all’interno di una API, utilizzando le classi messe a disposizione dal namespace System.Diagnostics e redirigendo i flussi standard di input, output ed errore.

L’esempio che ho pubblicato su Github è ispirato da un post di qualche settimana fa di Marco Giannini sul suo blog Marco’s Box, che spiegava come utilizzare fortune per stampare nella CLI Linux delle piccole perle di saggezza, personalizzabile anche con dizionari custom.

I punti di interesse di questo progetto di esempio sono due: come utilizzare un processo di sistema (in questo caso fortune con dizionari personalizzati in un container Debian) e come aggiungere applicazioni nel container Docker che ospita l’applicativo.

Eseguire un processo di sistema all’interno di una Web API C#

Esattamente come nei contesti più classici (console application e applicazioni desktop), per eseguire un processo di sistema è sufficiente istanziare un nuovo oggetto System.Diagnostics.ProcessStartInfo in cui inserire tutti i parametri del processo da lanciare, e su cui configurare il redirect dei flussi standard di input, output ed errore:

// https://github.com/Defkon1/pikaquote/blob/main/Pikaquote/Pikaquote.Core/Services/QuotesService.cs

try
{
    var psi = new ProcessStartInfo()
    {
        FileName = "/usr/games/fortune",
        Arguments = argument,
        UseShellExecute = false,
        RedirectStandardInput = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true
    };

    using (var process = Process.Start(psi))
    {
        errors = await process.StandardError.ReadToEndAsync();

        results = await process.StandardOutput.ReadToEndAsync();
    }

    return new QuoteDto()
    {
        CreatedAt = DateTime.UtcNow,
        Message = results,
        UsedDictionary = supportedDictionary?.Name ?? _supportedDictionaries.First(d => string.IsNullOrEmpty(d.Slug)).Name
    };
}
catch (System.ComponentModel.Win32Exception exception)
{
    _logger.LogError(exception, message: errors);
}
catch (Exception exception)
{
    _logger.LogError(exception, "Unknown error");
}

In questo caso il processo di fortune è installato in /usr/games/fortune e prende come argomento l’eventuale dizionario personalizzato da utilizzare; il risultato (la perla di saggezza) viene recuperata redirigendo lo standard output, mentre gli eventuali errori vengono catturati direttamente dallo standard error.

Il risultato viene impacchettato in DTO di risposta e restituito al controller. Semplice e veloce.

Aggiungere applicazioni di sistema nel container Docker Web API

L’immagine di default utilizzata da ASP.NET Core (nel caso mostrato basata su Debian) non include fortune out-of-the-box, per cui ho modificato il DockerFile per fare nell’ordine:

  • aggiungere contrib all’elenco sorgenti
  • installare i pacchett fortune-mod, wget e dos2unix
  • scaricare da PasteBin i dizionari personalizzati citati nell’articolo di Marco Giannini con wget
  • convertire i line break dei file scaricati da formato DOS a formato Unix con dos2unix
  • trasformare i file di testo scaricati in dizionari per fortune con strfile (incluso nel pacchetto fortune-mod)

Tutte queste operazioni sono facilmente eseguite mediante comandi RUN direttamente nel DockerFile sull’immagine base:

RUN sed -i'.bak' 's/$/ contrib/' /etc/apt/sources.list
RUN apt-get update && apt-get install -y fortune-mod
RUN apt-get install -y wget
RUN apt-get install -y dos2unix
RUN wget --no-check-certificate https://pastebin.com/raw/p296KDcE -O /usr/games/fortune-devops
RUN dos2unix /usr/games/fortune-devops
RUN strfile /usr/games/fortune-devops

In questo modo il container risulta essere già pronto con tutti gli strumenti necessari all’invocazione del processo dall’interno dell’API.

Il codice sorgente completo dell’esempio è disponibile su Github con licenza MIT: https://github.com/Defkon1/pikaquote/ (la stellina è sempre gradita)

Have fun!

Eseguire processi di sistema in una Web API C#
Tag: