Go、Gin、Reactを使用してWebアプリを構築する方法

この記事はもともと私のブログに投稿されました

TL; DR:このチュートリアルでは、GoとGinフレームワークを使用してWebアプリケーションを構築し、それに認証を追加することがいかに簡単であるかを示します。これから作成するコードについては、Githubリポジトリを確認してください。

ジンは高性能のマイクロフレームワークです。これは、Webアプリケーションとマイクロサービスの構築に必要な最も重要な機能、ライブラリ、および機能のみを備えた非常に最小限のフレームワークを提供します。モジュール式の再利用可能な部分からリクエスト処理パイプラインを簡単に構築できます。これは、1つ以上の要求ハンドラーまたは要求ハンドラーのグループにプラグインできるミドルウェアを作成できるようにすることで実現します。

ジンの特徴

Ginは、Go向けの高速でシンプルでありながら、完全な機能を備えた非常に効率的なWebフレームワークです。次のGolangプロジェクトで検討する価値のあるフレームワークとなる以下の機能のいくつかを確認してください。

  • スピード:ジンはスピードのために作られています。このフレームワークは、基数木ベースのルーティングと小さなメモリフットプリントを提供します。反射なし。予測可能なAPIパフォーマンス。
  • クラッシュフリー:Ginには、実行時にクラッシュやパニックをキャッチする機能があり、それらから回復できます。このようにして、アプリケーションは常に利用可能になります。
  • ルーティング: Ginは、WebアプリケーションまたはAPIルートがどのように見えるかを表現できるルーティングインターフェイスを提供します。
  • JSON検証: GinはJSONリクエストを簡単に解析および検証し、必要な値の存在を確認できます。
  • エラー管理: Ginは、HTTPリクエスト中に発生したすべてのエラーを収集するための便利な方法を提供します。最終的に、ミドルウェアはそれらをログファイルまたはデータベースに書き込み、ネットワークを介して送信できます。
  • 組み込みのレンダリング: Ginは、JSON、XML、およびHTMLレンダリング用の使いやすいAPIを提供します。

前提条件

このチュートリアルを実行するには、マシンにGoをインストールし、アプリを表示するためのWebブラウザーと、ビルドコマンドを実行するためのコマンドラインを用意する必要があります。

Go、または通常はGolangと呼ばれるものは、最新のソフトウェアを構築するためにGoogleによって開発されたプログラミング言語です。Goは、効率的かつ迅速に作業を行うために設計された言語です。Goの主な利点は次のとおりです。

  • 強くタイプされ、ガベージコレクション
  • 驚異的な高速コンパイル時間
  • 組み込みの並行性
  • 豊富な標準ライブラリ

Go Webサイトのダウンロードセクションにアクセスして、マシンでGoを実行します。

Ginでアプリを作成する

Ginを使用して簡単なジョークリストアプリを作成します。私たちのアプリは、いくつかの愚かなオヤジギャグを一覧表示します。これに認証を追加して、ログインしているすべてのユーザーがジョークを高く評価して表示する権限を持つようにします。

これにより、Ginを使用してWebアプリケーションやAPIを開発する方法を説明できます。

Ginが提供する次の機能を利用します。

  • ミドルウェア
  • ルーティング
  • ルートのグループ化

位置についてよーいどん

Goアプリケーション全体をmain.goファイルに書き込みます。小さなアプリケーションなのでgo run、端末からでも簡単に構築できます。

golang-ginGoワークスペースに新しいディレクトリを作成し、その中に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のヘッダーテキストWelcome to the JokeishAppが表示されます。

APIの定義

main.goAPI定義用のコードをファイルに追加しましょう。main2つのルート/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。ヘッダーとメッセージを//localhost:3000/api/jokes/like/1返すPOSTリクエスト。200 OKLikejoke handler not implemented yet

ジョークデータ

ルート定義はすでに設定されており、1つのこと(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

UIの構築(React)

APIが用意されているので、APIからのデータを表示するフロントエンドを構築しましょう。このために、Reactを使用します。このチュートリアルの範囲外になるため、Reactについてはあまり深く掘り下げません。Reactについてさらに学ぶ必要がある場合は、公式チュートリアルをチェックしてください。使い慣れたフロントエンドフレームワークを使用してUIを実装できます。

セットアップ

index.htmlファイルを編集して、Reactの実行に必要な外部ライブラリを追加します。次にapp.jsxviews/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は、ユーザーのログインごとにJSONWebトークンを発行します。これは、シングルサインオン、ユーザー管理、ソーシャルIDプロバイダー(Facebook、Github、Twitterなど)、エンタープライズIDプロバイダー(Active Directory、LDAP、SAMLなど)のサポートを含む、堅固なIDインフラストラクチャを持つことができることを意味します。ほんの数行のコードで、ユーザーの独自のデータベース。

Auth0を使用すると、GINアプリで認証を簡単に設定できます。この部分をフォローするには、アカウントが必要です。Auth0アカウントをまだお持ちでない場合は、今すぐサインアップしてください。

免責事項:これはスポンサーコンテンツではありません。

APIクライアントの作成

トークンはAuth0で生成されるため、Auth0ダッシュボードからAPIとクライアントを作成する必要があります。繰り返しになりますが、まだ登録していない場合は、Auth0アカウントにサインアップしてください。

新しいAPIを作成するには、ダッシュボードの[ API ]セクションに移動し、[ API作成]ボタンをクリックします

API識別子を選択します。識別子はミドルウェアのオーディエンスになります。署名アルゴリズムでなければなりませんRS256

新しいクライアントを作成するには、ダッシュボードの[クライアント]セクションに移動し、[クライアント作成]ボタンをクリックします。タイプを選択しRegular Web Applicationsます。

クライアントが作成されたら、後で必要になるので、client_idclient_secretに注意してください。

APIに必要な認証情報を環境変数に追加する必要があります。ルートディレクトリで、新しいファイル.envを作成し、Auth0ダッシュボードの詳細を使用して次のファイルを追加します。

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

APIエンドポイントの保護

現在、私たちのAPIは世界中に公開されています。エンドポイントを保護して、許可されたユーザーのみがエンドポイントにアクセスできるようにする必要があります。

JWTミドルウェアを利用して、エンドポイントに到達する各リクエストから有効なJSONWebトークンを確認します。

ミドルウェアを作成しましょう:

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

上記のコードではjwtMiddleWaremain関数で初期化される新しい変数があります。authMiddleware中間機能で使用されます。

お気づきの方もいらっしゃると思いますが、サーバー側の認証情報を環境変数(12要素アプリの信条の1つ)から取得しています。ミドルウェアはリクエストからトークンをチェックして受信し、jwtMiddleWare.CheckJWT送信されたトークンを検証するためにメソッドを呼び出します。

JSONWebキーを返す関数も作成しましょう。

// ... 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_IDAUTH0_DOMAINAUTH0_API_AUDIENCEあなたAuth0管理ダッシュボードからデータを。

callbackAuth0がリダイレクトする先を設定する必要があります。ダッシュボードの[クライアント]セクションに移動します。設定で、コールバックを//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 ; } }

私たちは、3つのコンポーネントのメソッド(とアプリケーションコンポーネントを更新しsetupparseHash、および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コンポーネント

LoggedInAPIと通信するようにコンポーネントを更新し、すべてのジョークをプルします。各ジョークをコンポーネントに渡しpropJokeコンポーネントはブートストラップパネルをレンダリングします。それらを書いてみましょう:

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

すべてを一緒に入れて

UIとAPIが完成したら、アプリをテストできます。サーバーを起動することから始めsource .env && go run main.go、次に//localhost:3000任意のブラウザーからに移動します。Homeサインインボタンのあるコンポーネントが表示されます。サインインボタンをクリックすると、ホストされているロックページにリダイレクトされ(アカウントを作成するか、ログインします)、アプリケーションを引き続き使用できます。

ホーム:

Auth0ホストロック画面:

LoggedInアプリビュー:

結論

おめでとうございます!GoとGinフレームワークを使用してアプリケーションとAPIを構築する方法を学習しました。

重要なことを見逃しましたか?コメントで教えてください。

Twitter @ codehakaseでこんにちはと言うことができます