Développement de serveur Web Golang - De facile à complexe



Il y a cinq ans, j'ai commencé à développer Gophish , qui a permis d'apprendre le Golang. J'ai réalisé que Go est un langage puissant, dont les capacités sont complétées par de nombreuses bibliothèques. Go est universel: en particulier, avec son aide, il est possible de développer des applications serveur sans problème.

Cet article concerne l'écriture d'un serveur dans Go. Commençons par des choses simples, comme «Hello world!», Et terminons par une application avec les fonctionnalités suivantes:

- Utilisation de Let's Encrypt pour HTTPS.
- Fonctionne comme un routeur API.
- Travailler avec un middleware.
- Traitement des fichiers statiques.
- Arrêt correct.

Skillbox recommande: Le cours pratique de Python Developer from scratch .

Nous vous rappelons: pour tous les lecteurs de «Habr» - une remise de 10 000 roubles lors de l'inscription à un cours Skillbox en utilisant le code promo «Habr».

Bonjour tout le monde!


La création d'un serveur Web sur Go est très rapide. Voici un exemple d'utilisation d'un gestionnaire qui renvoie le "Bonjour, monde!" Promis ci-dessus.

package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) http.ListenAndServe(":80", nil) } 

Après cela, si vous démarrez l'application et ouvrez la page localhost , vous verrez immédiatement le texte "Bonjour, monde!" (bien sûr, si tout fonctionne correctement).

Ensuite, nous utiliserons à plusieurs reprises le gestionnaire, mais d'abord, comprenons comment tout fonctionne.

net / http


L'exemple utilise le package net/http , qui est l'outil principal de Go pour développer à la fois des serveurs et des clients HTTP. Afin de comprendre le code, examinons la signification de trois éléments importants: http.Handler, http.ServeMux et http.Server.

Gestionnaires HTTP


Lorsque nous recevons une demande, le gestionnaire l'analyse et forme une réponse. Les gestionnaires Go sont implémentés comme suit:

 type Handler interface { ServeHTTP(ResponseWriter, *Request) } 

Le premier exemple utilise la fonction d'assistance http.HandleFunc. Il encapsule une autre fonction, qui à son tour accepte http.ResponseWriter et http.Request dans ServeHTTP.

En d'autres termes, les gestionnaires Golang sont représentés par une interface unique, qui offre de nombreuses opportunités au programmeur. Ainsi, par exemple, le middleware est implémenté à l'aide d'un gestionnaire, où ServeHTTP fait d'abord quelque chose, puis appelle la méthode ServeHTTP d'un autre gestionnaire.

Comme mentionné ci-dessus, les gestionnaires génèrent simplement des réponses aux demandes. Mais quel gestionnaire particulier doit être utilisé à un moment donné?

Acheminement des demandes


Pour faire le bon choix, utilisez le multiplexeur HTTP. On l'appelle muxer ou routeur dans un certain nombre de bibliothèques, mais c'est tout de même. La fonction du multiplexeur consiste à analyser le chemin de demande et à sélectionner le gestionnaire approprié.

Si vous avez besoin de prise en charge pour un routage complexe, il est préférable d'utiliser des bibliothèques tierces. L'une des plus avancées est gorilla / mux et go-chi / chi , ces bibliothèques permettent d'implémenter sans problème des traitements intermédiaires. Avec leur aide, vous pouvez configurer le routage générique et effectuer un certain nombre d'autres tâches. Leur plus est la compatibilité avec les gestionnaires HTTP standard. En conséquence, vous pouvez écrire du code simple avec la possibilité de sa modification à l'avenir.

Travailler avec des cadres complexes dans une situation normale nécessitera des solutions pas tout à fait standard, ce qui complique grandement l'utilisation de gestionnaires par défaut. Pour créer la grande majorité des applications, une combinaison de la bibliothèque par défaut et d'un simple routeur suffit.

Traitement des demandes


De plus, nous avons besoin d'un composant qui «écoutera» les connexions entrantes et redirigera toutes les demandes vers le gestionnaire approprié. Cette tâche peut facilement être effectuée par http.Server.

Ce qui suit montre que le serveur est responsable de toutes les tâches liées au traitement de la connexion. Cela, par exemple, fonctionne sur le protocole TLS. Pour implémenter l'appel http.ListenAndServer, un serveur HTTP standard est utilisé.

Voyons maintenant des exemples plus complexes.

Ajout de Let's Encrypt


Par défaut, notre application s'exécute sur le protocole HTTP, mais il est recommandé d'utiliser le protocole HTTPS. Dans Go, cela peut se faire sans problème. Si vous avez reçu un certificat et une clé privée, enregistrez simplement ListenAndServeTLS avec le certificat et les fichiers de clés appropriés.

 http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil) 

On peut toujours faire mieux.

Let's Encrypt donne des certificats gratuits avec la possibilité de se renouveler automatiquement. Pour utiliser le service, vous avez besoin du package autocert .

Le moyen le plus simple de le configurer consiste à utiliser la méthode autocert.NewListener en combinaison avec http.Serve. La méthode vous permet de recevoir et de renouveler des certificats TLS, tandis que le serveur HTTP traite les demandes:

 http.Serve(autocert.NewListener("example.com"), nil) 

Si nous ouvrons example.com dans le navigateur, nous obtenons la réponse HTTPS "Bonjour tout le monde!".

Si vous avez besoin d'une configuration plus approfondie, vous devez utiliser le gestionnaire autocert.Manager. Ensuite, nous créons notre propre instance http.Server (jusqu'à présent, nous l'avons utilisé par défaut) et ajoutons le gestionnaire au serveur TLSConfig:

 m := &autocert.Manager{ Cache: autocert.DirCache("golang-autocert"), Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"), } server := &http.Server{ Addr: ":443", TLSConfig: m.TLSConfig(), } server.ListenAndServeTLS("", "") 

Il s'agit d'un moyen simple d'implémenter une prise en charge HTTPS complète avec renouvellement automatique des certificats.

Ajout d'itinéraires personnalisés


Le routeur par défaut inclus dans la bibliothèque standard est bon, mais il est très simple. La plupart des applications nécessitent un routage plus complexe, notamment des itinéraires imbriqués et génériques, ou la procédure de définition des modèles et des paramètres de chemin.

Dans ce cas, vous devez utiliser les packages gorilla / mux et go-chi / chi . Nous allons apprendre à travailler avec ce dernier - un exemple est illustré ci-dessous.

Étant donné le fichier api / v1 / api.go contenant les routes pour notre API:

 / HelloResponse is the JSON representation for a customized message type HelloResponse struct { Message string `json:"message"` } // HelloName returns a personalized JSON message func HelloName(w http.ResponseWriter, r *http.Request) { name := chi.URLParam(r, "name") response := HelloResponse{ Message: fmt.Sprintf("Hello %s!", name), } jsonResponse(w, response, http.StatusOK) } // NewRouter returns an HTTP handler that implements the routes for the API func NewRouter() http.Handler { r := chi.NewRouter() r.Get("/{name}", HelloName) return r } 

Nous définissons le préfixe api / vq pour les itinéraires dans le fichier principal.

Nous pouvons ensuite le monter sur notre routeur principal sous le préfixe api / v1 / dans notre application principale:

 // NewRouter returns a new HTTP handler that implements the main server routes func NewRouter() http.Handler { router := chi.NewRouter() router.Mount("/api/v1/", v1.NewRouter()) return router } http.Serve(autocert.NewListener("example.com"), NewRouter()) 

La simplicité de travailler avec des routes complexes dans Go permet de simplifier la structuration en servant de grandes applications complexes.

Travailler avec un middleware


Dans le cas d'un traitement intermédiaire, l'encapsulation d'un gestionnaire HTTP avec un autre est utilisée, ce qui permet d'authentifier rapidement, de compresser, de journaliser et d'autres fonctions.

À titre d'exemple, considérons l'interface http.Handler, avec son aide, nous écrivons un gestionnaire avec authentification des utilisateurs du service.

 func RequireAuthentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !isAuthenticated(r) { http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) return } // Assuming authentication passed, run the original handler next.ServeHTTP(w, r) }) } 

Il existe des routeurs tiers, par exemple chi, qui vous permettent d'étendre les fonctionnalités du traitement intermédiaire.

Travailler avec des fichiers statiques


La bibliothèque standard Go comprend des fonctionnalités pour travailler avec du contenu statique, y compris des images, ainsi que des fichiers JavaScript et CSS. Ils sont accessibles via la fonction http.FileServer. Il renvoie un gestionnaire qui distribue des fichiers à partir d'un répertoire spécifique.

 func NewRouter() http.Handler { router := chi.NewRouter() r.Get("/{name}", HelloName) //     staticPath, _ := filepath.Abs("../../static/") fs := http.FileServer(http.Dir(staticPath)) router.Handle("/*", fs) return r 

Il convient de se rappeler que http.Dir affiche le contenu du répertoire s'il ne possède pas le fichier index.html principal. Dans ce cas, pour éviter que le répertoire ne soit compromis, il vaut la peine d'utiliser le package unindexed .

Arrêt correct


Go a également une fonctionnalité telle que l'arrêt correct du serveur HTTP. Cela peut être fait en utilisant la méthode Shutdown (). Le serveur démarre en goroutine, puis le canal est écouté pour recevoir un signal d'interruption. Dès que le signal est reçu, le serveur s'arrête, mais pas immédiatement, mais après quelques secondes.

 handler := server.NewRouter() srv := &http.Server{ Handler: handler, } go func() { srv.Serve(autocert.NewListener(domains...)) }() // Wait for an interrupt c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) <-c // Attempt a graceful shutdown ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() srv.Shutdown(ctx) 

En conclusion


Go est un langage puissant avec une bibliothèque standard presque universelle. Ses capacités par défaut sont très étendues, et vous pouvez les renforcer à l'aide d'interfaces - cela vous permet de développer des serveurs HTTP vraiment fiables.
Skillbox recommande:

Source: https://habr.com/ru/post/fr446454/


All Articles