Как да създадем уеб приложение с Go, Gin и React

Тази статия първоначално е публикувана в My Blog

TL; DR: В този урок ще ви покажа колко лесно е да създадете уеб приложение с Go и Gin framework и да добавите удостоверяване към него. Вижте репозитория на Github за кода, който ще напишем.

Gin е микро-рамка с висока производителност. Той предоставя много минималистична рамка, която носи със себе си само най-съществените функции, библиотеки и функционалности, необходими за изграждането на уеб приложения и микроуслуги. Улеснява изграждането на тръбопровод за обработка на заявки от модулни части за многократна употреба. Това се прави, като ви позволява да пишете междинен софтуер, който може да бъде включен в един или повече обработващи заявки или групи обработващи заявки.

Джин функции

Gin е бърза, проста, но напълно функционална и много ефективна уеб рамка за Go. Вижте някои от функциите по-долу, които го правят достойна рамка, която да вземете под внимание за следващия си проект Golang.

  • Скорост: Джинът е създаден за скорост. Рамката предлага маршрутизация, базирана на дърво на Radix, и малък отпечатък на паметта. Няма размисъл. Предсказуема ефективност на API.
  • Без сривове: Джин има способността да улавя сривове или паника по време на изпълнение и може да се възстанови от тях. По този начин вашето приложение винаги ще бъде достъпно.
  • Маршрутизация: Gin предоставя интерфейс за маршрутизиране, за да ви позволи да изразите как трябва да изглеждат вашите уеб приложения или API маршрути.
  • JSON проверка: Gin може лесно да анализира и проверява заявките на JSON, като проверява за съществуването на необходимите стойности.
  • Управление на грешки: Gin предоставя удобен начин за събиране на всички грешки, възникнали по време на HTTP заявка. В крайна сметка междинен софтуер може да ги запише в лог файл или в база данни и да ги изпрати през мрежата.
  • Вградено изобразяване: Gin предоставя лесен за използване API за визуализиране на JSON, XML и HTML.

Предпоставки

За да следвате този урок, ще трябва да инсталирате Go на вашата машина, уеб браузър за преглед на приложението и команден ред за изпълнение на команди за изграждане.

Go, или както обикновено се нарича Golang , е език за програмиране, разработен от Google за изграждане на съвременен софтуер. Go е език, предназначен да свърши нещата ефективно и бързо. Основните предимства на Go включват:

  • Силно набрани и събрани боклуци
  • Огромно бързо компилиране
  • Вградена паралелност
  • Обширна стандартна библиотека

Преминете към раздела за изтегляния на уебсайта Go, за да стартирате Go на вашата машина.

Изграждане на приложение с Gin

Ще създадем просто приложение за списъци с шеги с Gin . Нашето приложение ще изброи някои глупави шеги на татко. Ще добавим удостоверяване към него, така че всички влезли потребители да имат привилегията да харесват и да гледат вицовете.

Това ще ни позволи да илюстрираме как Gin може да се използва за разработване на уеб приложения и / или API.

Ще използваме следните функционалности, предлагани от Gin:

  • Middleware
  • Маршрутизиране
  • Групиране на маршрути

Готови, старт

Ще напишем цялото ни приложение Go във main.goфайл. Тъй като това е малко приложение, ще бъде лесно да се изгради приложението само go runот терминала.

Ще създадем нова директория golang-ginв нашето работно пространство Go и след това main.goфайл в нея:

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

Съдържанието на main.goфайла:

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

Ще трябва да създадем още няколко директории за нашите статични файлове. В същата директория като main.goфайла, нека създадем viewsпапка. В viewsпапката създайте jsпапка и index.htmlфайл в нея.

Засега index.htmlфайлът ще бъде много лесен:

   Jokeish App   

Welcome to the Jokeish App

Преди да тестваме това, което имаме досега, нека инсталираме добавените зависимости:

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

За да видим какво работи, ще трябва да стартираме нашия сървър, като стартираме go run main.go.

След като приложението се стартира, отидете до //localhost:3000вашия браузър. Ако всичко е минало добре, трябва да видите показан заглавен текст на ниво 1 Добре дошли в приложението Jokeish .

Дефиниране на API

Нека добавим още малко код в нашия main.goфайл за нашите API дефиниции. Ще актуализираме нашата mainфункция с два маршрута /jokes/и /jokes/like/:jokeIDкъм групата маршрути /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", }) }

Съдържанието на main.goфайла трябва да изглежда така:

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

Нека стартираме приложението си отново go run main.goи да осъществим достъп до нашите маршрути. //localhost:3000/api/jokesще върне 200 OKотговор на заглавката със съобщението jokes handler not implemented yet. Искане POST за //localhost:3000/api/jokes/like/1връщане на 200 OKзаглавка и съобщението Likejoke handler not implemented yet.

Вицове данни

Тъй като вече имаме зададена дефиниция на маршрути, която прави само едно нещо (връща JSON отговор), ще подправим малко нашата кодова база, като добавим още малко код към нея.

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

Тъй като нашият код изглежда добре, нека продължим и тестваме нашия API. Можем да тестваме с cURLили postmanи след това да изпратим GETзаявка до, за //localhost:3000/jokesда получим пълния списък на шегите, и POSTзаявка //localhost:3000/jokes/like/{jokeid}за увеличаване на харесванията на шегата.

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

Изграждане на потребителския интерфейс (React)

Имаме наш API, така че нека да изградим интерфейс, за да представим данните от нашия API. За това ще използваме React. Няма да навлизаме твърде дълбоко в React, тъй като той ще бъде извън обхвата на този урок. Ако трябва да научите повече за React, разгледайте официалния урок. Можете да внедрите потребителския интерфейс с която и да е фреймворкова рамка, която ви е удобна.

Настройвам

Ще редактираме index.htmlфайла, за да добавим външните библиотеки, необходими за стартиране на React. След това ще трябва да създадем app.jsxфайл в views/jsдиректорията, който ще съдържа нашия React код.

Нашият index.htmlфайл трябва да изглежда така:

     Jokeish App 

Изграждане на нашите компоненти

В React изгледите се разбиват на компоненти. Ще трябва да изградим някои компоненти:

  • един Appкомпонент, като основен вход, който стартира на приложението
  • един Homeкомпонент, който ще се изправи без регистрирани потребители
  • един LoggedInкомпонент, със съдържание видимо само от регистрирани потребители
  • и Jokeкомпонент за показване на списък с шеги.

Ще запишем всички тези компоненти във app.jsxфайла.

Компонентът на приложението

Този компонент зарежда цялото ни приложение React. Той решава кой компонент да се показва въз основа на това дали даден потребител е удостоверен или не. Ще започнем само с нейната основа, а по-късно ще я актуализираме с повече функционалност.

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

Компонентът Начало

Този компонент се показва на невлезли потребители, заедно с бутон, който отваря хостван заключващ екран, където те могат да се регистрират или да влязат. Ще добавим тази функционалност по-късно.

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

Jokeish

A load of Dad jokes XD

Sign in to get access

Sign In ) } }

Компонент LoggedIn

Този компонент се показва, когато потребителят е удостоверен. Той съхранява в своя stateнабор от шеги, които се попълват, когато компонентът се монтира.

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компонента ще съдържа информация за всеки елемент от шегите отговор се показва.

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

Написахме нашите компоненти, така че нека сега кажем на React къде да изобрази приложението. Ще добавим блока с код по-долу в дъното на нашия app.jsxфайл.

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

Нека рестартираме нашия Go сървър go run main.goи се насочим към URL адреса на нашето приложение //localhost:3000/. Ще видите, че Homeкомпонентът се изобразява.

Защита на нашето приложение за шеги с Auth0

Auth0 издава JSON Web Tokens при всяко влизане за вашите потребители. Това означава, че можете да имате солидна инфраструктура за идентичност, включително единичен вход, управление на потребители, поддръжка на доставчици на социална идентичност (Facebook, Github, Twitter и др.), Доставчици на корпоративна идентичност (Active Directory, LDAP, SAML и др.) и вашата собствена база данни на потребители, само с няколко реда код.

Ние можем лесно да настроим удостоверяване в нашето GIN приложение, като използваме Auth0. Ще ви е необходим акаунт, който да следвате заедно с тази част. Ако все още нямате акаунт в Auth0, регистрирайте се сега.

Отказ от отговорност: Това не е спонсорирано съдържание.

Създаване на API клиент

Нашите токени ще бъдат генерирани с Auth0, така че трябва да създадем API и клиент от нашето табло за управление Auth0. Отново, ако все още не сте, регистрирайте се за Auth0 акаунт.

За да създадете нов API, отворете раздела за API в таблото си за управление и щракнете върху бутона Създаване на API .

Изберете име на API и идентификатор . Идентификаторът ще бъде аудиторията за междинния софтуер. В Подписване алгоритъм трябва да бъде RS256 .

За да създадете нов клиент, отворете секцията за клиенти в таблото за управление и щракнете върху бутона Създаване на клиент . Изберете типа Regular Web Applications.

След като клиентът е създаден, обърнете внимание на client_idи client_secret, тъй като ще ни трябват по-късно.

Трябва да добавим идентификационните данни, необходими за нашия API, към променлива на околната среда. В основната директория създайте нов файл .envи добавете следното към него с подробностите от таблото за управление Auth0:

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

Защита на нашите крайни точки на API

В момента нашият API е отворен за света. Трябва да защитим нашите крайни точки, така че само оторизирани потребители да имат достъп до тях.

Ще използваме JWT Middleware, за да проверим за валиден JSON Web Token от всяка заявка, удряща крайните ни точки.

Нека създадем нашия междинен софтуер:

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

В горния код имаме нова jwtMiddleWareпроменлива, която се инициализира във mainфункцията. Използва се в authMiddlewareсредната функция.

Ако забележите, ние изтегляме нашите идентификационни данни от страна на сървъра от променлива на околната среда (един от принципите на 12-факторно приложение ). Нашият междинен софтуер проверява и получава токен от заявка и извиква jwtMiddleWare.CheckJWTметода за валидиране на изпратения маркер.

Нека също напишем функцията за връщане на уеб ключовете 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 }

Използване на JWT междинния софтуер

Използването на междинния софтуер е много лесно. Просто го предаваме като параметър в дефиницията на нашите маршрути.

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

Нашият main.goфайл трябва да изглежда така:

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

Нека инсталираме jwtmiddlewareбиблиотеките:

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

Нека извлечем файла с нашата среда и рестартираме нашия сървър за приложения:

$ source .env $ go run main.go

Сега, ако се опитаме да осъществим достъп до някоя от крайните точки, ще се сблъскаме с 401 Unauthorizedгрешка. Това е така, защото трябва да изпратим символ заедно със заявката.

Влезте с Auth0 и React

Нека внедрим система за вход, така че потребителите да могат да влизат или да създават акаунти и да получат достъп до нашите шеги. Ще добавим към нашия app.jsxфайл следните идентификационни данни за Auth0:

  • AUTH0_CLIENT_ID
  • AUTH0_DOMAIN
  • AUTH0_CALLBACK_URL - URL адресът на вашето приложение
  • AUTH0_API_AUDIENCE
Можете да намерите данните AUTH0_CLIENT_ID, AUTH0_DOMAINи AUTH0_API_AUDIENCEданните от вашето табло за управление на Auth0.

Трябва да зададем a, callbackкойто Auth0 пренасочва. Придвижете се до секцията Клиенти в таблото си за управление. В настройките нека зададем обратно повикване на //localhost:3000:

С наличните идентификационни данни, нека актуализираме нашите React компоненти.

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

Ние актуализира App компонент с три компонентни методи ( setup, parseHashи setState), както и метод на жизнения цикъл componentWillMount. В parseHashметода инициализира auth0webAuthклиента и прави разбор хеша към по-четлив формат, да ги записвате localSt. За да покажете заключения екран, уловете и съхранявайте потребителския маркер и добавете правилния заглавие за оторизация към всички заявки към нашия API

Домашен компонент

Нашият компонент Начало ще бъде актуализиран. Ще добавим функционалността за authenticateметода, която ще задейства показването на хоствания заключващ екран и ще позволи на потребителите ни да влязат или да се регистрират.

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

Ще актуализираме LoggedInкомпонента, за да комуникира с нашия API и ще извадим всички шеги. Той ще предаде всяка шега като a propна Jokeкомпонента, който изобразява панел за зареждане. Нека напишем тези:

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компонента, за да форматира всеки елемент на шега, предаден му от родителския компонент ( LoggedIn). Ще добавим и likeметод, който ще увеличи харесванията на шега.

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

Събирайки всичко

С пълния потребителски интерфейс и API можем да тестваме нашето приложение. Ще започнем с зареждане на нашия сървър source .env && go run main.goи след това ще преминем към //localhost:3000всеки браузър. Трябва да видите Homeкомпонента с бутон за вход. Кликването върху бутона за влизане ще пренасочи към хоствана страница за заключване (създайте акаунт или вход), за да продължите да използвате приложението.

У дома:

Екран за заключване на Auth0:

Изглед на приложението LoggedIn:

Заключение

Поздравления! Научихте как да създадете приложение и API с Go и Gin framework.

Пропуснах ли нещо важно? Уведомете ме в коментарите.

Можете да кажете „Здравей ми“ в Twitter @codehakase