Comment créer une application Web avec Go, Gin et React

Cet article a été initialement publié sur Mon blog

TL; DR: Dans ce tutoriel, je vais vous montrer à quel point il est facile de créer une application Web avec Go et le framework Gin et d'y ajouter une authentification. Consultez le dépôt Github pour le code que nous allons écrire.

Gin est un micro-framework performant. Il fournit un cadre très minimaliste qui ne comporte que les fonctionnalités, bibliothèques et fonctionnalités les plus essentielles nécessaires pour créer des applications Web et des microservices. Il simplifie la création d'un pipeline de traitement des demandes à partir de pièces modulaires et réutilisables. Pour ce faire, il vous permet d'écrire des intergiciels qui peuvent être connectés à un ou plusieurs gestionnaires de requêtes ou groupes de gestionnaires de requêtes.

Caractéristiques du gin

Gin est un framework Web rapide, simple mais complet et très efficace pour Go. Découvrez quelques-unes des fonctionnalités ci-dessous qui en font un cadre digne à considérer pour votre prochain projet Golang.

  • Vitesse: Gin est conçu pour la vitesse. Le framework offre un routage basé sur l'arbre Radix et une faible empreinte mémoire. Pas de réflexion. Performances de l'API prévisibles.
  • Crash-Free : Gin a la capacité d'attraper des plantages ou des paniques pendant l'exécution, et peut s'en remettre. De cette façon, votre application sera toujours disponible.
  • Routage: Gin fournit une interface de routage pour vous permettre d'exprimer l'apparence de votre application Web ou des routes API.
  • Validation JSON: Gin peut analyser et valider facilement les requêtes JSON, en vérifiant l'existence des valeurs requises.
  • Gestion des erreurs: Gin fournit un moyen pratique de collecter toutes les erreurs survenues lors d'une requête HTTP. Finalement, un middleware peut les écrire dans un fichier journal ou dans une base de données et les envoyer via le réseau.
  • Rendu intégré: Gin fournit une API facile à utiliser pour le rendu JSON, XML et HTML.

Conditions préalables

Pour suivre ce didacticiel, vous devez avoir Go installé sur votre machine, un navigateur Web pour afficher l'application et une ligne de commande pour exécuter les commandes de construction.

Go, ou comme on l'appelle normalement Golang , est un langage de programmation développé par Google pour créer des logiciels modernes. Go est un langage conçu pour faire avancer les choses efficacement et rapidement. Les principaux avantages de Go incluent:

  • Fortement typé et ramassé
  • Temps de compilation ultra-rapides
  • Concurrence intégrée
  • Bibliothèque standard étendue

Rendez-vous dans la section des téléchargements du site Web de Go pour lancer Go sur votre ordinateur.

Créer une application avec Gin

Nous allons créer une application de liste de blagues simple avec Gin . Notre application listera quelques blagues idiotes de papa. Nous allons y ajouter une authentification, afin que tous les utilisateurs connectés aient le privilège d'aimer et de voir des blagues.

Cela nous permettra d'illustrer comment Gin peut être utilisé pour développer des applications Web et / ou des API.

Nous utiliserons les fonctionnalités suivantes proposées par Gin:

  • Intergiciel
  • Routage
  • Regroupement d'itinéraires

À vos marques, prêts, partez

Nous écrirons toute notre application Go dans un main.gofichier. Comme il s'agit d'une petite application, il sera facile de créer l'application uniquement à go runpartir du terminal.

Nous allons créer un nouveau répertoire golang-gindans notre espace de travail Go, puis un main.gofichier dedans:

$ mkdir -p $GOPATH/src/github.com/user/golang-gin $ cd $GOPATH/src/github.com/user/golang-gin $ touch main.go

Le contenu du main.gofichier:

package main import ( "net/http" "github.com/gin-gonic/contrib/static" "github.com/gin-gonic/gin" ) func main() { // Set the router as the default one shipped with Gin router := gin.Default() // Serve frontend static files router.Use(static.Serve("/", static.LocalFile("./views", true))) // Setup route group for the API api := router.Group("/api") { api.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H { "message": "pong", }) }) } // Start and run the server router.Run(":3000") }

Nous devrons créer quelques répertoires supplémentaires pour nos fichiers statiques. Dans le même répertoire que le main.gofichier, créons un viewsdossier. Dans le viewsdossier, créez un jsdossier et un index.htmlfichier dedans.

Le index.htmlfichier sera très simple pour l'instant:

   Jokeish App   

Welcome to the Jokeish App

Avant de tester ce que nous avons jusqu'à présent, installons les dépendances ajoutées:

$ go get -u github.com/gin-gonic/gin $ go get -u github.com/gin-gonic/contrib/static

Pour voir ce qui fonctionne, nous devons démarrer notre serveur en exécutant go run main.go.

Une fois l'application en cours d'exécution, accédez à //localhost:3000dans votre navigateur. Si tout s'est bien passé, vous devriez voir le texte d'en-tête de niveau 1 Bienvenue dans l'application Jokeish affiché.

Définition de l'API

Ajoutons un peu plus de code dans notre main.gofichier pour nos définitions d'API. Nous mettrons à jour notre mainfonction avec deux routes /jokes/et /jokes/like/:jokeIDle groupe de routes /api/.

func main() { // ... leave the code above untouched... // Our API will consit of just two routes // /jokes - which will retrieve a list of jokes a user can see // /jokes/like/:jokeID - which will capture likes sent to a particular joke api.GET("/jokes", JokeHandler) api.POST("/jokes/like/:jokeID", LikeJoke) } // JokeHandler retrieves a list of available jokes func JokeHandler(c *gin.Context) { c.Header("Content-Type", "application/json") c.JSON(http.StatusOK, gin.H { "message":"Jokes handler not implemented yet", }) } // LikeJoke increments the likes of a particular joke Item func LikeJoke(c *gin.Context) { c.Header("Content-Type", "application/json") c.JSON(http.StatusOK, gin.H { "message":"LikeJoke handler not implemented yet", }) }

Le contenu du main.gofichier doit ressembler à ceci:

package main import ( "net/http" "github.com/gin-gonic/contrib/static" "github.com/gin-gonic/gin" ) func main() { // Set the router as the default one shipped with Gin router := gin.Default() // Serve frontend static files router.Use(static.Serve("/", static.LocalFile("./views", true))) // Setup route group for the API api := router.Group("/api") { api.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H { "message": "pong", }) }) } // Our API will consit of just two routes // /jokes - which will retrieve a list of jokes a user can see // /jokes/like/:jokeID - which will capture likes sent to a particular joke api.GET("/jokes", JokeHandler) api.POST("/jokes/like/:jokeID", LikeJoke) // Start and run the server router.Run(":3000") } // JokeHandler retrieves a list of available jokes func JokeHandler(c *gin.Context) { c.Header("Content-Type", "application/json") c.JSON(http.StatusOK, gin.H { "message":"Jokes handler not implemented yet", }) } // LikeJoke increments the likes of a particular joke Item func LikeJoke(c *gin.Context) { c.Header("Content-Type", "application/json") c.JSON(http.StatusOK, gin.H { "message":"LikeJoke handler not implemented yet", }) }

Exécutons à nouveau notre application go run main.goet accédons à nos itinéraires. //localhost:3000/api/jokesrenverra une 200 OKréponse d'en-tête, avec le message jokes handler not implemented yet. Une requête POST pour //localhost:3000/api/jokes/like/1renvoyer un en- 200 OKtête et le message Likejoke handler not implemented yet.

Données de blagues

Puisque nous avons déjà notre définition de routes définie, qui ne fait qu'une seule chose (retourner une réponse JSON), nous allons pimenter un peu notre base de code en y ajoutant du code.

// ... leave the code above untouched... // Let's create our Jokes struct. This will contain information about a Joke // Joke contains information about a single Joke type Joke struct { ID int `json:"id" binding:"required"` Likes int `json:"likes"` Joke string `json:"joke" binding:"required"` } // We'll create a list of jokes var jokes = []Joke{ Joke{1, 0, "Did you hear about the restaurant on the moon? Great food, no atmosphere."}, Joke{2, 0, "What do you call a fake noodle? An Impasta."}, Joke{3, 0, "How many apples grow on a tree? All of them."}, Joke{4, 0, "Want to hear a joke about paper? Nevermind it's tearable."}, Joke{5, 0, "I just watched a program about beavers. It was the best dam program I've ever seen."}, Joke{6, 0, "Why did the coffee file a police report? It got mugged."}, Joke{7, 0, "How does a penguin build it's house? Igloos it together."}, } func main() { // ... leave this block untouched... } // JokeHandler retrieves a list of available jokes func JokeHandler(c *gin.Context) { c.Header("Content-Type", "application/json") c.JSON(http.StatusOK, jokes) } // LikeJoke increments the likes of a particular joke Item func LikeJoke(c *gin.Context) { // confirm Joke ID sent is valid // remember to import the `strconv` package if jokeid, err := strconv.Atoi(c.Param("jokeID")); err == nil { // find joke, and increment likes for i := 0; i < len(jokes); i++ { if jokes[i].ID == jokeid { jokes[i].Likes += 1 } } // return a pointer to the updated jokes list c.JSON(http.StatusOK, &jokes) } else { // Joke ID is invalid c.AbortWithStatus(http.StatusNotFound) } } // NB: Replace the JokeHandler and LikeJoke functions in the previous version to the ones above

Avec notre code en bon état, allons-y et testons notre API. Nous pouvons tester avec cURLou postman, puis envoyer une GETdemande à //localhost:3000/jokespour obtenir la liste complète des blagues, et une POSTdemande //localhost:3000/jokes/like/{jokeid}pour incrémenter les goûts d'une blague.

$ curl //localhost:3000/api/jokes $ curl -X POST //localhost:3000/api/jokes/like/4

Construire l'interface utilisateur (React)

Nous avons notre API en place, alors créons une interface pour présenter les données de notre API. Pour cela, nous utiliserons React. Nous n'entrerons pas trop profondément dans React, car cela sortira du cadre de ce tutoriel. Si vous avez besoin d'en savoir plus sur React, consultez le didacticiel officiel. Vous pouvez implémenter l'interface utilisateur avec n'importe quel framework frontend avec lequel vous êtes à l'aise.

Installer

Nous éditerons le index.htmlfichier pour ajouter les bibliothèques externes nécessaires pour exécuter React. Ensuite, nous devrons créer un app.jsxfichier dans le views/jsrépertoire, qui contiendra notre code React.

Notre index.htmlfichier devrait ressembler à ceci:

     Jokeish App 

Construire nos composants

Dans React, les vues sont décomposées en composants. Nous devrons construire quelques composants:

  • un Appcomposant comme entrée principale qui lance l'application
  • un Homecomposant qui fera face à des utilisateurs non connectés
  • un LoggedIncomposant dont le contenu n'est visible que par les utilisateurs authentifiés
  • et un Jokecomposant pour afficher une liste de blagues.

Nous écrirons tous ces composants dans le app.jsxfichier.

Le composant app

Ce composant démarre toute notre application React. Il décide quel composant afficher en fonction du fait qu'un utilisateur est authentifié ou non. Nous allons commencer avec juste sa base, et la mettre à jour plus tard avec plus de fonctionnalités.

class App extends React.Component { render() { if (this.loggedIn) { return (); } else { return (); } } }

Le composant Home

Ce composant est présenté aux utilisateurs non connectés, avec un bouton qui ouvre un écran de verrouillage hébergé où ils peuvent s'inscrire ou se connecter. Nous ajouterons cette fonctionnalité plus tard.

class Home extends React.Component { render() { return ( 

Jokeish

A load of Dad jokes XD

Sign in to get access

Sign In ) } }

Composant LoggedIn

Ce composant s'affiche lorsqu'un utilisateur est authentifié. Il stocke dans son stateun tableau de blagues qui est rempli lorsque le composant se monte.

class LoggedIn extends React.Component { constructor(props) { super(props); this.state = { jokes: [] } } render() { return (

Log out

Jokeish

Let's feed you with some funny Jokes!!!

{this.state.jokes.map(function(joke, i){ return (); })} ) } }

Le composant Joke

Le Jokecomposant contiendra des informations sur chaque élément de la réponse de blagues à afficher.

class Joke extends React.Component { constructor(props) { super(props); this.state = { liked: "" } this.like = this.like.bind(this); } like() { // ... we'll add this block later } render() { return ( #{this.props.joke.id} {this.state.liked} {this.props.joke.joke} {this.props.joke.likes} Likes ) } }

Nous avons écrit nos composants, alors disons maintenant à React où rendre l'application. Nous ajouterons le bloc de code ci-dessous au bas de notre app.jsxfichier.

ReactDOM.render(, document.getElementById('app'));

Redémarrons notre serveur Go go run main.goet dirigons-nous vers l'URL de notre application //localhost:3000/. Vous verrez que le Homecomposant est en cours de rendu.

Sécuriser notre application blagues avec Auth0

Auth0 émet des jetons Web JSON à chaque connexion pour vos utilisateurs. Cela signifie que vous pouvez disposer d'une infrastructure d'identité solide, y compris l'authentification unique, la gestion des utilisateurs, la prise en charge des fournisseurs d'identité sociale (Facebook, Github, Twitter, etc.), des fournisseurs d'identité d'entreprise (Active Directory, LDAP, SAML, etc.) et votre propre base de données d'utilisateurs, avec seulement quelques lignes de code.

Nous pouvons facilement configurer l'authentification dans notre application GIN en utilisant Auth0. Vous aurez besoin d'un compte pour suivre cette partie. Si vous n'avez pas encore de compte Auth0, créez-en un maintenant.

Avertissement: ce n'est pas du contenu sponsorisé.

Création du client API

Nos jetons seront générés avec Auth0, nous devons donc créer une API et un client à partir de notre tableau de bord Auth0. Encore une fois, si vous ne l'avez pas déjà fait, créez un compte Auth0.

Pour créer une nouvelle API, accédez à la section API de votre tableau de bord, puis cliquez sur le bouton Créer une API .

Choisissez un nom d' API et un identifiant . L'identifiant sera le public du middleware. L' algorithme de signature doit être RS256 .

Pour créer un nouveau client, accédez à la section clients de votre tableau de bord et cliquez sur le bouton Créer un client . Sélectionnez le type Regular Web Applications.

Une fois le client créé, notez le client_idet client_secret, car nous en aurons besoin plus tard.

Nous devons ajouter les informations d'identification nécessaires pour notre API à une variable d'environnement. Dans le répertoire racine, créez un nouveau fichier .envet ajoutez-y les éléments suivants, avec les détails du tableau de bord Auth0:

export export export AUTH0_DOMAIN="yourdomain.auth0.com" export

Sécuriser nos points de terminaison d'API

Actuellement, notre API est ouverte sur le monde. Nous devons sécuriser nos terminaux afin que seuls les utilisateurs autorisés puissent y accéder.

Nous allons utiliser un middleware JWT pour vérifier un jeton Web JSON valide à partir de chaque demande atteignant nos points de terminaison.

Créons notre middleware:

// ... var jwtMiddleWare *jwtmiddleware.JWTMiddleware func main() { jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{ ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { aud := os.Getenv("AUTH0_API_AUDIENCE") checkAudience := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false) if !checkAudience { return token, errors.New("Invalid audience.") } // verify iss claim iss := os.Getenv("AUTH0_DOMAIN") checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false) if !checkIss { return token, errors.New("Invalid issuer.") } cert, err := getPemCert(token) if err != nil { log.Fatalf("could not get cert: %+v", err) } result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert)) return result, nil }, SigningMethod: jwt.SigningMethodRS256, }) // register our actual jwtMiddleware jwtMiddleWare = jwtMiddleware // ... the rest of the code below this function doesn't change yet } // authMiddleware intercepts the requests, and check for a valid jwt token func authMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // Get the client secret key err := jwtMiddleWare.CheckJWT(c.Writer, c.Request) if err != nil { // Token not found fmt.Println(err) c.Abort() c.Writer.WriteHeader(http.StatusUnauthorized) c.Writer.Write([]byte("Unauthorized")) return } } }

Dans le code ci-dessus, nous avons une nouvelle jwtMiddleWarevariable qui est initialisée dans la mainfonction. Il est utilisé dans la authMiddlewarefonction intermédiaire.

Si vous remarquez, nous tirons nos informations d'identification côté serveur d'une variable d'environnement (l'un des principes d'une application à 12 facteurs ). Notre middleware vérifie et reçoit un jeton d'une requête et appelle la jwtMiddleWare.CheckJWTméthode pour valider le jeton envoyé.

Écrivons également la fonction pour renvoyer les clés Web JSON:

// ... the code above is untouched... // Jwks stores a slice of JSON Web Keys type Jwks struct { Keys []JSONWebKeys `json:"keys"` } type JSONWebKeys struct { Kty string `json:"kty"` Kid string `json:"kid"` Use string `json:"use"` N string `json:"n"` E string `json:"e"` X5c []string `json:"x5c"` } func main() { // ... the code in this method is untouched... } func getPemCert(token *jwt.Token) (string, error) { cert := "" resp, err := http.Get(os.Getenv("AUTH0_DOMAIN") + ".well-known/jwks.json") if err != nil { return cert, err } defer resp.Body.Close() var jwks = Jwks{} err = json.NewDecoder(resp.Body).Decode(&jwks) if err != nil { return cert, err } x5c := jwks.Keys[0].X5c for k, v := range x5c { if token.Header["kid"] == jwks.Keys[k].Kid { cert = "-----BEGIN CERTIFICATE-----\n" + v + "\n-----END CERTIFICATE-----" } } if cert == "" { return cert, errors.New("unable to find appropriate key.") } return cert, nil }

Utilisation du middleware JWT

L'utilisation du middleware est très simple. Nous le passons simplement comme paramètre à la définition de nos routes.

... api.GET("/jokes", authMiddleware(), JokeHandler) api.POST("/jokes/like/:jokeID", authMiddleware(), LikeJoke) ...

Notre main.gofichier devrait ressembler à ceci:

package main import ( "encoding/json" "errors" "fmt" "log" "net/http" "os" "strconv" jwtmiddleware "github.com/auth0/go-jwt-middleware" jwt "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/contrib/static" "github.com/gin-gonic/gin" ) type Response struct { Message string `json:"message"` } type Jwks struct { Keys []JSONWebKeys `json:"keys"` } type JSONWebKeys struct { Kty string `json:"kty"` Kid string `json:"kid"` Use string `json:"use"` N string `json:"n"` E string `json:"e"` X5c []string `json:"x5c"` } type Joke struct { ID int `json:"id" binding:"required"` Likes int `json:"likes"` Joke string `json:"joke" binding:"required"` } /** we'll create a list of jokes */ var jokes = []Joke{ Joke{1, 0, "Did you hear about the restaurant on the moon? Great food, no atmosphere."}, Joke{2, 0, "What do you call a fake noodle? An Impasta."}, Joke{3, 0, "How many apples grow on a tree? All of them."}, Joke{4, 0, "Want to hear a joke about paper? Nevermind it's tearable."}, Joke{5, 0, "I just watched a program about beavers. It was the best dam program I've ever seen."}, Joke{6, 0, "Why did the coffee file a police report? It got mugged."}, Joke{7, 0, "How does a penguin build it's house? Igloos it together."}, } var jwtMiddleWare *jwtmiddleware.JWTMiddleware func main() { jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{ ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { aud := os.Getenv("AUTH0_API_AUDIENCE") checkAudience := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false) if !checkAudience { return token, errors.New("Invalid audience.") } // verify iss claim iss := os.Getenv("AUTH0_DOMAIN") checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false) if !checkIss { return token, errors.New("Invalid issuer.") } cert, err := getPemCert(token) if err != nil { log.Fatalf("could not get cert: %+v", err) } result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert)) return result, nil }, SigningMethod: jwt.SigningMethodRS256, }) jwtMiddleWare = jwtMiddleware // Set the router as the default one shipped with Gin router := gin.Default() // Serve the frontend router.Use(static.Serve("/", static.LocalFile("./views", true))) api := router.Group("/api") { api.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "pong", }) }) api.GET("/jokes", authMiddleware(), JokeHandler) api.POST("/jokes/like/:jokeID", authMiddleware(), LikeJoke) } // Start the app router.Run(":3000") } func getPemCert(token *jwt.Token) (string, error) { cert := "" resp, err := http.Get(os.Getenv("AUTH0_DOMAIN") + ".well-known/jwks.json") if err != nil { return cert, err } defer resp.Body.Close() var jwks = Jwks{} err = json.NewDecoder(resp.Body).Decode(&jwks) if err != nil { return cert, err } x5c := jwks.Keys[0].X5c for k, v := range x5c { if token.Header["kid"] == jwks.Keys[k].Kid { cert = "-----BEGIN CERTIFICATE-----\n" + v + "\n-----END CERTIFICATE-----" } } if cert == "" { return cert, errors.New("unable to find appropriate key") } return cert, nil } // authMiddleware intercepts the requests, and check for a valid jwt token func authMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // Get the client secret key err := jwtMiddleWare.CheckJWT(c.Writer, c.Request) if err != nil { // Token not found fmt.Println(err) c.Abort() c.Writer.WriteHeader(http.StatusUnauthorized) c.Writer.Write([]byte("Unauthorized")) return } } } // JokeHandler returns a list of jokes available (in memory) func JokeHandler(c *gin.Context) { c.Header("Content-Type", "application/json") c.JSON(http.StatusOK, jokes) } func LikeJoke(c *gin.Context) { // Check joke ID is valid if jokeid, err := strconv.Atoi(c.Param("jokeID")); err == nil { // find joke and increment likes for i := 0; i < len(jokes); i++ { if jokes[i].ID == jokeid { jokes[i].Likes = jokes[i].Likes + 1 } } c.JSON(http.StatusOK, &jokes) } else { // the jokes ID is invalid c.AbortWithStatus(http.StatusNotFound) } }

Installons les jwtmiddlewarebibliothèques:

$ go get -u github.com/auth0/go-jwt-middleware $ go get -u github.com/dgrijalva/jwt-go

Provoquons notre fichier d'environnement et redémarrons notre serveur d'applications:

$ source .env $ go run main.go

Maintenant, si nous essayons d'accéder à l'un des points de terminaison, nous serons confrontés à une 401 Unauthorizederreur. C'est parce que nous devons envoyer un jeton avec la demande.

Connectez-vous avec Auth0 et React

Implémentons un système de connexion afin que les utilisateurs puissent se connecter ou créer des comptes et avoir accès à nos blagues. Nous ajouterons à notre app.jsxfichier les informations d'identification Auth0 suivantes:

  • AUTH0_CLIENT_ID
  • AUTH0_DOMAIN
  • AUTH0_CALLBACK_URL - L'URL de votre application
  • AUTH0_API_AUDIENCE
Vous pouvez trouver AUTH0_CLIENT_ID, AUTH0_DOMAINet les AUTH0_API_AUDIENCEdonnées de votre tableau de bord de gestion Auth0.

Nous devons définir un vers callbacklequel Auth0 redirige. Accédez à la section Clients de votre tableau de bord. Dans les paramètres, définissons le rappel sur //localhost:3000:

Une fois les informations d'identification en place, mettons à jour nos composants React.

Composant APP

const AUTH0_CLIENT_ID = "aIAOt9fkMZKrNsSsFqbKj5KTI0ObTDPP"; const AUTH0_DOMAIN = "hakaselabs.auth0.com"; const AUTH0_CALLBACK_URL = location.href; const AUTH0_API_AUDIENCE = "golang-gin"; class App extends React.Component { parseHash() { this.auth0 = new auth0.WebAuth({ domain: AUTH0_DOMAIN, clientID: AUTH0_CLIENT_ID }); this.auth0.parseHash(window.location.hash, (err, authResult) => { if (err) { return console.log(err); } if ( authResult !== null && authResult.accessToken !== null && authResult.idToken !== null ) { localStorage.setItem("access_token", authResult.accessToken); localStorage.setItem("id_token", authResult.idToken); localStorage.setItem( "profile", JSON.stringify(authResult.idTokenPayload) ); window.location = window.location.href.substr( 0, window.location.href.indexOf("#") ); } }); } setup() { $.ajaxSetup({ beforeSend: (r) => { if (localStorage.getItem("access_token")) { r.setRequestHeader( "Authorization", "Bearer " + localStorage.getItem("access_token") ); } } }); } setState() { let idToken = localStorage.getItem("id_token"); if (idToken) { this.loggedIn = true; } else { this.loggedIn = false; } } componentWillMount() { this.setup(); this.parseHash(); this.setState(); } render() { if (this.loggedIn) { return ; } return ; } }

Nous mis à jour le composant App avec trois méthodes composants ( setup, parseHashet setState), et une méthode du cycle de vie componentWillMount. La parseHashméthode initialise le auth0webAuthclient et analyse le hachage dans un format plus lisible, en les enregistrant dans localSt. Pour afficher l'écran de verrouillage, capturez et stockez le jeton utilisateur et ajoutez l'en-tête d'autorisation correct à toutes les demandes à notre API

Composant maison

Notre composant Accueil sera mis à jour. Nous ajouterons la fonctionnalité de la authenticateméthode, qui déclenchera l'affichage de l'écran de verrouillage hébergé et permettra à nos utilisateurs de se connecter ou de s'inscrire.

class Home extends React.Component { constructor(props) { super(props); this.authenticate = this.authenticate.bind(this); } authenticate() { this.WebAuth = new auth0.WebAuth({ domain: AUTH0_DOMAIN, clientID: AUTH0_CLIENT_ID, scope: "openid profile", audience: AUTH0_API_AUDIENCE, responseType: "token id_token", redirectUri: AUTH0_CALLBACK_URL }); this.WebAuth.authorize(); } render() { return ( 

Jokeish

A load of Dad jokes XD

Sign in to get access

Sign In ); } }

Composant LoggedIn

Nous mettrons à jour le LoggedIncomposant pour communiquer avec notre API et extraire toutes les blagues. Il transmettra chaque blague propau Jokecomposant, qui restituera un panneau de bootstrap. Écrivons ceux-ci:

class LoggedIn extends React.Component { constructor(props) { super(props); this.state = { jokes: [] }; this.serverRequest = this.serverRequest.bind(this); this.logout = this.logout.bind(this); } logout() { localStorage.removeItem("id_token"); localStorage.removeItem("access_token"); localStorage.removeItem("profile"); location.reload(); } serverRequest() { $.get("//localhost:3000/api/jokes", res => { this.setState({ jokes: res }); }); } componentDidMount() { this.serverRequest(); } render() { return (

Log out

Jokeish

Let's feed you with some funny Jokes!!!

{this.state.jokes.map(function(joke, i) { return ; })} ); } }

Composant blague

Nous mettrons également à jour le Jokecomposant pour formater chaque élément Joke qui lui est passé depuis le composant Parent ( LoggedIn). Nous ajouterons également une likeméthode qui incrémentera les goûts d'une blague.

class Joke extends React.Component { constructor(props) { super(props); this.state = { liked: "", jokes: [] }; this.like = this.like.bind(this); this.serverRequest = this.serverRequest.bind(this); } like() { let joke = this.props.joke; this.serverRequest(joke); } serverRequest(joke) { $.post( "//localhost:3000/api/jokes/like/" + joke.id, { like: 1 }, res => { console.log("res... ", res); this.setState({ liked: "Liked!", jokes: res }); this.props.jokes = res; } ); } render() { return ( #{this.props.joke.id}{" "} {this.state.liked} {this.props.joke.joke} {this.props.joke.likes} Likes ) } }

Mettre tous ensemble

Une fois l'interface utilisateur et l'API terminées, nous pouvons tester notre application. Nous commencerons par démarrer notre serveur source .env && go run main.go, puis nous naviguerons à //localhost:3000partir de n'importe quel navigateur. Vous devriez voir le Homecomposant avec un bouton de connexion. Cliquez sur le bouton de connexion pour rediriger vers une page de verrouillage hébergée (créer un compte ou vous connecter) pour continuer à utiliser l'application.

Accueil:

Écran de verrouillage hébergé par Auth0:

Vue de l'application LoggedIn:

Conclusion

Félicitations! Vous avez appris à créer une application et une API avec Go et le framework Gin.

Ai-je manqué quelque chose d'important? Faites-le moi savoir dans les commentaires.

Vous pouvez me dire bonjour sur Twitter @codehakase