Écrire l’API de la banque

Effectué

Maintenant que nous avons créé la logique principale de la banque en ligne, nous allons créer une API Web pour la tester à partir d’un navigateur (ou même de la ligne de commande). Pour l’instant, nous n’utiliserons pas de base de données pour conserver les données. Nous devrons donc créer une variable globale pour stocker tous les comptes en mémoire.

En outre, nous allons ignorer la partie test pour éviter que ce guide soit trop long. Dans l’idéal, vous devez suivre la même approche que celle que nous avons suivie lors de la création du package de base pour écrire des tests avant le code.

Configurer un compte en mémoire

Au lieu d’utiliser une base de données pour conserver les données, nous allons utiliser un mappage de mémoire pour les comptes que nous allons créer au démarrage du programme. En outre, nous allons utiliser un mappage pour accéder aux informations de compte à l’aide du numéro de compte.

Accédez au fichier $GOPATH/src/bankapi/main.go et ajoutez le code suivant pour créer la variable accounts globale et l’initialiser avec un compte. (Ce code est similaire à ce que nous avons fait lorsque nous avons créé les tests précédemment.)

package main

import (
    "github.com/msft/bank"
)

var accounts = map[float64]*bank.Account{}

func main() {
    accounts[1001] = &bank.Account{
        Customer: bank.Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number: 1001,
    }
}

Vérifiez que vous êtes à l’emplacement $GOPATH/src/bankapi/. Exécutez le programme avec go run main.go pour vous assurer que vous n’avez pas d’erreurs. Le programme ne fait rien d’autre pour le moment. Nous allons donc ajouter la logique pour créer une API Web.

Exposer la méthode d’instruction

Il est facile de créer une API Web dans Go, comme vous l’avez vu dans un module précédent. Nous continuerons à utiliser le package net/http. Nous allons également utiliser les fonctions HandleFunc et ListenAndServe pour exposer des points de terminaison et lancer le serveur. La fonction HandleFunc requiert un nom pour le chemin d’accès de l’URL que vous souhaitez exposer et le nom d’une fonction avec la logique de ce point de terminaison.

Commençons par exposer la fonctionnalité d’impression de l’instruction d’un compte. Copiez et collez la fonction suivante dans main.go :

func statement(w http.ResponseWriter, req *http.Request) {
    numberqs := req.URL.Query().Get("number")

    if numberqs == "" {
        fmt.Fprintf(w, "Account number is missing!")
        return
    }

    if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
        fmt.Fprintf(w, "Invalid account number!")
    } else {
        account, ok := accounts[number]
        if !ok {
            fmt.Fprintf(w, "Account with number %v can't be found!", number)
        } else {
            fmt.Fprintf(w, account.Statement())
        }
    }
}

La première partie mise en surbrillance de la fonction statement indique qu’elle reçoit l’objet pour écrire une réponse dans le navigateur (w http.ResponseWriter). Elle reçoit également l’objet de requête pour accéder aux informations à partir de la requête HTTP (req *http.Request).

Remarquez ensuite que nous utilisons la fonction req.URL.Query().Get() pour lire un paramètre à partir de la chaîne de requête. Ce paramètre est le numéro de compte que nous allons envoyer via l’appel HTTP. Nous allons utiliser cette valeur pour accéder à la carte de compte et recevoir ses informations.

Étant donné que nous obtenons des données de l’utilisateur, nous devons inclure des validations pour éviter un incident. Lorsque nous savons que nous avons un numéro de compte valide, nous pouvons appeler la méthode Statement() et imprimer la chaîne qu’elle renvoie au navigateur (fmt.Fprintf(w, account.Statement())).

À présent, modifiez votre fonction main() pour qu’elle ressemble à ceci :

func main() {
    accounts[1001] = &bank.Account{
        Customer: bank.Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number: 1001,
    }

    http.HandleFunc("/statement", statement)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

Si vous ne voyez aucune erreur ou sortie quand vous exécutez le programme (go run main.go), cela fonctionne correctement. Ouvrez un navigateur web et entrez l’URL http://localhost:8000/statement?number=1001ou exécutez la commande suivante dans un autre interpréteur de commandes pendant que votre programme est en cours d’exécution :

curl http://localhost:8000/statement?number=1001

Vous devez normalement voir la sortie suivante.

1001 - John - 0

Exposer la méthode de dépôt

Nous allons continuer à utiliser la même approche pour exposer la méthode de dépôt. Dans ce cas, nous souhaitons ajouter de l’argent au compte que nous disposons en mémoire. Chaque fois que nous appelons la méthode Deposit(), le solde doit augmenter.

Dans le programme principal, ajoutez une fonction deposit() comme celle qui suit. La fonction obtient le numéro de compte à partir de la chaîne de requête, vérifie que le compte existe dans le mappage de accounts, valide que le montant à déposer est un nombre valide, puis appelle la méthode Deposit().

func deposit(w http.ResponseWriter, req *http.Request) {
    numberqs := req.URL.Query().Get("number")
    amountqs := req.URL.Query().Get("amount")

    if numberqs == "" {
        fmt.Fprintf(w, "Account number is missing!")
        return
    }

    if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
        fmt.Fprintf(w, "Invalid account number!")
    } else if amount, err := strconv.ParseFloat(amountqs, 64); err != nil {
        fmt.Fprintf(w, "Invalid amount number!")
    } else {
        account, ok := accounts[number]
        if !ok {
            fmt.Fprintf(w, "Account with number %v can't be found!", number)
        } else {
            err := account.Deposit(amount)
            if err != nil {
                fmt.Fprintf(w, "%v", err)
            } else {
                fmt.Fprintf(w, account.Statement())
            }
        }
    }
}

Notez que cette fonction suit une approche similaire pour obtenir et valider les données qu’elle reçoit de l’utilisateur. Nous déclarons et utilisons également des variables directement dans l’instruction if. Enfin, après avoir ajouté des fonds au compte, nous imprimons le relevé pour voir le nouveau solde du compte.

À présent, vous devez exposer un point de terminaison /deposit qui appelle la fonction deposit. Modifiez votre fonction main() pour qu’elle ressemble à ceci :

func main() {
    accounts[1001] = &bank.Account{
        Customer: bank.Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number: 1001,
    }

    http.HandleFunc("/statement", statement)
    http.HandleFunc("/deposit", deposit)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

Si vous ne voyez aucune erreur ou sortie quand vous exécutez le programme (go run main.go), cela fonctionne correctement. Ouvrez un navigateur web et entrez l’URL http://localhost:8000/deposit?number=1001&amount=100ou exécutez la commande suivante dans un autre interpréteur de commandes pendant que votre programme est en cours d’exécution :

curl "http://localhost:8000/deposit?number=1001&amount=100"

Vous devez normalement voir la sortie suivante.

1001 - John - 100

Si vous effectuez le même appel plusieurs fois, le solde du compte continuera à augmenter. Essayez de confirmer que la carte accounts en mémoire est mise à jour au moment de l’exécution. Si vous arrêtez le programme, tous les dépôts que vous avez effectués seront perdus, mais cela est normal dans la version initiale.

Exposer la méthode de retrait

Enfin, nous allons exposer la méthode pour retirer de l’argent d’un compte. Nous allons tout d’abord créer la fonction withdraw dans le programme principal. La fonction validera les informations sur le numéro de compte, retirera et imprimera toutes les erreurs que vous recevez du package principal. Ajoutez la fonction suivante à votre programme principal :

func withdraw(w http.ResponseWriter, req *http.Request) {
    numberqs := req.URL.Query().Get("number")
    amountqs := req.URL.Query().Get("amount")

    if numberqs == "" {
        fmt.Fprintf(w, "Account number is missing!")
        return
    }

    if number, err := strconv.ParseFloat(numberqs, 64); err != nil {
        fmt.Fprintf(w, "Invalid account number!")
    } else if amount, err := strconv.ParseFloat(amountqs, 64); err != nil {
        fmt.Fprintf(w, "Invalid amount number!")
    } else {
        account, ok := accounts[number]
        if !ok {
            fmt.Fprintf(w, "Account with number %v can't be found!", number)
        } else {
            err := account.Withdraw(amount)
            if err != nil {
                fmt.Fprintf(w, "%v", err)
            } else {
                fmt.Fprintf(w, account.Statement())
            }
        }
    }
}

Ajoutez maintenant le point de terminaison /withdraw dans la fonction main() pour exposer la logique que vous avez dans la fonction withdraw(). Modifiez la fonction main() pour qu’elle ressemble à ceci :

func main() {
    accounts[1001] = &bank.Account{
        Customer: bank.Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number: 1001,
    }

    http.HandleFunc("/statement", statement)
    http.HandleFunc("/deposit", deposit)
    http.HandleFunc("/withdraw", withdraw)
    log.Fatal(http.ListenAndServe("localhost:8000", nil))
}

Si vous ne voyez aucune erreur ou sortie quand vous exécutez le programme (go run main.go), cela fonctionne correctement. Ouvrez un navigateur web et entrez l’URL http://localhost:8000/withdraw?number=1001&amount=100ou exécutez la commande suivante dans un autre interpréteur de commandes pendant que votre programme est en cours d’exécution :

curl "http://localhost:8000/withdraw?number=1001&amount=100"

Vous devez normalement voir la sortie suivante.

the amount to withdraw should be greater than the account's balance

Notez que l’erreur que nous obtenons provient du package principal. Au démarrage du programme, le solde du compte est égal à zéro. Par conséquent, vous ne pouvez pas retirer de l’argent. Appelez le point de terminaison /deposit plusieurs fois pour ajouter des fonds, puis appelez à nouveau le point de terminaison de /withdraw pour confirmer que cela fonctionne :

curl "http://localhost:8000/deposit?number=1001&amount=100"
curl "http://localhost:8000/deposit?number=1001&amount=100"
curl "http://localhost:8000/deposit?number=1001&amount=100"
curl "http://localhost:8000/withdraw?number=1001&amount=100"

Vous devez normalement voir la sortie suivante.

1001 - John - 200

Et voilà ! Vous avez créé une API Web pour exposer les fonctionnalités d’un package que vous avez créé à partir de zéro. Passez à la section suivante pour poursuivre la mise en pratique. Cette fois, vous serez confronté à un défi dans lequel vous allez écrire votre propre solution.