Skriva bank-API:et

Slutförd

Nu när vi har skapat onlinebankens kärnlogik ska vi skapa ett webb-API för att testa det från en webbläsare (eller till och med kommandoraden). För tillfället använder vi inte en databas för att spara data, så vi måste skapa en global variabel för att lagra alla konton i minnet.

Dessutom hoppar vi över testdelen för att undvika att den här guiden hålls för lång. Vi rekommenderar att du följer samma metod som vi följde när du skapar kärnpaketet för att skriva tester före kod.

Konfigurera ett konto i minnet

I stället för att använda en databas för att spara data använder vi en minneskarta för de konton som vi skapar när programmet startar. Dessutom använder vi en karta för att komma åt kontoinformationen med hjälp av kontonumret.

Gå till $GOPATH/src/bankapi/main.go filen och lägg till följande kod för att skapa den globala accounts variabeln och initiera den med ett konto. (Den här koden liknar den vi gjorde när vi skapade testerna tidigare.)

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

Kontrollera att du är på platsen $GOPATH/src/bankapi/. Kör programmet med go run main.go för att kontrollera att du inte har några fel. Programmet gör inget annat för tillfället, så vi lägger till logiken för att skapa ett webb-API.

Exponera instruktionsmetoden

Det är enkelt att skapa ett webb-API i Go, som du såg i en tidigare modul. Vi fortsätter att använda net/http paketet. Vi använder HandleFunc även funktionerna och ListenAndServe för att exponera slutpunkter och starta servern. Funktionen HandleFunc kräver ett namn på den URL-sökväg som du vill exponera och namnet på en funktion med logiken för slutpunkten.

Vi börjar med att exponera funktionen för att skriva ut -instruktionen för ett konto. Kopiera och klistra in följande funktion i 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())
        }
    }
}

Den första markmarkeringen statement från funktionen är att den tar emot objektet för att skriva ett svar tillbaka till webbläsaren (w http.ResponseWriter). Den tar också emot begärandeobjektet för att få åtkomst till informationen från HTTP-begäran (req *http.Request).

Observera sedan att vi använder req.URL.Query().Get() funktionen för att läsa en parameter från frågesträngen. Den här parametern är det kontonummer som vi skickar via HTTP-anropet. Vi använder det värdet för att komma åt kontokartan och hämta dess information.

Eftersom vi hämtar data från användaren bör vi inkludera vissa valideringar för att undvika en krasch. När vi vet att vi har ett giltigt kontonummer kan vi anropa Statement() metoden och skriva ut strängen som den returnerar till webbläsaren (fmt.Fprintf(w, account.Statement())).

Ändra funktionen main() så att den ser ut så här:

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

Om du inte ser några fel eller utdata när du kör programmet (go run main.go) fungerar det korrekt. Öppna en webbläsare och ange URL:en http://localhost:8000/statement?number=1001eller kör följande kommando i ett annat gränssnitt medan programmet körs:

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

Du bör se följande utdata:

1001 - John - 0

Exponera insättningsmetoden

Vi fortsätter att använda samma metod för att exponera insättningsmetoden. I det här fallet vill vi lägga till pengar på det konto som vi har i minnet. Varje gång vi anropar Deposit() metoden bör saldot öka.

I huvudprogrammet lägger du till en deposit() funktion som följande. Funktionen hämtar kontonumret från frågesträngen, verifierar att kontot finns på accounts kartan, verifierar att beloppet som ska sättas in är ett giltigt nummer och anropar Deposit() sedan metoden.

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())
            }
        }
    }
}

Observera att den här funktionen följer en liknande metod för att hämta och verifiera de data som den tar emot från användaren. Vi deklarerar och använder även variabler direkt i -instruktionen if . Slutligen, när vi har lagt till några medel till kontot, skriver vi ut -instruktionen för att se det nya kontosaldot.

Nu bör du exponera en /deposit slutpunkt som anropar deposit funktionen. Ändra funktionen main() så här:

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

Om du inte ser några fel eller utdata när du kör programmet (go run main.go) fungerar det korrekt. Öppna en webbläsare och ange URL:en http://localhost:8000/deposit?number=1001&amount=100eller kör följande kommando i ett annat gränssnitt medan programmet körs:

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

Du bör se följande utdata:

1001 - John - 100

Om du gör samma anrop flera gånger fortsätter kontosaldot att öka. Försök bekräfta att accounts kartan i minnet uppdateras vid körning. Om du stoppar programmet går alla insättningar du gjorde förlorade, men det förväntas i den här första versionen.

Exponera återdragningsmetoden

Slutligen ska vi exponera metoden för att ta ut pengar från ett konto. Återigen ska vi först skapa withdraw funktionen i huvudprogrammet. Funktionen verifierar kontonummerinformationen, drar tillbaka och skriver ut eventuella fel som du får från kärnpaketet. Lägg till följande funktion i huvudprogrammet:

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())
            }
        }
    }
}

Lägg nu till /withdraw slutpunkten i main() funktionen för att exponera logiken withdraw() som du har i funktionen. main() Ändra funktionen så här:

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

Om du inte ser några fel eller utdata när du kör programmet (go run main.go) fungerar det korrekt. Öppna en webbläsare och ange URL:en http://localhost:8000/withdraw?number=1001&amount=100eller kör följande kommando i ett annat gränssnitt medan programmet körs:

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

Du bör se följande utdata:

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

Observera att felet vi får kommer från kärnpaketet. När programmet startar är kontosaldot noll. Därför kan du inte ta ut någon summa pengar. /deposit Anropa slutpunkten några gånger för att lägga till pengar och anropa /withdraw slutpunkten igen för att bekräfta att det fungerar:

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"

Du bör se följande utdata:

1001 - John - 200

Det var allt! Du har skapat ett webb-API för att exponera funktioner från ett paket som du skapade från grunden. Gå till nästa avsnitt för att fortsätta öva. Den här gången får du en utmaning där du skriver en egen lösning.