Escritura de la API del banco
Ahora que hemos creado la lógica principal del banco en línea, vamos a crear una API web para probarla desde un explorador (o incluso desde la línea de comandos). Por ahora, no usaremos una base de datos para conservar los datos, por lo que tendremos que crear una variable global para almacenar todas las cuentas en la memoria.
Además, omitiremos la parte de pruebas para evitar que esta guía sea demasiado larga. Idealmente, debe seguir el mismo enfoque que hemos seguido al crear el paquete principal para escribir pruebas antes del código.
Configuración de una cuenta en memoria
En lugar de usar una base de datos para conservar los datos, usaremos una asignación de memoria para las cuentas que crearemos cuando se inicie el programa. Además, usaremos una asignación para acceder a la información de la cuenta mediante el número de cuenta.
Vaya al archivo $GOPATH/src/bankapi/main.go
y agregue el código siguiente para crear la variable accounts
global e inicializarla con una cuenta. (Este código es similar a lo que hicimos cuando creamos las pruebas 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,
}
}
Asegúrese de que está en la ubicación $GOPATH/src/bankapi/
. Ejecute el programa con go run main.go
para asegurarse de que no tiene errores. El programa no hace nada más por ahora, así que vamos a agregar la lógica para crear una API web.
Exposición del método de extracto
Crear una API web en Go es fácil, como se vio en un módulo anterior. Seguiremos usando el paquete net/http
. También usaremos las funciones HandleFunc
y ListenAndServe
para exponer puntos de conexión e iniciar el servidor. La función HandleFunc
requiere un nombre para la ruta de acceso de la dirección URL que desea exponer y el nombre de una función con la lógica para ese punto de conexión.
Comencemos por exponer la funcionalidad para imprimir el extracto de una cuenta. Copie y pegue la siguiente función en 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())
}
}
}
El primer aspecto destacado de la función statement
es que está recibiendo el objeto para escribir una respuesta en el explorador (w http.ResponseWriter
). También recibe el objeto de solicitud para acceder a la información de la solicitud HTTP (req *http.Request
).
A continuación, observe que estamos usando la función req.URL.Query().Get()
para leer un parámetro de la cadena de consulta. Este parámetro es el número de cuenta que se enviará mediante la llamada HTTP. Usaremos ese valor para acceder a la asignación de la cuenta y obtener su información.
Dado que vamos a obtener datos del usuario, debemos incluir algunas validaciones para evitar un bloqueo. Cuando sabemos que tenemos un número de cuenta válido, podemos realizar la llamada al método Statement()
e imprimir la cadena que devuelve al explorador (fmt.Fprintf(w, account.Statement())
).
Ahora, modifique la función main()
de modo que tenga este aspecto:
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 no aparece ningún error o salida al ejecutar el programa (go run main.go
), significa que funciona correctamente. Abra un explorador web y escriba la dirección URL http://localhost:8000/statement?number=1001
, o bien ejecute el comando siguiente en otro shell mientras se ejecuta el programa:
curl http://localhost:8000/statement?number=1001
Debería ver la siguiente salida:
1001 - John - 0
Exposición del método de ingreso de dinero
Vamos a seguir usando el mismo enfoque para exponer el método de depósito de dinero. En este caso, queremos ingresar dinero en la cuenta que tenemos en la memoria. Cada vez que llamamos al método Deposit()
, el saldo debería aumentar.
En el programa principal, agregue una función deposit()
como la siguiente. La función obtiene el número de cuenta de la cadena de consulta, valida que la cuenta existe en la asignación accounts
, valida que la cantidad que se va a ingresar es un número válido y, por último, llama al 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())
}
}
}
}
Tenga en cuenta que esta función sigue un enfoque similar para obtener y validar los datos que recibe del usuario. También se declaran y usan variables directamente en el extracto if
. Por último, después de agregar algunos fondos a la cuenta, se imprime el extracto para ver el nuevo saldo de la cuenta.
Ahora, debe exponer un punto de conexión /deposit
que llame a la función deposit
. Modifique la función main()
para que tenga el siguiente aspecto:
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 no aparece ningún error o salida al ejecutar el programa (go run main.go
), significa que funciona correctamente. Abra un explorador web y escriba la dirección URL http://localhost:8000/deposit?number=1001&amount=100
, o bien ejecute el comando siguiente en otro shell mientras se ejecuta el programa:
curl "http://localhost:8000/deposit?number=1001&amount=100"
Debería ver la siguiente salida:
1001 - John - 100
Si realiza la misma llamada varias veces, el saldo de la cuenta seguirá aumentando. Pruébelo para confirmar que la asignación accounts
en la memoria se actualiza en tiempo de ejecución. Si detiene el programa, se perderán todos los ingresos que haya realizado, pero en esta versión inicial se espera tal comportamiento.
Exposición del método de retirada de dinero
Por último, vamos a exponer el método para retirar dinero de una cuenta. Una vez más, vamos a crear primero la función withdraw
en el programa principal. La función validará la información del número de cuenta, retirará dinero e imprimirá cualquier error que reciba del paquete principal. Agregue la siguiente función al 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())
}
}
}
}
Ahora, agregue el punto de conexión /withdraw
en la función main()
para exponer la lógica que tiene en la función withdraw()
. Modifique la función main()
para que tenga el siguiente aspecto:
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 no aparece ningún error o salida al ejecutar el programa (go run main.go
), significa que funciona correctamente. Abra un explorador web y escriba la dirección URL http://localhost:8000/withdraw?number=1001&amount=100
, o bien ejecute el comando siguiente en otro shell mientras se ejecuta el programa:
curl "http://localhost:8000/withdraw?number=1001&amount=100"
Debería ver la siguiente salida:
the amount to withdraw should be greater than the account's balance
Tenga en cuenta que el error que aparece procede del paquete principal. Cuando el programa se inicia, el saldo de la cuenta es cero. Por lo tanto, no se puede retirar ninguna cantidad de dinero. Llame al punto de conexión /deposit
varias veces para agregar fondos y llame de nuevo al punto de conexión /withdraw
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"
Debería ver la siguiente salida:
1001 - John - 200
Eso es todo. Ha creado una API web para exponer la funcionalidad de un paquete que creó desde cero. Vaya a la sección siguiente para continuar practicando. Esta vez se le presentará un desafío en el que escribirá su propia solución.