Sådan oprettes en webapp med Go, Gin og React

TL; DR: I denne vejledning viser jeg dig, hvor let det er at oprette en webapplikation med Go og Gin-rammen og tilføje godkendelse til den. Tjek Github-repoen for den kode, vi skal skrive.
Gin er en højtydende mikroramme. Det leverer en meget minimalistisk ramme, der kun indeholder de mest væsentlige funktioner, biblioteker og funktioner, der er nødvendige for at opbygge webapplikationer og mikrotjenester. Det gør det nemt at oprette en anmodningshåndteringsrørledning fra modulære, genanvendelige stykker. Det gør det ved at lade dig skrive middleware, der kan tilsluttes en eller flere anmodningshåndterere eller grupper af anmodningshåndterere.
Gin-funktioner
Gin er en hurtig, enkel men alligevel fuldt udstyret og meget effektiv webramme til Go. Tjek nogle af nedenstående funktioner, der gør det til en værdig ramme at overveje til dit næste Golang-projekt.
- Hastighed: Gin er bygget til hastighed. Rammen tilbyder en Radix-træbaseret routing og et lille hukommelsesfodaftryk. Ingen refleksion. Forudsigelig API-ydeevne.
- Crash-Free : Gin har evnen til at fange nedbrud eller panik under kørsel og kan komme sig efter dem. På denne måde vil din ansøgning altid være tilgængelig.
- Routing: Gin giver en routing-interface, der giver dig mulighed for at udtrykke, hvordan din webapplikation eller API-ruter skal se ud.
- JSON-validering: Gin kan let analysere og validere JSON-anmodninger og kontrollere for eksistensen af krævede værdier.
- Fejladministration: Gin giver en bekvem måde at indsamle alle de fejl, der opstod under en HTTP-anmodning. Til sidst kan en middleware skrive dem til en logfil eller til en database og sende dem gennem netværket.
- Indbygget gengivelse: Gin giver en brugervenlig API til JSON-, XML- og HTML-gengivelse.
Forudsætninger
For at følge denne vejledning skal du have Go installeret på din maskine, en webbrowser for at se appen og en kommandolinje til at udføre build-kommandoer.
Go, eller som det normalt kaldes Golang , er et programmeringssprog udviklet af Google til opbygning af moderne software. Go er et sprog designet til at få ting gjort effektivt og hurtigt. De vigtigste fordele ved Go inkluderer:
- Stærkt skrevet og affald indsamlet
- Flammende hurtige kompileringstider
- Samtidig indbygget
- Omfattende standardbibliotek
Gå over til downloads-sektionen på Go-webstedet for at komme i gang på din maskine.
Opbygning af en app med Gin
Vi bygger en simpel vittighedsapp med Gin . Vores app viser nogle fjollede farvittigheder. Vi vil tilføje godkendelse til det, så alle indloggede brugere har privilegiet til at kunne lide og se vittigheder.
Dette giver os mulighed for at illustrere, hvordan Gin kan bruges til at udvikle webapplikationer og / eller API'er.

Vi bruger følgende funktioner, der tilbydes af Gin:
- Middleware
- Routing
- Ruter gruppering
Klar parat start
Vi skriver hele vores Go-applikation i en main.go
fil. Da det er en lille applikation, bliver det let at oprette applikationen med lige go run
fra terminalen.
Vi opretter en ny mappe golang-gin
i vores Go-arbejdsområde og derefter en main.go
fil i det:
$ mkdir -p $GOPATH/src/github.com/user/golang-gin $ cd $GOPATH/src/github.com/user/golang-gin $ touch main.go
Indholdet af main.go
filen:
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") }
Vi bliver nødt til at oprette nogle flere mapper til vores statiske filer. main.go
Lad os oprette en views
mappe i samme bibliotek som filen . I views
mappen, skal du oprette en js
mappe og en index.html
fil i det.
Den index.html
fil vil være meget simpelt for nu:
Jokeish App Welcome to the Jokeish App
Før vi tester, hvad vi hidtil har, lad os installere de tilføjede afhængigheder:
$ go get -u github.com/gin-gonic/gin $ go get -u github.com/gin-gonic/contrib/static
For at se hvad der fungerer, skal vi starte vores server ved at køre go run main.go
.

Når applikationen kører, skal du navigere til //localhost:3000
i din browser. Hvis alt gik godt, skulle du se niveau 1-overskriftstekst Velkommen til Jokeish-appen vist.

Definition af API
Lad os tilføje mere kode i vores main.go
fil til vores API-definitioner. Vi opdaterer vores main
funktion med to ruter /jokes/
og /jokes/like/:jokeID
til rutegruppen /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", }) }
Indholdet af main.go
filen skal se sådan ud:
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", }) }
Lad os køre vores app igen go run main.go
og få adgang til vores ruter. //localhost:3000/api/jokes
returnerer et 200 OK
header-svar med beskeden jokes handler not implemented yet
. En POST-anmodning om at //localhost:3000/api/jokes/like/1
returnere et 200 OK
overskrift og meddelelsen Likejoke handler not implemented yet
.
Vittigheder data
Da vi allerede har vores rutedefinitionssæt, som kun gør en ting (returnerer et JSON-svar), krydrer vi vores codebase lidt ved at tilføje noget mere kode til det.
// ... 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
Når vores kode ser godt ud, lad os gå videre og teste vores API. Vi kan teste med cURL
eller postman
og derefter sende en GET
anmodning om //localhost:3000/jokes
at få den fulde liste over vittigheder og en POST
anmodning om //localhost:3000/jokes/like/{jokeid}
at øge en vits.
$ curl //localhost:3000/api/jokes $ curl -X POST //localhost:3000/api/jokes/like/4
Opbygning af brugergrænsefladen (React)
Vi har vores API på plads, så lad os bygge en frontend til at præsentere dataene fra vores API. Til dette bruger vi React. Vi går ikke for dybt ind i React, da det vil være uden for omfanget af denne tutorial. Hvis du har brug for at lære mere om React, skal du tjekke den officielle tutorial. Du kan implementere brugergrænsefladen med enhver frontend-ramme, du er fortrolig med.
Opsætning
Vi redigerer index.html
filen for at tilføje de eksterne biblioteker, der er nødvendige for at køre React. Så bliver vi nødt til at oprette en app.jsx
fil i views/js
biblioteket, der indeholder vores React-kode.
Vores index.html
fil skal se sådan ud:
Jokeish App
Opbygning af vores komponenter
I React er visninger opdelt i komponenter. Vi bliver nødt til at bygge nogle komponenter:
- en
App
komponent som hovedindgang, der starter applikationen - en
Home
komponent, der står over for ikke-loggede brugere - en
LoggedIn
komponent med indhold, der kun er synligt af godkendte brugere - og en
Joke
komponent, der viser en liste med vittigheder.
Vi skriver alle disse komponenter i app.jsx
filen.
App-komponenten
Denne komponent bootstrapper vores hele React-app. Det bestemmer, hvilken komponent der skal vises baseret på, om en bruger er godkendt eller ej. Vi starter med kun dens base og opdaterer den senere med mere funktionalitet.
class App extends React.Component { render() { if (this.loggedIn) { return (); } else { return (); } } }
Hjemmekomponenten
Denne komponent vises til ikke-loggede brugere sammen med en knap, der åbner en Hosted-låseskærm, hvor de kan tilmelde sig eller logge ind. Vi tilføjer denne funktionalitet senere.
class Home extends React.Component { render() { return ( Jokeish
A load of Dad jokes XD
Sign in to get access
Sign In ) } }
LoggedIn-komponent
Denne komponent vises, når en bruger er godkendt. Det gemmer i state
en række vittigheder, der befolkes, når komponenten monteres.
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 (); })} ) } }
Joke-komponenten
Den Joke
komponent vil indeholde oplysninger om hvert element fra de vittigheder respons, der skal vises.
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 ) } }
Vi har skrevet vores komponenter, så lad os nu fortælle Reager, hvor appen skal gengives. Vi tilføjer blokken kode nedenfor til bunden af vores app.jsx
fil.
ReactDOM.render(, document.getElementById('app'));
Lad os genstarte vores Go-server go run main.go
og gå over til vores apps URL //localhost:3000/
. Du vil se, at Home
komponenten gengives.

Sikring af vores vittighedsapp med Auth0
Auth0 udsteder JSON Web Tokens på hvert login til dine brugere. Dette betyder, at du kan have en solid identitetsinfrastruktur, herunder enkelt login, brugeradministration, support til sociale identitetsudbydere (Facebook, Github, Twitter osv.), Virksomhedsidentitetsudbydere (Active Directory, LDAP, SAML osv.) og din egen database med brugere med kun et par linier kode.
Vi kan nemt konfigurere godkendelse i vores GIN-app ved hjælp af Auth0. Du skal bruge en konto for at følge denne del. Hvis du ikke allerede har en Auth0-konto, kan du tilmelde dig en nu.
Ansvarsfraskrivelse: Dette er ikke sponsoreret indhold.Oprettelse af API-klienten
Vores tokens genereres med Auth0, så vi skal oprette en API og en klient fra vores Auth0-dashboard. Igen, hvis du ikke allerede har gjort det, skal du tilmelde dig en Auth0-konto.
For at oprette en ny API skal du navigere til afsnittet API'er i dit dashboard og klikke på knappen Opret API .

Vælg et API- navn og en identifikator . Identifikatoren vil være publikum for middleware. Den Signing Algoritme bør være RS256 .
For at oprette en ny klient skal du navigere til klientsektionen i dit dashboard og klikke på knappen Opret klient . Vælg typen Regular Web Applications
.

Når klienten er oprettet, skal du være opmærksom på client_id
og client_secret
, da vi har brug for dem senere.

Vi er nødt til at tilføje de legitimationsoplysninger, der er nødvendige for vores API, til en miljøvariabel. Opret en ny fil i rodmappen .env
og tilføj følgende til den med detaljerne fra Auth0-dashboardet:
export export export AUTH0_DOMAIN="yourdomain.auth0.com" export
Sikring af vores API-slutpunkter
I øjeblikket er vores API åben for verden. Vi er nødt til at sikre vores slutpunkter, så kun autoriserede brugere har adgang til dem.
Vi vil bruge en JWT Middleware til at kontrollere, om der er et gyldigt JSON-webtoken fra hver anmodning, der rammer vores slutpunkter.
Lad os oprette vores 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 } } }
I ovenstående kode har vi en ny jwtMiddleWare
variabel, der initialiseres i main
funktionen. Det bruges i den authMiddleware
midterste funktion.
Hvis du bemærker, trækker vi vores serveroplysninger fra en miljøvariabel (en af principperne i en 12-faktor app ). Vores middleware kontrollerer og modtager et token fra en anmodning og kalder jwtMiddleWare.CheckJWT
metoden for at validere det sendte token.
Lad os også skrive funktionen til at returnere JSON Web Keys:
// ... 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 }
Brug af JWT middleware
Brug af middleware er meget ligetil. Vi sender det bare som en parameter til vores rutedefinition.
... api.GET("/jokes", authMiddleware(), JokeHandler) api.POST("/jokes/like/:jokeID", authMiddleware(), LikeJoke) ...
Vores main.go
fil skal se sådan ud:
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) } }
Lad os installere jwtmiddleware
bibliotekerne:
$ go get -u github.com/auth0/go-jwt-middleware $ go get -u github.com/dgrijalva/jwt-go
Lad os kilde vores miljøfil og genstarte vores app-server:
$ source .env $ go run main.go
Hvis vi nu prøver at få adgang til et af slutpunkterne, står vi over for en 401 Unauthorized
fejl. Det er fordi vi skal sende et token sammen med anmodningen.
Log ind med Auth0 og React
Lad os implementere et login-system, så brugerne kan logge ind eller oprette konti og få adgang til vores vittigheder. Vi tilføjer app.jsx
følgende Auth0-legitimationsoplysninger til vores fil:
AUTH0_CLIENT_ID
AUTH0_DOMAIN
AUTH0_CALLBACK_URL
- URL'en til din appAUTH0_API_AUDIENCE
AUTH0_CLIENT_ID
, AUTH0_DOMAIN
og AUTH0_API_AUDIENCE
data fra din Auth0 management instrumentbræt.
Vi er nødt til at indstille en, callback
som Auth0 omdirigerer til. Naviger til sektionen Kunder i dit dashboard. Lad os i indstillingerne indstille tilbagekaldet til //localhost:3000
:

Med legitimationsoplysninger på plads, lad os opdatere vores React-komponenter.
APP-komponent
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 ; } }
Vi har opdateret App komponent med tre komponent metoder ( setup
, parseHash
, og setState
), og en livscyklus metode componentWillMount
. Den parseHash
metode initialiserer auth0
webAuth
klienten og parser hash til en mere læsbart format, gemme dem til localSt. For at vise låseskærmen skal du fange og gemme brugertokenet og føje den korrekte autorisationsoverskrift til eventuelle anmodninger til vores API
Hjemmekomponent
Vores Home-komponent opdateres. Vi tilføjer funktionaliteten til authenticate
metoden, som vil udløse den hostede låseskærm til at blive vist og tillade vores brugere at logge ind eller tilmelde sig.
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 ); } }
LoggedIn-komponent
Vi opdaterer LoggedIn
komponenten til at kommunikere med vores API og trække alle vittigheder. Det videregiver hver joke som en prop
til Joke
komponenten, som gengiver et bootstrap-panel. Lad os skrive dem:
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 ; })} ); } }
Joke-komponent
Vi opdaterer også Joke
komponenten til at formatere hvert Joke-element, der sendes til det fra forældrekomponenten ( LoggedIn
). Vi tilføjer også en like
metode, der øger en joke.
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 ) } }
Samler det hele
Når brugergrænsefladen og API er komplet, kan vi teste vores app. Vi starter med at starte vores server source .env && go run main.go
, og derefter navigerer vi til //localhost:3000
fra enhver browser. Du skal se Home
komponenten med en login-knap. Ved at klikke på loginsknappen omdirigeres til en hostet låseside (opret en konto eller login) for at fortsætte med at bruge applikationen.
Hjem:

Auth0-hostet låseskærm:

LoggedIn App-visning:

Konklusion
Tillykke! Du har lært, hvordan man bygger en applikation og en API med Go og Gin-rammen.
Savnede jeg noget vigtigt? Lad mig vide af det i kommentarerne.
Du kan sige Hej til mig på Twitter @codehakase