Escrever a API do banco
Agora que criamos a lógica do núcleo do banco online, vamos criar uma API Web para testá-la em um navegador (ou até mesmo na linha de comando). Por enquanto, não usaremos um banco de dados para manter os dados, então precisaremos criar uma variável global para armazenar todas as contas na memória.
Além disso, vamos ignorar a parte de teste para evitar manter este guia muito longo. O ideal é que você siga a mesma abordagem que seguimos ao criar o pacote de núcleo para escrever testes antes do código.
Configurar uma conta na memória
Em vez de usar um banco de dados para manter os dados, usaremos um mapa de memória para as contas que criaremos quando o programa for iniciado. Além disso, usaremos um mapa para acessar as informações da conta usando o número da conta.
Acesse o arquivo $GOPATH/src/bankapi/main.go
e adicione o código a seguir para criar a variável global accounts
e inicializá-la com uma conta. Esse código é semelhante ao que fizemos quando criamos os testes anteriormente.
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,
}
}
Você deve estar no local $GOPATH/src/bankapi/
. Execute o programa com go run main.go
para verificar se você não tem nenhum erro. O programa não fará mais nada por enquanto, então vamos adicionar a lógica para criar uma API Web.
Expor o método de extrato
A criação de uma API Web em Go é fácil, como vimos em um módulo anterior. Continuaremos usando o pacote net/http
. Também usaremos as funções HandleFunc
e ListenAndServe
para expor pontos de extremidade e iniciar o servidor. A função HandleFunc
requer um nome para o caminho da URL que você deseja expor e o nome de uma função com a lógica para esse ponto de extremidade.
Vamos começar expondo a funcionalidade para imprimir o extrato de uma conta. Copie e cole a seguinte função em 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())
}
}
}
O primeiro destaque da função statement
é que ela está recebendo o objeto para gravar uma resposta de volta no navegador (w http.ResponseWriter
). Ela também está recebendo o objeto de solicitação para acessar as informações da solicitação HTTP (req *http.Request
).
Em seguida, observe que estamos usando a função req.URL.Query().Get()
para ler um parâmetro da cadeia de caracteres de consulta. Esse parâmetro é o número da conta que enviaremos por meio da chamada HTTP. Usaremos esse valor para acessar o mapa da conta e obter as informações dela.
Como estamos obtendo os dados do usuário, devemos incluir algumas validações para evitar uma falha. Quando sabemos que temos um número de conta válido, podemos fazer a chamada ao método Statement()
e imprimir a cadeia de caracteres que ela retorna para o navegador (fmt.Fprintf(w, account.Statement())
).
Agora, modifique sua função main()
para que ela tenha a seguinte aparência:
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))
}
Se você não vê nenhum erro ou resultado ao executar o programa (go run main.go
), ele está funcionando corretamente. Abra um navegador da Web e insira a URL http://localhost:8000/statement?number=1001
ou execute o seguinte comando em outro shell enquanto o programa estiver em execução:
curl http://localhost:8000/statement?number=1001
Você deve ver o seguinte resultado:
1001 - John - 0
Expor o método de depósito
Vamos continuar usando a mesma abordagem para expor o método de depósito. Nesse caso, queremos adicionar dinheiro à conta que temos na memória. Toda vez que chamamos o método Deposit()
, o saldo deve aumentar.
No programa principal, adicione uma função deposit()
como a descrita a seguir. A função obtém o número da conta da cadeia de caracteres de consulta, valida que a conta existe no mapa accounts
, valida que o valor a ser depositado é um número válido e, depois, chama o método 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())
}
}
}
}
Observe que essa função segue uma abordagem semelhante para obter e validar os dados recebidos do usuário. Também estamos declarando e usando variáveis diretamente na instrução if
. Por fim, depois de adicionarmos alguns fundos à conta, vamos imprimir o extrato para ver o novo saldo.
Agora, você deve expor um ponto de extremidade /deposit
que chama a função deposit
. Modifique sua função main()
para que ela tenha a seguinte aparência:
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))
}
Se você não vê nenhum erro ou resultado ao executar o programa (go run main.go
), ele está funcionando corretamente. Abra um navegador da Web e insira a URL http://localhost:8000/deposit?number=1001&amount=100
ou execute o seguinte comando em outro shell enquanto o programa estiver em execução:
curl "http://localhost:8000/deposit?number=1001&amount=100"
Você deve ver o seguinte resultado:
1001 - John - 100
Se você fizer a mesma chamada várias vezes, o saldo da conta continuará aumentando. Experimente fazer isso para confirmar que o mapa accounts
na memória é atualizado no runtime. Se você parar o programa, todos os depósitos realizados serão perdidos, mas isso é esperado na versão inicial.
Expor o método de saque
Por fim, vamos expor o método para sacar dinheiro de uma conta. Novamente, vamos primeiro criar a função withdraw
no programa principal. A função validará as informações de número da conta, fará saques e imprimirá qualquer erro que você receber do pacote de núcleo. Adicione a seguinte função ao programa 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())
}
}
}
}
Agora adicione o ponto de extremidade /withdraw
na função main()
para expor a lógica que você tem na função withdraw()
. Modifique a função main()
para que ela tenha esta aparência:
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))
}
Se você não vê nenhum erro ou resultado ao executar o programa (go run main.go
), ele está funcionando corretamente. Abra um navegador da Web e insira a URL http://localhost:8000/withdraw?number=1001&amount=100
ou execute o seguinte comando em outro shell enquanto o programa estiver em execução:
curl "http://localhost:8000/withdraw?number=1001&amount=100"
Você deve ver o seguinte resultado:
the amount to withdraw should be greater than the account's balance
Observe que o erro que estamos obtendo é proveniente do pacote de núcleo. Quando o programa é iniciado, o saldo da conta é zero. Portanto, você não pode sacar nenhuma quantidade de dinheiro. Chame o ponto de extremidade /deposit
algumas vezes para adicionar fundos e chame o ponto de extremidade /withdraw
novamente para confirmar que está funcionando:
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"
Você deve ver o seguinte resultado:
1001 - John - 200
É isso! Você criou uma API Web para expor a funcionalidade de um pacote que criou do zero. Acesse a próxima seção para continuar praticando. Desta vez, você terá um desafio para escrever a própria solução.