こんにちは、KOUKIです。
GolangのWebフレームワークであるfiberを使ってAPIを開発しています。
前回は、Order APIを実装しました。
今回からは、毛色が変わってAmbassador APIを実装します。
尚、この記事に出てくるソースコードは、Udemyの「React, NextJS and Golang: A Rapid Guide – Advanced」コースを参考にしています。解釈は私が勝手に付けているので、本物をみたい場合は受講をお勧めします!
前回
作るもの
Ambassdor機能を作りたいと思います。エンドポイントは、次の通りです。
- POST /api/ambassador/register
- POST /api/ambassador/login
- GET /api/ambassador/user
- POST /api/ambassador/logout
- PUT /api/ambassador/users/info
- PUT /api/ambassador/users/password
今回は、上記ルートへの全てのパスを実装します。
Ambassador API
Ambassador ルートの設定
今回、Admin(管理者)とAmbassador(個人)へのリクエストを分離するため、コントローラーの処理は共有しますが、ルートは別に設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// routes/routes.go ... func Setup(app *fiber.App) { // Group api := app.Group("api") // admin ... // Ambassador ambassador := api.Group("ambassador") ambassador.Post("register", controllers.Register) ambassador.Post("login", controllers.Login) ambassadorAuthentication := ambassador.Use(middleware.IsAuthenticate) ambassadorAuthentication.Get("user", controllers.User) ambassadorAuthentication.Post("logout", controllers.Logout) ambassadorAuthentication.Put("users/info", controllers.UpdateInfo) ambassadorAuthentication.Put("users/password", controllers.UpdatePassword) } |
fiberのGrouping機能を使うと、上記のようにすっきりとしたコードを実装できます。
登録処理の修正
AdminとAmbassador APIの処理は大体同じですが、いくつか異なる点もあります。
User構造体のis_ambassadorプロパティもその内の一つです。
1 2 3 4 5 6 7 8 9 |
// models/user.go type User struct { Model FirstName string `json:"first_name"` LastName string `json:"last_name"` Email string `json:"email" gorm:"unique"` Password []byte `json:"-"` IsAmbassador bool `json:"-"` } |
このプロパティは、Ambassadorユーザーを登録する時にはtrueである必要があります。
そのため、auathController.goの登録処理を修正しましょう。
1 2 3 4 5 6 7 8 |
// controllers/auathController.go func Register(ctx *fiber.Ctx) error { .... user := models.User{ IsAmbassador: strings.Contains(ctx.Path(), "/api/ambassador"), // 修正 } ... } |
リクエストURLに「/api/ambassador」が含まれる場合は、trueを設定します。そうでない場合は、falseですね。
検証
以下のパラメータでユーザー登録しましょう。
- URL: http://localhost:8000/api/ambassador/register
- 形式: POST
1 2 3 4 5 6 7 |
{ "first_name": "self", "last_name": "note", "email": "ambassdor@ne.jp", "password": "b", "password_confirm": "b" } |

登録できました。DBを確認してみましょう。

is_ambassdorがtrue(1)になっているので、問題なく登録されたようです。
次回
次回は、リクエストスコープを実装しましょう。
Go言語まとめ
ソースコード
ここまでのソースコードを以下に記載します。
routes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// routes/routes.go package routes import ( "admin/src/controllers" "admin/src/middleware" "github.com/gofiber/fiber/v2" ) func Setup(app *fiber.App) { // Group api := app.Group("api") // admin admin := api.Group("admin") admin.Post("register", controllers.Register) admin.Post("login", controllers.Login) adminAuthenticated := admin.Use(middleware.IsAuthenticate) adminAuthenticated.Get("user", controllers.User) adminAuthenticated.Post("logout", controllers.Logout) adminAuthenticated.Put("info", controllers.UpdateInfo) adminAuthenticated.Put("password", controllers.UpdatePassword) adminAuthenticated.Get("ambassadors", controllers.Ambassadors) adminAuthenticated.Get("products", controllers.Products) adminAuthenticated.Post("products", controllers.CreateProducts) adminAuthenticated.Get("products/:id", controllers.GetProduct) adminAuthenticated.Put("products/:id", controllers.UpdateProduct) adminAuthenticated.Delete("products/:id", controllers.DeleteProduct) adminAuthenticated.Get("users/:id/links", controllers.Link) adminAuthenticated.Get("orders", controllers.Orders) // Ambassador ambassador := api.Group("ambassador") ambassador.Post("register", controllers.Register) ambassador.Post("login", controllers.Login) ambassadorAuthentication := ambassador.Use(middleware.IsAuthenticate) ambassadorAuthentication.Get("user", controllers.User) ambassadorAuthentication.Post("logout", controllers.Logout) ambassadorAuthentication.Put("users/info", controllers.UpdateInfo) ambassadorAuthentication.Put("users/password", controllers.UpdatePassword) } |
auathController.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
// controllers/auathController.go package controllers import ( "admin/src/database" "admin/src/middleware" "admin/src/models" "strconv" "strings" "time" "github.com/dgrijalva/jwt-go" "github.com/gofiber/fiber/v2" "golang.org/x/crypto/bcrypt" ) func Register(ctx *fiber.Ctx) error { var data map[string]string if err := ctx.BodyParser(&data); err != nil { return err } if data["password"] != data["password_confirm"] { ctx.Status(fiber.StatusBadRequest) // 400 return ctx.JSON(fiber.Map{ "message": "パスワードに誤りがあります", }) } // ハッシュパスワードを作成 pwd, _ := bcrypt.GenerateFromPassword([]byte(data["password"]), 12) user := models.User{ FirstName: data["first_name"], LastName: data["last_name"], Email: data["email"], Password: pwd, IsAmbassador: strings.Contains(ctx.Path(), "/api/ambassador"), } // パスワードセット user.SetPassword(data["password"]) // ユーザー作成 result := database.DB.Create(&user) if result.Error != nil { ctx.Status(fiber.StatusBadRequest) return ctx.JSON(fiber.Map{ "message": "そのEmailは既に登録されています", }) } return ctx.JSON(user) } func Login(ctx *fiber.Ctx) error { var data map[string]string if err := ctx.BodyParser(&data); err != nil { return err } var user models.User database.DB.Where("email = ?", data["email"]).First(&user) if user.ID == 0 { ctx.Status(fiber.StatusBadRequest) return ctx.JSON(fiber.Map{ "message": "ログイン情報に誤りがあります", }) } // パスワードチェック err := user.ComparePassword(data["password"]) if err != nil { ctx.Status(fiber.StatusBadRequest) return ctx.JSON(fiber.Map{ "message": "ログイン情報に誤りがあります", }) } // トークンの発行 payload := jwt.StandardClaims{ Subject: strconv.Itoa(int(user.ID)), ExpiresAt: time.Now().Add(time.Hour * 24).Unix(), } token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, payload).SignedString([]byte("secret")) if err != nil { ctx.Status(fiber.StatusBadRequest) return ctx.JSON(fiber.Map{ "message": "ログイン情報に誤りがあります", }) } // Cookieに保存 cookie := fiber.Cookie{ Name: "jwt", Value: token, Expires: time.Now().Add(time.Hour * 24), HTTPOnly: true, } ctx.Cookie(&cookie) return ctx.JSON(fiber.Map{ "message": "success", }) } func User(ctx *fiber.Ctx) error { id, _ := middleware.GetUserID(ctx) // ユーザー検索 var user models.User database.DB.Where("id = ?", id).First(&user) return ctx.JSON(user) } func Logout(ctx *fiber.Ctx) error { // cookieをクリアする cookie := fiber.Cookie{ Name: "jwt", Value: "", Expires: time.Now().Add(-time.Hour * 24), // -を指定 HTTPOnly: true, } ctx.Cookie(&cookie) return ctx.JSON(fiber.Map{ "message": "success", }) } func UpdateInfo(ctx *fiber.Ctx) error { var data map[string]string // リクエストデータをパースする if err := ctx.BodyParser(&data); err != nil { return err } // cookieからidを取得する id, _ := middleware.GetUserID(ctx) user := models.User{ FirstName: data["first_name"], LastName: data["last_name"], Email: data["email"], } user.ID = id // ユーザー情報更新 database.DB.Model(&user).Updates(&user) return ctx.JSON(user) } func UpdatePassword(ctx *fiber.Ctx) error { var data map[string]string // リクエストデータをパースする if err := ctx.BodyParser(&data); err != nil { return err } // パスワードチェック if data["password"] != data["password_confirm"] { ctx.Status(fiber.StatusBadRequest) // 400 return ctx.JSON(fiber.Map{ "message": "パスワードに誤りがあります", }) } // cookieからidを取得する id, _ := middleware.GetUserID(ctx) user := models.User{} user.ID = id // パスワードセット user.SetPassword(data["password"]) // ユーザー情報更新 database.DB.Model(&user).Updates(&user) return ctx.JSON(user) } |
コメントを残す
コメントを投稿するにはログインしてください。