Рубрики
Без рубрики

Как построить веб-приложение с GO, GIN и реагировать

Фрэнсис воскресенье Как построить веб-приложение с GO, GIN, и ATVESTIS, статью изначально была опубликована на моем блоге; DR: В этом руководстве я покажу вам, насколько легко создать веб-приложение с GO и Framework GIN и добавить аутентификацию к нему. Проверять

Автор оригинала: FreeCodeCamp Community Member.

Фрэнсис Воскресенье

TL; DR: В этом руководстве я покажу вам, насколько легко создать веб-приложение с GO и Framework GIN и добавить аутентификацию к нему. Проверьте Github репо Для кода мы собираемся написать.

Джин является высокопроизводительным микро-каркасом. Он обеспечивает очень минималистичные рамки, которые выполняют его только самые важные функции, библиотеки и функциональные возможности, необходимые для создания веб-приложений и микросервисов. Это делает его простым для создания трубопровода обработки запросов от модульных, многоразовых деталей. Это делает это, позволяя вам писать промежуточное программное обеспечение, которое можно подключить к одному или нескольким обработчикам запроса или групп обработчиков запросов.

Особенности джина

Джин – это быстрый, простая, но полностью полностью представлена и очень эффективная веб-каркас для GO. Проверьте некоторые из функций ниже, которые делают достойную структуру для рассмотрения вашего следующего проекта Golang.

  • Скорость: Джин построен для скорости. Рамки предлагают маршрутизацию на основе Radix на основе дерева и небольшую площадь памяти. Нет отражения. Предсказуемая производительность API.
  • БЕСПЛАТНЫЙ : Джин имеет возможность ловить аварию или панику во время выполнения, и может оправиться от них. Таким образом, ваше приложение всегда будет доступно.
  • Маршрутизация: GIN предоставляет интерфейс маршрутизации, чтобы позволить вам выразить ваше веб-приложение или маршруты API.
  • Валидация JSON: Джин может легко разбирать и подтвердить запросы JSON, проверяя наличие необходимых значений.
  • Управление ошибками: Джин дает удобный способ собрать все ошибки во время HTTP-запроса. В конце концов, промежуточное программное обеспечение может написать их в файл журнала или в базу данных и отправлять их через сеть.
  • Встроенный рендеринг: GIN обеспечивает простые в использовании API для JSON, XML и HTML-рендеринга.

Предварительные условия

Чтобы следить с этим руководством, вам нужно будет установить на вашем компьютере, веб-браузер для просмотра приложения и командной строки для выполнения команд сборки.

Иди, или как его нормально называют Голанг Этот язык программирования, разработанный Google для создания современного программного обеспечения. Go – это язык, предназначенный для эффективного и быстро и быстро. Основные преимущества GO включают в себя:

  • Сильно набран и мусор собранный
  • Пылающий быстрые времена компиляции
  • Встроенный параллелизм
  • Обширная стандартная библиотека

Перейти к Скачать раздел веб-сайта Go, чтобы пойти на работу на вашу машину.

Создание приложения с джином

Мы будем строить простое приложение для списка шуток с Джин . Наше приложение будет перечислять некоторые глупые шутки папы. Мы собираемся добавить аутентификацию к нему, чтобы все зарегистрированные пользователи будут иметь привилегию любить и просматривать шутки.

Это позволит нам иллюстрировать, как Джин Может использоваться для разработки веб-приложений и/или API.

Мы будем использовать следующие функциональные возможности, предлагаемые Джином:

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

На старт, внимание, марш

Мы напишем все наше приложение Go в main.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 Файл, давайте создадим Виды папка. В Виды Папка, создать 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 Отказ

Как только приложение запущено, перейдите к http://localhost: 3000 в вашем браузере. Если все прошло хорошо, вы должны увидеть текст заголовка уровня 1 Добро пожаловать в приложение для шутки отображается.

Определение API

Давайте добавим еще код в нашем main.go Файл для наших определений API. Мы обновим наше Главная Функция с двумя маршрутами /шутки/ и /анекдоты/как/: joundid в группу маршрутов /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 и получить доступ к нашим маршрутам. http://localhost: 3000/API/Jokes вернется 200 ОК Ответ заголовка, с сообщением Шутки обработчика еще не реализованы Отказ Пост запрос на http://localhost: 3000/api/jokes/like/1 Возвращает 200 ОК Заголовок и сообщение Обработчик AlceJoke не реализован еще Отказ

Шутки данных

Поскольку у нас уже есть наши маршруты набор определений, что делает только одно (вернуть ответ 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 или почтальон , а затем отправьте Получить Запрос на http://localhost: 3000/jokes Чтобы получить полный список шуток и Пост Запрос на http://localhost: 3000/jokes/like/{jokeid} увеличить симпатию шутки.

$ curl http://localhost:3000/api/jokes

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

Создание пользовательского интерфейса (реагировать)

У нас есть наш API на месте, поэтому давайте построим интерфейс для представления данных с наших API. Для этого мы будем использовать React. Мы не будем слишком глубоко реагировать, так как он будет вне сферы этого учебника. Если вам нужно узнать больше о реагировании, оформить заказ официальным Учебное пособие Отказ Вы можете реализовать пользовательский интерфейс с любыми беспризорными каркасами, с которыми вам удобно.

Настраивать

Мы отредактируем index.html Файл, чтобы добавить внешние библиотеки, необходимые для работы. Тогда нам нужно создать app.jsx Файл в Виды/JS. каталог, который будет содержать наш код REACT.

Наше index.html Файл должен выглядеть так:






  
  
  Jokeish App
  
  
  
  
  
  
  



  

Создание наших компонентов

В реакции представления разбиты на компоненты. Нам нужно будет построить некоторые компоненты:

  • Приложение Компонент в качестве основной записи, которая запускает приложение
  • А Главная Компонент, который столкнется с запутанными пользователями
  • А Logugedin Компонент с контентом виден только аутентифицированными пользователями
  • и а Шутка Компонент для отображения списка шуток.

Мы напишем все эти компоненты в app.jsx файл.

Компонент приложения

Этот компонент загрузил все наше время реагирования. Он решает, какой компонент показать на основе того, аутентифицирован ли пользователь или нет. Мы начнем с его базой, а позже обновите его с большим количеством функциональности.

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

Компонент logugedin

Этот компонент отображается, когда пользователь аутентифицирован. Он хранит в своем Государство Массив шуток, который заполняется при установке компонентов.

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

Компонент шутки

Шутка Компонент будет содержать информацию о каждом элементе из отклика шуток, который будет отображаться.

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

Мы написали наши компоненты, поэтому теперь давайте скажем отреагировать, где сделать приложение. Мы добавим блок код ниже в нижнюю часть нашего app.jsx файл.

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

Давайте перезапустим наш сервер Go Go Run Main.go и отправьтесь к URL-адресу нашего приложения http://localhost: 3000/ Отказ Вы увидите, что Главная компонент оказывается.

Обеспечение нашего приложения Joxes с AUTH0

AUTH0 проблемы JSON WEB. Токены На каждом входе для ваших пользователей. Это означает, что вы можете иметь твердое Инфраструктура идентичности , в том числе Одиночный вход , Управление пользователем, поддержка поставщиков социальной идентичности (Facebook, Github, Twitter и т. Д.), Поставщики Identity Enterprise (Active Directory, LDAP, SAML и т. Д.) И ваша собственная база данных пользователей с несколькими линиями кода.

Мы можем легко настроить аутентификацию в нашем приложении GIN с помощью AUTH0. Вам понадобится учетная запись, чтобы следовать вместе с этой частью. Если у вас нет учетной записи AUTH0, Зарегистрироваться на один сейчас.

Создание клиента API

Наши токены будут сгенерированы с помощью AUTH0, поэтому нам нужно создать API и клиент с нашей панели инструментов AUTH0. Опять же, если вы еще не сделали, Зарегистрироваться Для учетной записи AUTH0.

Чтобы создать новую API, перейдите к Секция API на приборной панели и нажмите на Создать API кнопка.

Выберите API Имя и Идентификатор Отказ Идентификатор будет Аудитория для промежуточного программного обеспечения. Подписание алгоритма должно быть RS256. .

Чтобы создать новый клиент, перейдите к Секция клиентов на приборной панели и нажмите на Создать клиент кнопка. Выберите тип Регулярные веб-приложения .

Как только клиент создан, обратите внимание на Client_id и Client_Secret. , как нам понадобится позже.

Мы должны добавить учетные данные, необходимые для нашего API для переменной среды. В корневом каталоге создайте новый файл .env И добавьте его к нему, с деталями приборной панели AUTH0:

export AUTH0_API_CLIENT_SECRET=""
export AUTH0_CLIENT_ID=""
export AUTH0_DOMAIN="yourdomain.auth0.com"
export AUTH0_API_AUDIENCE=""

Защита наших конечных точек API

В настоящее время наша API открыта для мира. Нам нужно обеспечить наши конечные точки, поэтому только авторизованные пользователи могут получить доступ к ним.

Мы собираемся использовать JWT Mardware Чтобы проверить действительный JSON WEB. Токен Из каждого запроса ударяет наши конечные точки.

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

// ...

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 Переменная, которая инициализируется в Главная функция. Он используется в 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 Несанкционировано ошибка. Это потому, что нам нужно отправить в токен с запросом.

Войти с AUTH0 и реагировать

Давайте реализуем систему входа в систему, чтобы пользователи могли войти или создавать учетные записи и получить доступ к нашим шуткам. Мы добавим к нашему app.jsx Файл следующие учетные данные AUTH0:

  • Auth0_client_id.
  • Auth0_domain
  • Auth0_callback_url – URL вашего приложения
  • AUTH0_API_AUDINE.

Нам нужно установить Обратный вызов на что AUTH0 перенаправляет. Перейдите к разделу клиентов на приборной панели. В настройках давайте установим обратный вызов на http://localhost: 3000 :

С учетом учетных данных давайте обновим наши комментарии реагирования.

Компонент приложения

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

Мы обновили компонент приложения с тремя методами компонентов ( Setup , Parsehash и SetState ) и метод жизненного цикла ComponentWillmount Отказ Parsehash Метод инициализирует AUTH0 Webauth Клиент и анализируют хэш до более читаемого формата, сохраняя их в локальную. Чтобы показать экран блокировки, захватить и хранить токен пользователя и добавьте правильный заголовок авторизации в любые запросы на нашу API

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

Наш домашний компонент будет обновлен. Мы добавим функциональность для аутентифицировать Метод, который забудит размещенный экран блокировки размещенного блокировки и позволит нашим пользователям войти или регистрацию.

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

Компонент logugedin

Мы обновим Logugedin Компонент для общения с нашим API и потяните все шутки. Это пройдет каждую шутку как опора к Шутка Компонент, который делает панель Bootstrap. Давайте напишем те:

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("http://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 ; })}
); } }

Шутка компонент

Мы также обновим Шутка Компонент для форматирования каждого элемента шутки передан ему из родительского компонента ( loggedin ). Мы также добавим Как Метод, который будет увеличиваться как шутка.

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(
      "http://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  
) } }

Положить все вместе

С помощью UI и API завершены, мы можем проверить наше приложение. Мы начнем, загрузив наш сервер Источник .env && Go Run Main.go , а затем мы перейдем к http://localhost: 3000 из любого браузера. Вы должны увидеть Главная Компонент с кнопкой входа. Нажатие на кнопку Signin будет перенаправлять на страницу Hosted Lock (создайте учетную запись или вход в систему), чтобы продолжить использование приложения.

Дом:

Auth0 Hosted Lock Screen:

Logugedin Просмотр приложений:

Заключение

Поздравляю! Вы узнали, как создать приложение и API с GO и GIN Framework.

Я пропустил что-то важное? Дайте мне знать об этом в комментариях.

Вы можете сказать привет мне в Twitter @Codehakase.

Оригинал: “https://www.freecodecamp.org/news/how-to-build-a-web-app-with-go-gin-and-react-cffdc473576/”