Azure Functions avec F#
Tags: azurefunctions, fsharp, devfr
Published: 2021-04-07 20:44:07

---
title: Azure Functions avec F#
published: true
description: Dans ce premier article, je vous enseigne comment utiliser F# pour créer des Azure Fonctions.
tags: AzureFunctions, Fsharp, DevFR
---


Bonjour à tous dans un nouvel article sur la plateforme `Dev.to`. Dans cette occasionne, nous apprendrons brièvement comment faire une Azure Fonction en utilisant F# pour la développer.

Dans la première version d'Azure Fonctions, nous pouvions faire facilement une fonction en utilisant un script **fsx**, tandis que dans la dernière version d'Azure Fonctions nous devons précompiler et uploader nos scripts **fs**

![L'information que Microsoft Developeurs nos donne](https://thepracticaldev.s3.amazonaws.com/i/gj9akzjj9fbgjiasqrug.PNG)

Après de voir la documentation, j'ai découvert qu'il n'y a pas un bon exemple pour faire une fonction azure avec F#, donc la Template ne fonctionne pas alors j'ai déçu de faire ce petit tutoriel.

#### Requis

On a besoin d'installer le suivant logiciel: 

* [Visual Studio Code](https://code.visualstudio.com/) ou votre éditeur du texte préféré.
* Installez [F#](https://fsharp.org/) et [NodeJS](https://nodejs.org/en/) avec le package [Azure Functions Core Tools](https://www.npmjs.com/package/azure-functions-core-tools)
* Installez le [.NET Core CLI](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50)
* Optionnellement vous pourriez créer un compte [Microsoft Azure](https://azure.microsoft.com/fr-fr/) en cas de vouloir publier votre fonction.

#### Temp de code 1..2..3

##### Pas 1
D'abord on va vérifier notre installation alors on va ouvrir un terminal unix et écrire les commandes :

```bash
darkhero> func --version # 3.0.3388
darkhero> func templates list # on verra qu'il n'y a pas F# templates
darkhero> dotnet --version # 5.0.201
```

Malgré la faute d'une Template F#, on peut créer une fonctionne en exécutant le commande :

```bash
# Ce commande va créer notre fonctionne base dont F# sera la langue pour programmer
darkhero> dotnet new classlib --language F# --name HelloDevTo
darkhero> cd HelloDevTo && ls
HelloDevTo.fsproj  Library.fs  obj
```
On a 2 fichiers, `HelloDevTo.fsproj` et `Library.fs`, le premier contient l'information essentiel de notre projet F#, toutes les libraires vont ici. `Library.fs` est le fichier F# crée par default, alors on va l'effacer et créer le fichier `HelloFunctions.fs`

##### Pas 2
Pour ajouter la fonctionnalité d'une Azure Fonction dans notre code, il est nécessaire d'ajouter le package **Microsoft.Net.Sdk.Functions**, après on doit créer les fichiers **host.json**, **local.settings.json** & **function.json**.
Pour ajouter le package, on va exécuter la suivante ligne de code:

```bash
darkhero> dotnet add package Microsoft.NET.Sdk.Functions
```

Pour le fichier **host.json**, on va copier (ou écrire) le suivante code:

```json
{
"version": "2.0",
}
```

Pour le fichier **local.settings.json**:
```json
{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet"
    }
}
```

Finalement dans le fichier **function.json**:
```json
{
  "bindings": [
    {
      "type": "httpTrigger",
      "methods": [
        "get",
        "post"
      ],
      "authLevel": "anonymous",
      "name": "req"
    }
  ],
  "disabled": false
}
```

Après de tenir les fichiers nécessaires, on va modifier le fichier **HelloDevTo.fsproj**, car on doit ajouter les fichiers qu'on avait créés avant. Si on ouvre ce fichier, on verrait le suivant :
```fsproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="Library.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
  </ItemGroup>

</Project>

```

Cependant, il n'y aura la configuration précise d'un azure fonction, pourtant, on va faire des petites modifications afin de lui donner la fonctionnalité. Pour faire cela, il n'est que nécessaire de changer et ajouter quelques lignes de code :

```csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <AzureFunctionsVersion>v2</AzureFunctionsVersion>
  </PropertyGroup>

  <ItemGroup>
    <Compile Include="HelloFunc.fs" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
  </ItemGroup>

  <ItemGroup>
    <Content Include="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
    <Content Include="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </Content>
  </ItemGroup>

</Project>

```

Après d'avoir changé le contenu de fichier **fsproj**, on va créer le fichier **HelloFunc.fs**, puis on va passer l'exemple avec C# à F#. Ici le code C#:

```C#
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace FunctionApp1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string name = req.Query["name"];

            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;

            return name != null
                ? (ActionResult)new OkObjectResult($"Hello, {name}")
                : new BadRequestObjectResult("Please pass a name on the query string or in the request body");
        }
    }
}
```

Maintenant, on va convertir ça. Ici le code transformé:
```F#
namespace HelloDevTo

open System
open Microsoft.AspNetCore.Mvc
open Microsoft.Azure.WebJobs
open Microsoft.Azure.WebJobs.Extensions.Http
open Microsoft.AspNetCore.Http
open Microsoft.Extensions.Logging
open Newtonsoft.Json
open System.IO

module HelloFunction =
    // Define a nullable container to deserialize into.
    [<AllowNullLiteral>]
    type NameContainer() =
        member val Name = "" with get, set

    // For convenience, it's better to have a central place for the literal.
    [<Literal>]
    let Name = "name"

    // Nombre de la funcion
    [<FunctionName("HelloFunction")>]
    // Nivel de Autorizacion para consumir la funcion, la cambiamos a Anonymous ya que para este ejemplo no requerimos nada de seguridad
    // Tambien que metodos HTTP acepta
    // Route nos permite customizar el nombre de salida al publicar la funcion
    let Run([<HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)>] req : HttpRequest, log: ILogger) =
        async {
            log.LogInformation("F# HTTP trigger function processed a request.")

            let nameOpt = 
                if req.Query.ContainsKey(Name) then
                    Some(req.Query.[Name].[0])
                else
                    None

            use stream = new StreamReader(req.Body)
            let! reqBody = stream.ReadToEndAsync() |> Async.AwaitTask

            let data = JsonConvert.DeserializeObject<NameContainer>(reqBody)

            let name =
                match nameOpt with
                | Some n -> n
                | None ->
                   match data with
                   | null -> ""
                   | nc -> nc.Name
            
            let responseMessage =             
                if (String.IsNullOrWhiteSpace(name)) then
                    "Please pass a name on the query string or in the request body"
                else
                    $"Hello, {name}"

            return OkObjectResult(responseMessage) :> IActionResult
        } |> Async.StartAsTask

```

Ensuite en utilisant la terminal, on exécute `func start --verbose`, ça va lancer la fonctionne dans notre environnement local et nous recevrons un lien avec lequel on peut tester, dans mon cas, j'utilisais curl pour y tester.

```bash
darkhero> curl http://localhost:7071/api/HelloFunction
Please pass a name on the query string or in the request body
darkhero> curl http://localhost:7071/api/HelloFunction?name=DevTo
Hello, DevTo
```

Voilà, on a un Azure Fonction qui utilise F# comme langue de programmation. Aussi il est possible de travailler avec Azure Fonction d'une autre manière plus simple, cependant, dans cet article, on verra seulement ce premier essai. 

En fin, j’ai fait toutes ses pas pour créer un azure fonction avec F#, tandis que la création d’une avec une autre langue de programmation va être facile puisque celle-ci est plus populaire, par exemple, quand on a installé `func` et on voit la liste des langues supportés, on peut voir que seulement a templates pour C#, NodeJS, PowerShell et Python, même si on a besoin de travailler avec Golang, F#, Rust ou Deno, nous devons créer nos projets d’une autre façon, étant donné que Microsoft oublie parfois qu’il peut supporter des autres langues de programmation. Pour moi, ça a été amusant puisque j’ai appris beaucoup pendants les recherches.