Cara membuat aplikasi web dengan Go, Gin, dan React

Artikel ini pertama kali diposting di Blog Saya

TL; DR: Dalam tutorial ini, saya akan menunjukkan kepada Anda betapa mudahnya membangun aplikasi web dengan Go dan kerangka Gin dan menambahkan otentikasi ke dalamnya. Lihat repo Github untuk kode yang akan kita tulis.

Gin adalah kerangka mikro berkinerja tinggi. Ini memberikan kerangka kerja yang sangat minimalis yang hanya membawa fitur, pustaka, dan fungsi paling penting yang diperlukan untuk membangun aplikasi web dan layanan mikro. Itu membuatnya mudah untuk membangun pipeline penanganan permintaan dari bagian modular yang dapat digunakan kembali. Hal ini dilakukan dengan mengizinkan Anda menulis middleware yang dapat dihubungkan ke satu atau lebih penangan permintaan atau kelompok penangan permintaan.

Fitur Gin

Gin adalah kerangka kerja web yang cepat, sederhana namun berfitur lengkap dan sangat efisien untuk Go. Lihat beberapa fitur di bawah ini yang menjadikannya kerangka kerja yang layak untuk dipertimbangkan untuk proyek Golang Anda berikutnya.

  • Kecepatan: Gin dibuat untuk kecepatan. Kerangka kerja ini menawarkan perutean berbasis pohon Radix dan jejak memori kecil. Tidak ada refleksi. Performa API yang dapat diprediksi.
  • Crash-Free : Gin memiliki kemampuan untuk menangkap crash atau panik selama runtime, dan dapat memulihkannya. Dengan cara ini aplikasi Anda akan selalu tersedia.
  • Perutean: Gin menyediakan antarmuka perutean untuk memungkinkan Anda mengekspresikan tampilan aplikasi web atau rute API Anda.
  • Validasi JSON: Gin dapat mengurai dan memvalidasi permintaan JSON dengan mudah, memeriksa keberadaan nilai yang diperlukan.
  • Manajemen Kesalahan: Gin menyediakan cara mudah untuk mengumpulkan semua kesalahan yang terjadi selama permintaan HTTP. Akhirnya, middleware dapat menuliskannya ke file log atau ke database dan mengirimkannya melalui jaringan.
  • Rendering Bawaan: Gin menyediakan API yang mudah digunakan untuk rendering JSON, XML, dan HTML.

Prasyarat

Untuk mengikuti tutorial ini, Anda harus menginstal Go di komputer Anda, browser web untuk melihat aplikasi, dan baris perintah untuk menjalankan perintah build.

Go, atau biasa disebut Golang , merupakan bahasa pemrograman yang dikembangkan oleh Google untuk membangun perangkat lunak modern. Go adalah bahasa yang dirancang untuk menyelesaikan pekerjaan secara efisien dan cepat. Manfaat utama Go meliputi:

  • Diketik dengan kuat dan sampah dikumpulkan
  • Waktu kompilasi yang sangat cepat
  • Konkurensi sudah terpasang
  • Perpustakaan standar yang luas

Buka bagian unduhan di situs web Go untuk menjalankan Go di komputer Anda.

Membuat aplikasi dengan Gin

Kami akan membuat aplikasi daftar lelucon sederhana dengan Gin . Aplikasi kami akan mencantumkan beberapa lelucon ayah konyol. Kami akan menambahkan otentikasi ke dalamnya, sehingga semua pengguna yang masuk akan memiliki hak istimewa untuk menyukai dan melihat lelucon.

Ini akan memungkinkan kami mengilustrasikan bagaimana Gin dapat digunakan untuk mengembangkan aplikasi web dan / atau API.

Kami akan menggunakan fungsi berikut yang ditawarkan oleh Gin:

  • Middleware
  • Rute
  • Pengelompokan Rute

Siap, siap, Mulai

Kami akan menulis seluruh aplikasi Go kami dalam sebuah main.gofile. Karena ini adalah aplikasi kecil, akan mudah untuk membangun aplikasi hanya dengan go rundari terminal.

Kami akan membuat direktori baru golang-gindi ruang kerja Go kami, dan kemudian main.gofile di dalamnya:

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

Isi main.gofile:

package main import ( "net/http" "github.com/gin-gonic/contrib/static" "github.com/gin-gonic/gin" ) func main() { // Set the router as the default one shipped with Gin router := gin.Default() // Serve frontend static files router.Use(static.Serve("/", static.LocalFile("./views", true))) // Setup route group for the API api := router.Group("/api") { api.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H { "message": "pong", }) }) } // Start and run the server router.Run(":3000") }

Kita perlu membuat beberapa direktori lagi untuk file statis kita. Di direktori yang sama dengan main.gofile, mari buat viewsfolder. Di dalam viewsfolder tersebut, buat jsfolder dan index.htmlfile di dalamnya.

The index.htmlfile akan sangat sederhana untuk saat ini:

   Jokeish App   

Welcome to the Jokeish App

Sebelum kita menguji apa yang kita miliki sejauh ini, mari kita instal dependensi yang ditambahkan:

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

Untuk melihat apa yang berhasil, kita perlu memulai server kita dengan menjalankan go run main.go.

Setelah aplikasi berjalan, navigasikan ke //localhost:3000di browser Anda. Jika semua berjalan lancar, Anda akan melihat teks header level 1 Selamat datang di Aplikasi Jokeish ditampilkan.

Mendefinisikan API

Mari tambahkan beberapa kode lagi di main.gofile kita untuk definisi API kita. Kami akan memperbarui mainfungsi kami dengan dua rute /jokes/dan /jokes/like/:jokeIDke grup rute /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", }) }

Konten main.gofile akan terlihat seperti ini:

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

Mari jalankan aplikasi kita lagi go run main.go, dan akses rute kita. //localhost:3000/api/jokesakan mengembalikan 200 OKrespons header, dengan pesannya jokes handler not implemented yet. Permintaan POST untuk //localhost:3000/api/jokes/like/1mengembalikan 200 OKheader, dan pesannya Likejoke handler not implemented yet.

Data lelucon

Karena kita sudah memiliki set definisi rute kita, yang hanya melakukan satu hal (mengembalikan respons JSON), kita akan meningkatkan basis kode kita sedikit dengan menambahkan beberapa kode lagi padanya.

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

Dengan kode kita terlihat bagus, mari lanjutkan dan uji API kita. Kami dapat menguji dengan cURLatau postman, dan kemudian mengirim GETpermintaan ke //localhost:3000/jokesuntuk mendapatkan daftar lengkap lelucon, dan POSTpermintaan //localhost:3000/jokes/like/{jokeid}untuk menambah suka dari sebuah lelucon.

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

Membangun UI (React)

Kami memiliki API kami, jadi mari buat frontend untuk menyajikan data dari API kami. Untuk ini, kami akan menggunakan React. Kami tidak akan membahas terlalu dalam tentang React, karena ini akan berada di luar cakupan tutorial ini. Jika Anda perlu mempelajari lebih lanjut tentang React, lihat tutorial resmi. Anda dapat mengimplementasikan UI dengan kerangka kerja frontend apa pun yang Anda sukai.

Mendirikan

Kami akan mengedit index.htmlfile untuk menambahkan perpustakaan eksternal yang diperlukan untuk menjalankan React. Kemudian kita perlu membuat app.jsxfile di views/jsdirektori, yang akan berisi kode React kita.

index.htmlFile kita akan terlihat seperti ini:

     Jokeish App 

Membangun komponen kami

Di React, tampilan dipecah menjadi beberapa komponen. Kami perlu membangun beberapa komponen:

  • sebuah Appkomponen sebagai entri utama yang meluncurkan aplikasi
  • sebuah Homekomponen yang akan menghadapi pengguna yang tidak masuk
  • sebuah LoggedInkomponen dengan konten yang hanya dapat dilihat oleh pengguna yang diautentikasi
  • dan Jokekomponen untuk menampilkan daftar lelucon.

Kami akan menulis semua komponen ini di app.jsxfile.

Komponen aplikasi

Komponen ini mem-bootstrap seluruh aplikasi React kita. Ini memutuskan komponen mana yang akan ditampilkan berdasarkan apakah pengguna diautentikasi atau tidak. Kami akan mulai hanya dengan basisnya, dan kemudian memperbaruinya dengan lebih banyak fungsionalitas.

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

Komponen Rumah

Komponen ini ditampilkan kepada pengguna yang tidak masuk, bersama dengan tombol yang membuka layar kunci Dihosting tempat mereka dapat mendaftar atau masuk. Kami akan menambahkan fungsi ini nanti.

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

Jokeish

A load of Dad jokes XD

Sign in to get access

Sign In ) } }

Komponen LoggedIn

Komponen ini ditampilkan saat pengguna diautentikasi. Ini menyimpan dalam stateserangkaian lelucon yang diisi saat komponen dipasang.

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

Komponen Joke

The Jokekomponen akan berisi informasi tentang setiap item dari lelucon respon yang akan ditampilkan.

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

Kami telah menulis komponen kami, jadi sekarang mari beri tahu React di mana merender aplikasi. Kami akan menambahkan blok kode di bawah ini ke bagian bawah app.jsxfile kami .

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

Mari restart server Go kita go run main.go, dan menuju ke URL aplikasi kita //localhost:3000/. Anda akan melihat bahwa Homekomponen sedang dirender.

Mengamankan aplikasi lelucon kami dengan Auth0

Auth0 mengeluarkan Token Web JSON pada setiap login untuk pengguna Anda. Artinya, Anda dapat memiliki infrastruktur identitas yang solid, termasuk sistem masuk tunggal, pengelolaan pengguna, dukungan untuk penyedia identitas sosial (Facebook, Github, Twitter, dll.), Penyedia identitas perusahaan (Active Directory, LDAP, SAML, dll.) dan database pengguna Anda sendiri, hanya dengan beberapa baris kode.

Kami dapat dengan mudah mengatur otentikasi di aplikasi GIN kami dengan menggunakan Auth0. Anda akan membutuhkan akun untuk mengikuti bagian ini. Jika Anda belum memiliki akun Auth0, daftar sekarang.

Penafian: Ini bukan konten bersponsor.

Membuat klien API

Token kami akan dibuat dengan Auth0, jadi kami perlu membuat API dan Klien dari dasbor Auth0 kami. Sekali lagi, jika Anda belum melakukannya, daftar untuk akun Auth0.

Untuk membuat API baru, navigasikan ke bagian API di dasbor Anda, dan klik tombol Buat API .

Pilih nama API dan pengenal . Pengidentifikasi akan menjadi audiens untuk middleware. The Penandatanganan Algoritma harus RS256 .

Untuk membuat Klien baru, navigasikan ke bagian klien di dasbor Anda, dan klik tombol Buat Klien . Pilih jenisnya Regular Web Applications.

Setelah klien dibuat, catat client_iddan client_secret, karena kita akan membutuhkannya nanti.

Kami harus menambahkan kredensial yang diperlukan untuk API kami ke variabel lingkungan. Di direktori root, buat file baru .envdan tambahkan yang berikut ini ke dalamnya, dengan detail dari dasbor Auth0:

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

Mengamankan titik akhir API kami

Saat ini, API kami terbuka untuk dunia. Kami perlu mengamankan titik akhir kami, jadi hanya pengguna resmi yang dapat mengaksesnya.

Kami akan menggunakan JWT Middleware untuk memeriksa Token Web JSON yang valid dari setiap permintaan yang mencapai titik akhir kami.

Mari buat middleware kita:

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

Dalam kode di atas, kami memiliki jwtMiddleWarevariabel baru yang diinisialisasi dalam mainfungsi. Ini digunakan di authMiddlewarefungsi tengah.

Jika Anda perhatikan, kami menarik kredensial sisi server kami dari variabel lingkungan (salah satu prinsip aplikasi 12 faktor ). Middleware kami memeriksa dan menerima token dari permintaan dan memanggil jwtMiddleWare.CheckJWTmetode untuk memvalidasi token yang dikirim.

Mari kita juga menulis fungsi untuk mengembalikan Kunci Web JSON:

// ... the code above is untouched... // Jwks stores a slice of JSON Web Keys type Jwks struct { Keys []JSONWebKeys `json:"keys"` } type JSONWebKeys struct { Kty string `json:"kty"` Kid string `json:"kid"` Use string `json:"use"` N string `json:"n"` E string `json:"e"` X5c []string `json:"x5c"` } func main() { // ... the code in this method is untouched... } func getPemCert(token *jwt.Token) (string, error) { cert := "" resp, err := http.Get(os.Getenv("AUTH0_DOMAIN") + ".well-known/jwks.json") if err != nil { return cert, err } defer resp.Body.Close() var jwks = Jwks{} err = json.NewDecoder(resp.Body).Decode(&jwks) if err != nil { return cert, err } x5c := jwks.Keys[0].X5c for k, v := range x5c { if token.Header["kid"] == jwks.Keys[k].Kid { cert = "-----BEGIN CERTIFICATE-----\n" + v + "\n-----END CERTIFICATE-----" } } if cert == "" { return cert, errors.New("unable to find appropriate key.") } return cert, nil }

Menggunakan middleware JWT

Menggunakan middleware sangat mudah. Kami hanya meneruskannya sebagai parameter untuk definisi rute kami.

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

main.goFile kita akan terlihat seperti ini:

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

Mari instal jwtmiddlewareperpustakaan:

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

Mari sumber file lingkungan kita, dan mulai ulang server aplikasi kita:

$ source .env $ go run main.go

Sekarang jika kami mencoba mengakses salah satu titik akhir, kami akan menghadapi 401 Unauthorizedkesalahan. Itu karena kita perlu mengirimkan token dengan permintaan tersebut.

Login dengan Auth0 dan React

Mari menerapkan sistem login sehingga pengguna dapat login atau membuat akun dan mendapatkan akses ke lelucon kita. Kami akan menambahkan ke app.jsxfile kami kredensial Auth0 berikut:

  • AUTH0_CLIENT_ID
  • AUTH0_DOMAIN
  • AUTH0_CALLBACK_URL - URL aplikasi Anda
  • AUTH0_API_AUDIENCE
Anda dapat menemukan AUTH0_CLIENT_ID, AUTH0_DOMAINdan AUTH0_API_AUDIENCEdata dari manajemen dashboard Auth0 Anda.

Kita perlu mengatur callbackpengalihan Auth0 ke mana. Arahkan ke bagian Klien di dasbor Anda. Dalam pengaturan, mari kita atur callback ke //localhost:3000:

Dengan kredensial di tempatnya, mari perbarui komponen React kita.

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

Kami memperbarui komponen App dengan tiga metode komponen ( setup, parseHashdan setState), dan metode siklus hidup componentWillMount. The parseHashMetode menginisialisasi auth0webAuthklien dan mem-parsing hash untuk format yang lebih mudah dibaca, menyimpannya ke localSt. Untuk menampilkan layar kunci, tangkap dan simpan token pengguna dan tambahkan header otorisasi yang benar untuk setiap permintaan ke API kami

Komponen rumah

Komponen Rumah kami akan diperbarui. Kami akan menambahkan fungsionalitas untuk authenticatemetode ini, yang akan memicu layar kunci yang dihosting untuk ditampilkan dan memungkinkan pengguna kami untuk masuk atau mendaftar.

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

Komponen LoggedIn

Kami akan memperbarui LoggedInkomponen untuk berkomunikasi dengan API kami dan menarik semua lelucon. Ini akan melewati setiap lelucon sebagai propke Jokekomponen, yang membuat panel bootstrap. Mari kita tulis itu:

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

Komponen lelucon

Kami juga akan memperbarui Jokekomponen untuk memformat setiap item Joke yang diteruskan dari compoent Parent ( LoggedIn). Kami juga akan menambahkan likemetode, yang akan menambah suka dari sebuah 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 ) } }

Menyatukan semuanya

Dengan UI dan API yang lengkap, kita dapat menguji aplikasi kita. Kami akan mulai dengan mem-boot server kami source .env && go run main.go, dan kemudian kami akan menavigasi ke //localhost:3000dari browser mana pun. Anda akan melihat Homekomponen dengan tombol masuk. Mengklik tombol masuk akan mengarahkan ke halaman Kunci yang dihosting (buat akun atau login) untuk terus menggunakan aplikasi.

Rumah:

Auth0 Hosted Lock Screen:

Tampilan LoggedIn App:

Kesimpulan

Selamat! Anda telah mempelajari cara membuat aplikasi dan API dengan kerangka Go dan Gin.

Apakah saya melewatkan sesuatu yang penting? Beri tahu saya di komentar.

Anda dapat menyapa saya di Twitter @codehakase