De bank-API schrijven

Voltooid

Nu we de kernlogica van de onlinebank hebben gebouwd, gaan we een web-API bouwen om deze te testen vanuit een browser (of zelfs de opdrachtregel). Voorlopig gebruiken we geen database om gegevens op te slaan. Daarom moeten we een globale variabele maken om alle accounts in het geheugen op te slaan.

Daarnaast slaan we het testonderdeel over om te voorkomen dat deze handleiding te lang blijft. In het ideale gevallen volgt u dezelfde benadering die we hebben gevolgd bij het bouwen van het kernpakket voor het schrijven van tests vóór code.

Een account instellen in het geheugen

In plaats van een database te gebruiken om gegevens te behouden, gebruiken we een geheugentoewijzing voor de accounts die we maken wanneer het programma wordt gestart. Daarnaast gebruiken we een kaart om toegang te krijgen tot de accountgegevens met behulp van het accountnummer.

Ga naar het $GOPATH/src/bankapi/main.go bestand en voeg de volgende code toe om de globale accounts variabele te maken en deze te initialiseren met een account. (Deze code is vergelijkbaar met wat we hebben gedaan toen we de tests eerder hebben gemaakt.)

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

Zorg ervoor dat u zich op de $GOPATH/src/bankapi/locatie bevindt. Voer het programma uit om go run main.go ervoor te zorgen dat u geen fouten hebt. Het programma doet momenteel niets anders, dus laten we de logica toevoegen om een web-API te maken.

De instructiemethode beschikbaar maken

Het maken van een web-API in Go is eenvoudig, zoals u in een vorige module hebt gezien. We blijven het net/http pakket gebruiken. We gebruiken ook de HandleFunc en ListenAndServe functies om eindpunten beschikbaar te maken en de server te starten. Voor HandleFunc de functie is een naam vereist voor het URL-pad dat u wilt weergeven en de naam van een functie met de logica voor dat eindpunt.

Laten we beginnen met het weergeven van de functionaliteit om de instructie voor een account af te drukken. Kopieer en plak de volgende functie in 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())
        }
    }
}

De eerste markering van de statement functie is dat het object wordt ontvangen om een antwoord terug te schrijven naar de browser (w http.ResponseWriter). Het ontvangt ook het aanvraagobject om toegang te krijgen tot de informatie van de HTTP-aanvraag (req *http.Request).

U ziet vervolgens dat we de req.URL.Query().Get() functie gebruiken om een parameter uit de querytekenreeks te lezen. Deze parameter is het accountnummer dat we via de HTTP-aanroep verzenden. We gebruiken deze waarde om toegang te krijgen tot de accounttoewijzing en de bijbehorende gegevens op te halen.

Omdat we gegevens van de gebruiker ophalen, moeten we enkele validaties opnemen om een crash te voorkomen. Wanneer we weten dat we een geldig rekeningnummer hebben, kunnen we de methode aanroepen Statement() en de tekenreeks afdrukken die wordt geretourneerd naar de browser (fmt.Fprintf(w, account.Statement())).

Wijzig nu uw main() functie zodat deze er als volgt uitziet:

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

Als er geen fout of uitvoer wordt weergegeven wanneer u het programma uitvoert (go run main.go), werkt het correct. Open een webbrowser en voer de URL http://localhost:8000/statement?number=1001in of voer de volgende opdracht uit in een andere shell terwijl uw programma wordt uitgevoerd:

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

U moet de volgende uitvoer zien:

1001 - John - 0

De stortingsmethode beschikbaar maken

Laten we dezelfde methode gebruiken om de stortingsmethode beschikbaar te maken. In dit geval willen we geld toevoegen aan de rekening die we in het geheugen hebben. Telkens wanneer we de Deposit() methode aanroepen, moet het saldo toenemen.

Voeg in het hoofdprogramma een deposit() functie toe zoals de volgende. De functie haalt het rekeningnummer op uit de querytekenreeks, valideert dat het account in de accounts kaart bestaat, valideert of het bedrag dat moet worden betaald een geldig nummer is en roept vervolgens de Deposit() methode aan.

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

U ziet dat deze functie een vergelijkbare benadering volgt om de gegevens op te halen en te valideren die door de gebruiker worden ontvangen. We declareren en gebruiken ook variabelen rechtstreeks in de if instructie. Ten slotte, nadat we wat geld aan de rekening hebben toegevoegd, drukken we de verklaring af om het nieuwe rekeningsaldo te zien.

Nu moet u een /deposit eindpunt beschikbaar maken waarmee de deposit functie wordt aangeroepen. Wijzig uw main() functie zodat deze er als volgt uitziet:

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

Als er geen fout of uitvoer wordt weergegeven wanneer u het programma uitvoert (go run main.go), werkt het correct. Open een webbrowser en voer de URL http://localhost:8000/deposit?number=1001&amount=100in of voer de volgende opdracht uit in een andere shell terwijl uw programma wordt uitgevoerd:

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

U moet de volgende uitvoer zien:

1001 - John - 100

Als u dezelfde aanroep meerdere keren doet, blijft het saldo van het account toenemen. Probeer te bevestigen dat de kaart in het accounts geheugen tijdens runtime wordt bijgewerkt. Als u het programma stopt, gaan alle stortingen die u hebt gedaan verloren, maar dat wordt verwacht in deze eerste versie.

De ingetrokken methode beschikbaar maken

Ten slotte gaan we de methode beschikbaar maken om geld van een account in te trekken. Nogmaals, laten we eerst de withdraw functie maken in het hoofdprogramma. Met de functie worden de accountnummergegevens gevalideerd, worden alle fouten die u van het kernpakket ontvangt, ingetrokken en afgedrukt. Voeg de volgende functie toe aan uw hoofdprogramma:

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

Voeg nu het /withdraw eindpunt toe aan de main() functie om de logica weer te geven die u in de withdraw() functie hebt. Wijzig de main() functie zodat deze er als volgt uitziet:

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

Als er geen fout of uitvoer wordt weergegeven wanneer u het programma uitvoert (go run main.go), werkt het correct. Open een webbrowser en voer de URL http://localhost:8000/withdraw?number=1001&amount=100in of voer de volgende opdracht uit in een andere shell terwijl uw programma wordt uitgevoerd:

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

U moet de volgende uitvoer zien:

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

U ziet dat de fout die we krijgen afkomstig is van het kernpakket. Wanneer het programma wordt gestart, is het rekeningsaldo nul. Daarom kunt u geen geldbedrag opnemen. Roep het /deposit eindpunt een paar keer aan om geld toe te voegen en roep het /withdraw eindpunt opnieuw aan om te bevestigen dat dit werkt:

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"

U moet de volgende uitvoer zien:

1001 - John - 200

Dat is het! U hebt een web-API gemaakt om functionaliteit beschikbaar te maken vanuit een pakket dat u helemaal zelf hebt gebouwd. Ga naar de volgende sectie om door te gaan met oefenen. Deze keer krijgt u een uitdaging te zien waarin u uw eigen oplossing schrijft.