Як створити веб-програму за допомогою Go, Gin та React

Ця стаття була спочатку розміщена на моєму блозі

TL; DR: У цьому посібнику я покажу вам, як легко створити веб-додаток із Go та фреймворком Gin та додати до нього аутентифікацію. Перевірте репо 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:

  • Проміжне програмне забезпечення
  • Маршрутизація
  • Групування маршрутів

На старт, увага, марш

Ми напишемо всю програму 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 ) } }

Вхідний компонент

Цей компонент відображається під час автентифікації користувача. Він зберігає у своєму 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 під час кожного входу для користувачів. Це означає, що ви можете мати надійну інфраструктуру ідентифікації, включаючи єдиний вхід, управління користувачами, підтримку постачальників соціальних ідентифікаторів (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, щоб перевірити наявність дійсного веб-маркера JSON з кожного запиту, що потрапляє в наші кінцеві точки.

Давайте створимо наше проміжне програмне забезпечення:

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

Нам потрібно встановити значення, на 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

Домашня складова

Наш компонент Home буде оновлений. Ми додамо функціонал для 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компонент для зв’язку з нашим API і потягнемо всі жарти. Він буде проходити кожну жарт , як 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