こんにちは、KOUKIです。
GolangのWebフレームワークであるfiberを使ってAPIを開発しています。
前回は、プロダクトのソート機能を実装しました。
今回は、ページネーション機能を実装しましょう。
尚、本記事は「React, NextJS and Golang: A Rapid Guide – Advanced」コースを参考にしています。解釈は私が勝手に付けているので、本物をみたい場合は受講をお勧めします!
<目次>
前回
作るもの
Ambassdor機能を作りたいと思います。エンドポイントは、次の通りです。
エンドポイント
- GET /api/ambassador/products/frontend
- GET /api/ambassador/products/backend
- POST /api/ambassador/links
- GET /api/ambassador/stats
- GET /api/ambassador/rankings
ページネーション
ページネーション機能をサクッと実装します。
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 |
// productController.go func ProductBackend(ctx *fiber.Ctx) error { ... // 検索 ... // ソート ... // ページネーション var total = len(searchProducts) // デフォルトは"1"ページ page, _ := strconv.Atoi(ctx.Query("page", "1")) // 1ページ最大9個の商品 perPage := 9 var data []models.Product if total <= page*perPage && total >= (page-1)*perPage { data = searchProducts[(page-1)*perPage : total] } else if total >= page*perPage { data = searchProducts[(page-1)*perPage : page*perPage] } else { data = []models.Product{} } // 1ページ目 -> 0 ~ 8 // 2パージ目 -> 9 ~ 17 return ctx.JSON(fiber.Map{ "data": data, "total": total, "page": page, "last_page": total/perPage + 1, }) } |
前回のソート機能と同じように、URLの末尾に「page」キーワードをつけ、ページを示す番号とともにリクエストを送ると、指定したページのプロダクトが取得できます。
検証
以下のパラメータで、検証をしてみましょう。
- URL: http://localhost:8000/api/ambassador/products/backend?page=1
- 形式: GET

page=1を指定したので、プロダクトの最初のデータが取得できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "data":[ {"id": 2, "title": "XqlRFSg", "description": "RRkTYHm", "image": "http://www.UJJHGJR.org/svPoDXd",…}, {"id": 3, "title": "FXRDomL", "description": "MyUmXVs", "image": "http://ACCQyXo.org/",…}, {"id": 4, "title": "fljYvbP", "description": "DtVyXKu", "image": "http://cLVxXaw.ru/RMBpJjy.html",…}, {"id": 5, "title": "lZVYbVy", "description": "IfhGxRT", "image": "http://www.gYcNeUf.ru/",…}, {"id": 6, "title": "ClrREbG", "description": "nMqxgwp", "image": "https://hvvYQGS.org/uCQWTHN",…}, {"id": 7, "title": "TDJISxL", "description": "GmbMLVE", "image": "http://RyZeQJU.com/qGLVSSi.php",…}, {"id": 8, "title": "wcEYQbo", "description": "sHsIXnb", "image": "http://dYgSKcG.info/OeQreUY.html",…}, {"id": 9, "title": "jZKFHfB", "description": "aFuDtLQ", "image": "https://www.TtxvPCW.info/OjPPsvf",…}, {"id": 10, "title": "MrpCuDm", "description": "pRMAUTu", "image": "http://RuJhREX.ru/",…} ], "last_page": 4, "page": 1, "total": 30 } |
次のページのデータも取得してみましょう。
- URL: http://localhost:8000/api/ambassador/products/backend?page=2

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "data":[ {"id": 11, "title": "mBsiEmC", "description": "YkwTuUP", "image": "https://SxWcBTD.info/yQsViZN",…}, {"id": 12, "title": "IodqwDn", "description": "oIaSxvq", "image": "https://www.QkaCRme.ru/mnykMEV",…}, {"id": 13, "title": "KWlMGEG", "description": "RnyLJFe", "image": "http://ssaYJpo.net/XMVdrVc",…}, {"id": 14, "title": "rmSkvxS", "description": "TVfHlXB", "image": "https://www.IRdWQkU.net/UqINJMR",…}, {"id": 15, "title": "icBdRsy", "description": "UXYHidE", "image": "http://SHFFMvL.com/ySvfRRr",…}, {"id": 16, "title": "eqDXAwP", "description": "xbqJOGI", "image": "https://HaMKkBF.ru/WiylslV.html",…}, {"id": 17, "title": "QnBtQIi", "description": "hnDyMIS", "image": "http://UpbQHGx.ru/iosZpIX",…}, {"id": 18, "title": "FMQOiaJ", "description": "fgodXdw", "image": "http://IxOKriF.net/xJKigFM.html",…}, {"id": 19, "title": "WhQuisN", "description": "LGQGudN", "image": "http://NDVegSK.net/HVorQMU.php",…} ], "last_page": 4, "page": 2, "total": 30 } |
OKですね。
次回
次回は、キャッシュの更新処理を実装しましょう。
Go言語まとめ
ソースコード
ここまでのソースコードを以下に記載します。
productController.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 188 189 190 191 192 193 |
// productController.go package controllers import ( "admin/src/database" "admin/src/models" "context" "encoding/json" "sort" "strconv" "strings" "time" "github.com/gofiber/fiber/v2" ) func Products(ctx *fiber.Ctx) error { var products []models.Product // 全てのプロダクトを取得 database.DB.Find(&products) return ctx.JSON(products) } func CreateProducts(ctx *fiber.Ctx) error { var product models.Product // リクエストデータをパースする if err := ctx.BodyParser(&product); err != nil { return err } // プロダクト取得 database.DB.Create(&product) return ctx.JSON(product) } func GetProduct(ctx *fiber.Ctx) error { // リクエストからIDを取得 id, _ := strconv.Atoi(ctx.Params("id")) var product models.Product product.ID = uint(id) // プロダクト検索 database.DB.Find(&product) return ctx.JSON(product) } func UpdateProduct(ctx *fiber.Ctx) error { // リクエストからIDを取得 id, _ := strconv.Atoi(ctx.Params("id")) product := models.Product{} product.ID = uint(id) if err := ctx.BodyParser(&product); err != nil { return err } // プロダクト更新 database.DB.Model(&product).Updates(&product) return ctx.JSON(product) } func DeleteProduct(ctx *fiber.Ctx) error { // リクエストからIDを取得 id, _ := strconv.Atoi(ctx.Params("id")) product := models.Product{} product.ID = uint(id) // プロダクト削除 database.DB.Delete(&product) return nil } func ProductFrontend(ctx *fiber.Ctx) error { var products []models.Product var c = context.Background() redisKey := "products_frontend" expiredTime := 30 * time.Minute // products_frontend keyでRedisからデータを取得 result, err := database.Cache.Get(c, redisKey).Result() if err != nil { database.DB.Find(&products) // Redisにデータを格納するため、エンコードする productBytes, err := json.Marshal(&products) if err != nil { panic(err) } // products_fronend keyでRedisにデータを格納 err = database.Cache.Set(c, redisKey, productBytes, expiredTime).Err() if err != nil { panic(err) } } else { // デコードする json.Unmarshal([]byte(result), &products) } return ctx.JSON(products) } func ProductBackend(ctx *fiber.Ctx) error { var products []models.Product var c = context.Background() redisKey := "products_backend" expiredTime := 30 * time.Minute // キャッシュ操作 result, err := database.Cache.Get(c, redisKey).Result() if err != nil { database.DB.Find(&products) productBytes, err := json.Marshal(&products) if err != nil { panic(err) } database.Cache.Set(c, redisKey, productBytes, expiredTime).Err() } else { json.Unmarshal([]byte(result), &products) } var searchProducts []models.Product // 検索 // urlの?q=XXXXから文字列を取得 if q := ctx.Query("q"); q != "" { // 大文字小文字の区別をなくすため、全て小文字扱いにする lower := strings.ToLower(q) for _, product := range products { // 検索条件1: Title if strings.Contains(strings.ToLower(product.Title), lower) || // 検索条件2: Description strings.Contains(strings.ToLower(product.Description), lower) { searchProducts = append(searchProducts, product) } } } else { // 検索しない場合は、全てのデータを返却 searchProducts = products } // ソート if sortParam := ctx.Query("sort"); sortParam != "" { sortLower := strings.ToLower(sortParam) if sortLower == "asc" { sort.Slice(searchProducts, func(i, j int) bool { return searchProducts[i].Price < searchProducts[j].Price }) } else if sortLower == "desc" { sort.Slice(searchProducts, func(i, j int) bool { return searchProducts[i].Price > searchProducts[j].Price }) } } // ページネーション var total = len(searchProducts) // デフォルトは"1"ページ page, _ := strconv.Atoi(ctx.Query("page", "1")) // 1ページ最大9個の商品 perPage := 9 var data []models.Product if total <= page*perPage && total >= (page-1)*perPage { data = searchProducts[(page-1)*perPage : total] } else if total >= page*perPage { data = searchProducts[(page-1)*perPage : page*perPage] } else { data = []models.Product{} } // 1ページ目 -> 0 ~ 8 // 2パージ目 -> 9 ~ 17 return ctx.JSON(fiber.Map{ "data": data, "total": total, "page": page, "last_page": total/perPage + 1, }) } |
コメントを残す
コメントを投稿するにはログインしてください。