Nell’articolo di oggi vedremo come instanziare un server ed esporre dei servizi per lettura e scrittura di una entità. Per fare questo utilizzeremo 2 nuovi package “net/http” e “encoding/json”, il primo ci permette di instanziare il server e definire le azioni da intraprendere per dei path, il secondo ci consente agevolvemente di fare encoding e decoding di oggetti json.
Incominciamo instanziando il server e implementando un servizio di keepalive che serve a stabilire se il server è attivo regolarmente.
1 2 |
http.HandleFunc("/isAlive", isAlive) log.Fatal(http.ListenAndServe(":10000", nil)) |
La funzione HandleFunc definisce il path e il relativo handler. Per handler si intende la funzione che verrà eseguita dal server in corrispondenza della richiesta riceveuta.
1 2 3 |
func isAlive(response http.ResponseWriter, request *http.Request) { fmt.Fprintf(response, "alive") } |
Se abbiamo fatto tutto bene invocando il servizio http://localhost:10000/isAlive riceveremo un 200 ok con testo alive.
A questo punto supponiamo di voler gestire una rubrica dei contatti e pertanto definiamo prima di tutto la nostra struct Contatto
1 2 3 4 |
type contatto struct { Nome string `json:"Nome"` Cognome string `json:"Cognome"` } |
La struct presenta 2 parametri Nome e Cognome e relative regole di trascodifica in json. Definiamo l’elenco dei contatti e lo inizializziamo con dei dati di prova, simulando la presenza di un database
1 2 3 4 5 6 7 |
var contatti []contatto func main() { contatti = []contatto{ contatto{Nome: "nome 1", Cognome: "Cognome 1"}, contatto{Nome: "nome 2", Cognome: "Cognome 2"}, } |
Infine definiamo l’handler relativo
1 2 3 4 5 6 7 |
func elencoContatti(response http.ResponseWriter, request *http.Request) { json.NewEncoder(response).Encode(contatti) } func main() { http.HandleFunc("/contatti", elencoContatti) } |
In tal modo invocando l’url http://localhost:10000/contatti otterremo il seguente json [{“Nome”:”nome 1″,”Cognome”:”Cognome 1″},{“Nome”:”nome 2″,”Cognome”:”Cognome 2″}]
Di seguito il codice completo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package main import ( "encoding/json" "fmt" "log" "net/http" ) type contatto struct { Nome string `json:"Nome"` Cognome string `json:"Cognome"` } func isAlive(response http.ResponseWriter, request *http.Request) { fmt.Fprintf(response, "alive") } func elencoContatti(response http.ResponseWriter, request *http.Request) { json.NewEncoder(response).Encode(contatti) } var contatti []contatto func main() { contatti = []contatto{ contatto{Nome: "nome 1", Cognome: "Cognome 1"}, contatto{Nome: "nome 2", Cognome: "Cognome 2"}, } fmt.Println("Http server up ") http.HandleFunc("/isAlive", isAlive) http.HandleFunc("/contatti", elencoContatti) log.Fatal(http.ListenAndServe(":10000", nil)) } |
Il package http ci ha consentito di tirare su il server e configurare delle risposte su dei path statici. Il package ha dei limiti, non ci permette di configurare degli endpoint variabili ed esplicitare il metodo http supportato. Per superare questo limite ci viene in soccorso un nuovo package “github.com/gorilla/mux”. Il package va installato con il comando
1 |
go get -u github.com/gorilla/mux |
A questo per tirare su il server e configurare opportunamente gli endpoint possiamo scrivere
1 2 3 4 5 6 7 |
myRouter := mux.NewRouter().StrictSlash(true) myRouter.HandleFunc("/isAlive", isAlive).Methods("GET") myRouter.HandleFunc("/contatti", elencoContatti).Methods("GET") myRouter.HandleFunc("/contatti/{id}", singoloContatto).Methods("GET") myRouter.HandleFunc("/contatti", creaContatto).Methods("POST") myRouter.HandleFunc("/contatti/{id}", eliminaContatto).Methods("DELETE") log.Fatal(http.ListenAndServe(":10000", myRouter)) |
Il packge ci mette a disposizione l’oggetto router che consente per ogni handler di definire maggiori proprietà per meglio mappare il comportamento
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func singoloContatto(response http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) key := vars["id"] for index, contatto := range contatti { fmt.Println(index, " ", key) i, _ := strconv.Atoi(key) if index == i { json.NewEncoder(response).Encode(contatto) return } } response.WriteHeader(http.StatusNotFound) } |
Il package ci consente agevolmente di leggere le path variable e superare i limiti del package net/http.
Di seguito il codice completo per l’analisi dei metodi restanti
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
package main import ( "encoding/json" "fmt" "log" "net/http" "strconv" "github.com/gorilla/mux" ) type contatto struct { Nome string `json:"Nome"` Cognome string `json:"Cognome"` } func isAlive(response http.ResponseWriter, request *http.Request) { fmt.Fprintf(response, "alive") } func elencoContatti(response http.ResponseWriter, request *http.Request) { json.NewEncoder(response).Encode(contatti) } func creaContatto(response http.ResponseWriter, request *http.Request) { var contatto contatto json.NewDecoder(request.Body).Decode(&contatto) contatti = append(contatti, contatto) json.NewEncoder(response).Encode(contatti) } func singoloContatto(response http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) key := vars["id"] for index, contatto := range contatti { fmt.Println(index, " ", key) i, _ := strconv.Atoi(key) if index == i { json.NewEncoder(response).Encode(contatto) return } } response.WriteHeader(http.StatusNotFound) } func eliminaContatto(response http.ResponseWriter, request *http.Request) { vars := mux.Vars(request) key := vars["id"] for index := range contatti { fmt.Println(index, " ", key) i, _ := strconv.Atoi(key) if index == i { contatti = append(contatti[:index], contatti[index+1:]...) response.WriteHeader(http.StatusOK) return } } response.WriteHeader(http.StatusNotFound) } var contatti []contatto func main() { contatti = []contatto{ contatto{Nome: "nome 1", Cognome: "Cognome 1"}, contatto{Nome: "nome 2", Cognome: "Cognome 2"}, } fmt.Println("Http server up ") //http.HandleFunc("/isAlive", isAlive) //http.HandleFunc("/contatti", elencoContatti) myRouter := mux.NewRouter().StrictSlash(true) myRouter.HandleFunc("/isAlive", isAlive).Methods("GET") myRouter.HandleFunc("/contatti", elencoContatti).Methods("GET") myRouter.HandleFunc("/contatti/{id}", singoloContatto).Methods("GET") myRouter.HandleFunc("/contatti", creaContatto).Methods("POST") myRouter.HandleFunc("/contatti/{id}", eliminaContatto).Methods("DELETE") log.Fatal(http.ListenAndServe(":10000", myRouter)) } |
Alla prossima