Come creare un'app Web con Go, Gin e React

Questo articolo è stato originariamente pubblicato sul mio blog

TL; DR: In questo tutorial, ti mostrerò quanto è facile creare un'applicazione web con Go e il framework Gin e aggiungervi l'autenticazione. Controlla il repository Github per il codice che scriveremo.

Il gin è un micro-framework ad alte prestazioni. Fornisce un framework molto minimalista che porta con sé solo le caratteristiche, le librerie e le funzionalità più essenziali necessarie per creare applicazioni Web e microservizi. Rende semplice costruire una pipeline di gestione delle richieste da pezzi modulari e riutilizzabili. Lo fa consentendo di scrivere middleware che può essere collegato a uno o più gestori di richieste o gruppi di gestori di richieste.

Caratteristiche del gin

Gin è un framework web veloce, semplice ma completo e molto efficiente per Go. Dai un'occhiata ad alcune delle funzionalità seguenti che lo rendono un quadro degno da considerare per il tuo prossimo progetto Golang.

  • Velocità: Gin è costruito per la velocità. Il framework offre un routing basato su albero Radix e un ridotto ingombro di memoria. Nessun riflesso. Prestazioni API prevedibili.
  • Crash-Free : Gin ha la capacità di rilevare arresti anomali o panico durante il runtime e può ripristinarsi da essi. In questo modo la tua applicazione sarà sempre disponibile.
  • Routing: Gin fornisce un'interfaccia di routing per consentirti di esprimere come dovrebbero apparire la tua applicazione web o le route API.
  • Convalida JSON: Gin può analizzare e convalidare facilmente le richieste JSON, verificando l'esistenza dei valori richiesti.
  • Gestione degli errori: Gin fornisce un modo conveniente per raccogliere tutti gli errori verificatisi durante una richiesta HTTP. Alla fine, un middleware può scriverli in un file di registro o in un database e inviarli attraverso la rete.
  • Rendering integrato: Gin fornisce un'API facile da usare per il rendering JSON, XML e HTML.

Prerequisiti

Per seguire questo tutorial, dovrai avere Go installato sulla tua macchina, un browser web per visualizzare l'app e una riga di comando per eseguire i comandi di build.

Go, o come si chiama normalmente Golang , è un linguaggio di programmazione sviluppato da Google per la creazione di software moderno. Go è un linguaggio progettato per fare cose in modo efficiente e rapido. I principali vantaggi di Go includono:

  • Fortemente dattiloscritta e raccolta dei rifiuti
  • Tempi di compilazione rapidissimi
  • Concorrenza integrata
  • Ampia libreria standard

Vai alla sezione dei download del sito Web Go per far funzionare Go sul tuo computer.

Costruire un'app con Gin

Costruiremo una semplice app per elenchi di scherzi con Gin . La nostra app elencherà alcune sciocche battute di papà. Aggiungeremo l'autenticazione, in modo che tutti gli utenti registrati abbiano il privilegio di mettere mi piace e visualizzare le barzellette.

Questo ci permetterà di illustrare come Gin può essere utilizzato per sviluppare applicazioni web e / o API.

Utilizzeremo le seguenti funzionalità offerte da Gin:

  • Middleware
  • Routing
  • Raggruppamento di percorsi

Pronti, partenza, via

Scriveremo l'intera applicazione Go in un main.gofile. Poiché si tratta di una piccola applicazione, sarà facile creare l'applicazione solo go rundal terminale.

Creeremo una nuova directory golang-ginnel nostro spazio di lavoro Go e quindi un main.gofile al suo interno:

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

Il contenuto del main.gofile:

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") }

Avremo bisogno di creare altre directory per i nostri file statici. Nella stessa directory del main.gofile, creiamo una viewscartella. Nella viewscartella, crea una jscartella e un index.htmlfile al suo interno.

Il index.htmlfile per ora sarà molto semplice:

   Jokeish App   

Welcome to the Jokeish App

Prima di testare ciò che abbiamo finora, installiamo le dipendenze aggiunte:

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

Per vedere cosa funziona, avremo bisogno di avviare il nostro server eseguendo go run main.go.

Una volta che l'applicazione è in esecuzione, vai a //localhost:3000nel tuo browser. Se tutto è andato bene, dovresti vedere il testo dell'intestazione di livello 1 Welcome to the Jokeish App visualizzato.

Definizione dell'API

Aggiungiamo altro codice nel nostro main.gofile per le nostre definizioni API. Aggiorneremo la nostra mainfunzione con due percorsi /jokes/e /jokes/like/:jokeIDal gruppo di percorsi /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", }) }

Il contenuto del main.gofile dovrebbe essere simile a questo:

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", }) }

Eseguiamo di nuovo la nostra app go run main.goe accediamo ai nostri percorsi. //localhost:3000/api/jokesrestituirà una 200 OKrisposta di intestazione, con il messaggio jokes handler not implemented yet. Una richiesta POST per //localhost:3000/api/jokes/like/1restituire 200 OKun'intestazione e il messaggio Likejoke handler not implemented yet.

Dati di scherzi

Poiché abbiamo già impostato la definizione delle rotte, che fa solo una cosa (restituire una risposta JSON), renderemo un po 'più piccante la nostra base di codice aggiungendovi altro codice.

// ... 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

Con il nostro codice che sembra buono, andiamo avanti e testiamo la nostra API. Possiamo testare con cURLo postman, quindi inviare una GETrichiesta a //localhost:3000/jokesper ottenere l'elenco completo delle barzellette e una POSTrichiesta //localhost:3000/jokes/like/{jokeid}per incrementare quelli del calibro di uno scherzo.

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

Costruire l'interfaccia utente (React)

Abbiamo la nostra API in atto, quindi creiamo un frontend per presentare i dati dalla nostra API. Per questo, useremo React. Non andremo troppo in profondità in React, poiché sarà fuori dallo scopo di questo tutorial. Se hai bisogno di saperne di più su React, dai un'occhiata al tutorial ufficiale. Puoi implementare l'interfaccia utente con qualsiasi framework di frontend con cui ti senti a tuo agio.

Impostare

Modificheremo il index.htmlfile per aggiungere le librerie esterne necessarie per eseguire React. Quindi dovremo creare un app.jsxfile nella views/jsdirectory, che conterrà il nostro codice React.

Il nostro index.htmlfile dovrebbe assomigliare a questo:

     Jokeish App 

Costruire i nostri componenti

In React, le visualizzazioni sono suddivise in componenti. Avremo bisogno di costruire alcuni componenti:

  • un Appcomponente come voce principale che avvia l'applicazione
  • un Homecomponente che affronterà gli utenti non registrati
  • un LoggedIncomponente con contenuto visibile solo da utenti autenticati
  • e un Jokecomponente per visualizzare un elenco di barzellette.

Scriveremo tutti questi componenti nel app.jsxfile.

Il componente dell'app

Questo componente avvia la nostra intera app React. Decide quale componente mostrare in base al fatto che un utente sia autenticato o meno. Inizieremo solo con la sua base e in seguito lo aggiorneremo con più funzionalità.

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

Il componente Home

Questo componente viene mostrato agli utenti che non hanno effettuato l'accesso, insieme a un pulsante che apre una schermata di blocco ospitata in cui possono registrarsi o accedere. Aggiungeremo questa funzionalità in seguito.

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

Jokeish

A load of Dad jokes XD

Sign in to get access

Sign In ) } }

Componente LoggedIn

Questo componente viene visualizzato quando un utente viene autenticato. Memorizza nella sua stateuna serie di battute che viene popolata quando il componente viene montato.

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 (); })} ) } }

Il componente Joke

Il Jokecomponente conterrà informazioni su ciascun elemento dalla risposta alle battute da visualizzare.

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 ) } }

Abbiamo scritto i nostri componenti, quindi ora diciamo a React dove eseguire il rendering dell'app. Aggiungeremo il blocco di codice di seguito in fondo al nostro app.jsxfile.

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

Riavvia il nostro server Go go run main.goe vai all'URL della nostra app //localhost:3000/. Vedrai che il Homecomponente è in fase di rendering.

Proteggere la nostra app per scherzi con Auth0

Auth0 emette token Web JSON a ogni accesso per i tuoi utenti. Ciò significa che puoi disporre di una solida infrastruttura di identità, inclusi Single Sign-On, gestione degli utenti, supporto per provider di identità social (Facebook, Github, Twitter, ecc.), Provider di identità aziendali (Active Directory, LDAP, SAML, ecc.) e il tuo database di utenti, con solo poche righe di codice.

Possiamo facilmente impostare l'autenticazione nella nostra app GIN utilizzando Auth0. Avrai bisogno di un account per seguire questa parte. Se non hai già un account Auth0, creane uno adesso.

Disclaimer: questo non è un contenuto sponsorizzato.

Creazione del client API

I nostri token verranno generati con Auth0, quindi dobbiamo creare un'API e un client dalla nostra dashboard Auth0. Ancora una volta, se non l'hai già fatto, registrati per un account Auth0.

Per creare una nuova API, vai alla sezione API nella dashboard e fai clic sul pulsante Crea API .

Scegli un nome API e un identificatore . L'identificatore sarà il pubblico del middleware. L' algoritmo di firma dovrebbe essere RS256 .

Per creare un nuovo cliente, vai alla sezione clienti nella dashboard e fai clic sul pulsante Crea cliente . Seleziona il tipo Regular Web Applications.

Una volta creato il client, prendi nota di client_ide client_secret, poiché ne avremo bisogno in seguito.

Dobbiamo aggiungere le credenziali necessarie per la nostra API a una variabile di ambiente. Nella directory principale, crea un nuovo file .enve aggiungi quanto segue, con i dettagli dalla dashboard di Auth0:

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

Protezione dei nostri endpoint API

Attualmente, la nostra API è aperta al mondo. Dobbiamo proteggere i nostri endpoint, quindi solo gli utenti autorizzati possono accedervi.

Utilizzeremo un middleware JWT per verificare la presenza di un token Web JSON valido da ogni richiesta che raggiunge i nostri endpoint.

Creiamo il nostro 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 } } }

Nel codice sopra, abbiamo una nuova jwtMiddleWarevariabile che viene inizializzata nella mainfunzione. È usato nella authMiddlewarefunzione centrale.

Se noti, stiamo estraendo le nostre credenziali lato server da una variabile di ambiente (uno dei principi di un'app a 12 fattori ). Il nostro middleware controlla e riceve un token da una richiesta e chiama il jwtMiddleWare.CheckJWTmetodo per convalidare il token inviato.

Scriviamo anche la funzione per restituire le chiavi 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 }

Utilizzo del middleware JWT

L'utilizzo del middleware è molto semplice. Lo passiamo come parametro alla nostra definizione di rotte.

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

Il nostro main.gofile dovrebbe assomigliare a questo:

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) } }

Installiamo le jwtmiddlewarelibrerie:

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

Proviamo il nostro file di ambiente e riavvia il nostro server app:

$ source .env $ go run main.go

Ora, se proviamo ad accedere a uno qualsiasi degli endpoint, ci troveremo di fronte a un 401 Unauthorizederrore. Questo perché dobbiamo inviare un token con la richiesta.

Accedi con Auth0 e React

Implementiamo un sistema di accesso in modo che gli utenti possano accedere o creare account e ottenere l'accesso alle nostre battute. Aggiungeremo al nostro app.jsxfile le seguenti credenziali Auth0:

  • AUTH0_CLIENT_ID
  • AUTH0_DOMAIN
  • AUTH0_CALLBACK_URL - L'URL della tua app
  • AUTH0_API_AUDIENCE
È possibile trovare il AUTH0_CLIENT_ID, AUTH0_DOMAINe AUTH0_API_AUDIENCEdati dal Dashboard di gestione Auth0.

Dobbiamo impostare a callbackcui Auth0 reindirizza. Vai alla sezione Clienti nella tua dashboard. Nelle impostazioni, impostiamo la richiamata su //localhost:3000:

Con le credenziali in atto, aggiorniamo i nostri componenti React.

Componente 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 ; } }

Abbiamo aggiornato la componente App con tre metodi componenti ( setup, parseHash, e setState), e un metodo del ciclo di vita componentWillMount. Il parseHashmetodo inizializza il auth0webAuthclient e analizza l'hash in un formato più leggibile, salvandolo in localSt. Per mostrare la schermata di blocco, acquisire e memorizzare il token utente e aggiungere l'intestazione di autorizzazione corretta a qualsiasi richiesta alla nostra API

Componente domestico

Il nostro componente Home verrà aggiornato. Aggiungeremo la funzionalità per il authenticatemetodo, che attiverà la schermata di blocco ospitata per visualizzare e consentire ai nostri utenti di accedere o registrarsi.

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 ); } }

Componente LoggedIn

Aggiorneremo il LoggedIncomponente per comunicare con la nostra API e tirare fuori tutte le battute. Trasmetterà ogni battuta come una propal Jokecomponente, che rende un pannello di bootstrap. Scriviamo quelli:

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 ; })} ); } }

Componente di scherzo

Aggiorneremo anche il Jokecomponente per formattare ogni elemento Joke passato ad esso dal componente principale ( LoggedIn). Aggiungeremo anche un likemetodo, che incrementerà artisti del calibro di uno scherzo.

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 ) } }

Mettere tutto insieme

Con l'interfaccia utente e l'API complete, possiamo testare la nostra app. Inizieremo avviando il nostro server source .env && go run main.go, quindi navigheremo //localhost:3000da qualsiasi browser. Dovresti vedere il Homecomponente con un pulsante di accesso. Facendo clic sul pulsante di accesso verrà reindirizzato a una pagina di blocco ospitata (creare un account o accedere) per continuare a utilizzare l'applicazione.

Casa:

Schermata di blocco ospitata da Auth0:

Accesso alla visualizzazione dell'app:

Conclusione

Congratulazioni! Hai imparato come creare un'applicazione e un'API con Go e il framework Gin.

Mi sono perso qualcosa di importante? Fatemelo sapere nei commenti.

Puoi salutarmi su Twitter @codehakase