Azure Functions avec F#

kiramishima

Paul Arizpe

Posted on April 7, 2021

Azure Functions avec F#

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

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:

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 :

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
Enter fullscreen mode Exit fullscreen mode

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

# 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
Enter fullscreen mode Exit fullscreen mode

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:

darkhero> dotnet add package Microsoft.NET.Sdk.Functions
Enter fullscreen mode Exit fullscreen mode

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

{
"version": "2.0",
}
Enter fullscreen mode Exit fullscreen mode

Pour le fichier local.settings.json:

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet"
    }
}
Enter fullscreen mode Exit fullscreen mode

Finalement dans le fichier function.json:

{
  "bindings": [
    {
      "type": "httpTrigger",
      "methods": [
        "get",
        "post"
      ],
      "authLevel": "anonymous",
      "name": "req"
    }
  ],
  "disabled": false
}
Enter fullscreen mode Exit fullscreen mode

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 :

<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>

Enter fullscreen mode Exit fullscreen mode

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 :

<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>

Enter fullscreen mode Exit fullscreen mode

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#:

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");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Maintenant, on va convertir ça. Ici le code transformé:

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

Enter fullscreen mode Exit fullscreen mode

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.

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
Enter fullscreen mode Exit fullscreen mode

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.

💖 💪 🙅 🚩
kiramishima
Paul Arizpe

Posted on April 7, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Azure Functions avec F#
azurefunctions Azure Functions avec F#

April 7, 2021