این آموزش مقدمات نوشتن یک وبسرویس RESTful API را با استفاده از زبان Go و فریمورک Gin آموزش میدهد.
بیشترین بهره را از این آموزش خواهید برد اگر آشنایی پایهای با Go و ابزارهای آن داشته باشید. اگر این اولین مواجهه شما با Go است، برای آشنایی سریع لطفا آموزش «شروع کار با Go» را ببینید.
فریمورک Gin بسیاری از کارهای کدنویسی مربوط به ساخت برنامههای وب (از جمله وبسرویسها) را ساده میکند. در این آموزش، از Gin برای مسیریابی درخواستها (Routing)، دریافت جزئیات درخواست و تبدیل دادهها به فرمت JSON (Marshalling) برای پاسخها استفاده خواهید کرد.
در این آموزش، یک سرور RESTful API با دو مسیر (Endpoint) میسازید. پروژه نمونه شما مخزنی از دادههای مربوط به آلبومهای موسیقی جاز قدیمی خواهد بود.
این آموزش شامل بخشهای زیر است:
-
طراحی مسیرهای API (Endpoints)
-
ایجاد پوشه برای کدها
-
ایجاد دادهها
-
نوشتن یک Handler برای بازگرداندن تمام آیتمها
-
نوشتن یک Handler برای افزودن آیتم جدید
-
نوشتن یک Handler برای بازگرداندن یک آیتم خاص
پیشنیازها
-
Go: توصیه میشود از آخرین نسخه Go برای دنبال کردن این آموزش استفاده کنید.
-
یک ابزار برای ویرایش کد: هر ویرایشگر متنی (Text Editor) یا IDE کار شما را راه میاندازد.
-
ترمینال (Command Terminal): زبان Go در هر ترمینالی در لینوکس، مک و PowerShell یا cmd در ویندوز به خوبی کار میکند.
-
ابزار curl: در لینوکس و مک به صورت پیشفرض نصب است. در ویندوز ۱۰ (نسخه ۱۷۰۶۳ به بعد) نیز به صورت پیشفرض همراه سیستمعامل است.
۱. طراحی مسیرهای API (Endpoints)
شما API برپایه یک فروشگاه آلبومهای موسیقی روی صفحه گرامافون (Vinyl) میسازید. بنابراین باید مسیرهایی ایجاد کنید که کاربر بتواند لیست آلبومها را دریافت کند یا آلبوم جدیدی اضافه کند.
هنگام توسعه یک API، معمولا کار با طراحی Endpointها آغاز میشود. اگر فهم مسیرها ساده باشد، کاربران API موفقتر خواهند بود.
مسیرهایی که در این آموزش میسازید:
-
GET /albumsدریافت لیست تمام آلبومها بهصورت JSON. -
POST /albumsافزودن یک آلبوم جدید از روی دادههای ارسالشده در درخواست بهصورت JSON. -
GET /albums/:idدریافت اطلاعات یک آلبوم بر اساس شناسهاش (ID) بهصورت JSON.
۲. ایجاد پوشه برای کدها
برای شروع، یک پروژه برای کدهایی که خواهید نوشت ایجاد کنید.
ترمینال را باز کرده و به مسیر Home بروید:
در لینوکس یا مک:
$ cd
در ویندوز:
C:\> cd %HOMEPATH%
سپس یک پوشه به نام web-service-gin بسازید و وارد آن شوید:
$ mkdir web-service-gin
$ cd web-service-gin
یک ماژول Go برای مدیریت وابستگیها (Dependencies) ایجاد کنید. دستور go mod init را همراه با مسیر ماژول اجرا کنید:
$ go mod init example/web-service-gin
go: creating new go.mod: module example/web-service-gin
این دستور یک فایل go.mod میسازد که وابستگیهای افزوده شده در آن ردگیری میشوند.
۳. ایجاد دادهها
برای ساده نگه داشتن آموزش، دادهها را در حافظه (Memory) ذخیره میکنیم. در یک API واقعی، برنامه با یک پایگاهداده (Database) در ارتباط است.
توجه: ذخیره دادهها در حافظه به این معناست که با هر بار متوقف کردن سرور، دادهها پاک شده و با شروع مجدد سرور دوباره از نو ساخته میشوند.
نوشتن کد
با ویرایشگر خود فایلی به نام main.go در پوشه پروژه ایجاد کنید.
در بالای فایل main.go عبارت زیر را قرار دهید:
package main
یک برنامه مستقل (در مقابل یک کتابخانه) همواره در package main قرار میگیرد.
در ادامه، استراکت (Struct) مربوط به آلبوم را تعریف کنید. از این استراکت برای ذخیره دادههای آلبوم در حافظه استفاده میشود.
تگهای Struct مانند json:"artist" مشخص میکنند که هنگام تبدیل استراکت به JSON (Serialization)، نام کلیدها چه باشد. بدون این تگها، JSON از نامهای بزرگنویسیشده حقول استراکت استفاده میکند.
// album نشاندهنده دادههای مربوط به یک آلبوم موسیقی است.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
سپس، یک اسلایس (Slice) از استراکتهای album بسازید تا دادههای اولیه را آماده کنید:
// اسلایس albums برای دادههای اولیه آلبومها.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
۴. نوشتن یک Handler برای بازگرداندن تمام آیتمها
وقتی کلاینت درخواستی به GET /albums میفرستد، میخواهیم تمام آلبومها را به صورت JSON برگردانیم.
برای این کار باید:
-
منطق آمادهسازی پاسخ را بنویسیم.
-
مسیر درخواست (Request Path) را به منطق خود نگاشت (Map) کنیم.
نوشتن کد
تابع زیر را برای دریافت لیست آلبومها اضافه کنید. تابع getAlbums فرمت JSON را از اسلایس آلبومها تولید کرده و در پاسخ مینویسد:
// getAlbums لیست تمام آلبومها را به صورت JSON پاسخ میدهد.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
در این کد:
-
تابیع به نام
getAlbumsنوشتهاید که پارامترc *gin.Contextرا میگیرد. -
gin.Contextمهمترین بخش Gin است که جزئیات درخواست را حمل کرده، JSON را اعتبارپذیری و سریالسازی میکند و موارد دیگر. -
تابع
Context.IndentedJSONاستراکت را به JSON تبدیل کرده و به پاسخ اضافه میکند. آرگومان اول آن کد وضعیت HTTP است (کدhttp.StatusOKیا همان200 OK). -
نکته: میتوانید به جای
IndentedJSONازContext.JSONاستفاده کنید تا JSON فشردهتری بفرستید. حالت Indented هنگام دیباگ کردن خواناتر است.
حالا تابع main را برای اختصاص دادن تابع Handler به مسیر Endpoint اضافه کنید:
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.Run("localhost:8080")
}
در این کد:
-
یک روتر (Router) با استفاده از
gin.Default()مقداردهی اولیه میشود. -
با استفاده از
router.GETمتد HTTP GET و مسیر/albumsرا به تابعgetAlbumsوصل میکنید. توجه کنید که نام خود تابع پاس داده میشود نه فراخوانی آن (getAlbumsنهgetAlbums()). -
با استفاده از
router.Runروتر به یکhttp.Serverمتصل شده و سرور رویlocalhost:8080روشن میشود.
در بالای فایل main.go نیز کتابخانههای مورد نیاز را وارد (Import) کنید:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
فایل main.go را ذخیره کنید.
اجرای کد
ابتدا با دستور زیر وابستگی Gin را اضافه کنید:
$ go get .
go get: added github.com/gin-gonic/gin v1.7.2
سپس برنامه را اجرا کنید:
$ go run .
در یک پنجره ترمینال جدید، با استفاده از curl درخواست ارسال کنید:
$ curl http://localhost:8080/albums
خروجی باید لیست آلبومهای اولیه را نشان دهد:
[
{
"id": "1",
"title": "Blue Train",
"artist": "John Coltrane",
"price": 56.99
},
{
"id": "2",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
},
{
"id": "3",
"title": "Sarah Vaughan and Clifford Brown",
"artist": "Sarah Vaughan",
"price": 39.99
}
]
۵. نوشتن یک Handler برای افزودن آیتم جدید
وقتی کلاینت درخواست POST به /albums میفرستد، میخواهیم آلبوم فرستاده شده در بدنه درخواست (Request Body) را به لیست موجود اضافه کنیم.
نوشتن کد
کد زیر را به فایل اضافه کنید:
// postAlbums یک آلبوم جدید را از روی JSON دریافتی در بدنه درخواست اضافه میکند.
func postAlbums(c *gin.Context) {
var newAlbum album
// فراخوانی BindJSON برای متصل کردن JSON دریافتی به newAlbum
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// اضافه کردن آلبوم جدید به اسلایس
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
در این کد:
-
از
Context.BindJSONبرای اتصال (Bind) بدنه درخواست به متغیرnewAlbumاستفاده میکنید. -
آلبوم جدید به اسلایس
albumsاضافه میشود. -
کد وضعیت
201 Created(http.StatusCreated) همراه با JSON آلبوم اضافه شده در پاسخ ارسال میشود.
تابع main را به شکل زیر به روز رسانی کنید تا مسیر POST اضافه شود:
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
اجرای کد
برنامه را متوقف کرده و دوباره با go run . اجرا کنید.
در یک ترمینال دیگر، این دستور curl را برای ارسال درخواست POST اجرا کنید:
$ curl http://localhost:8080/albums \
--include \
--header "Content-Type: application/json" \
--request "POST" \
--data '{"id": "4","title": "The Modern Sound of Betty Carter","artist": "Betty Carter","price": 49.99}'
خروجی باید شامل هدرهای HTTP و دادههای آلبوم جدید با کد وضعیت 201 Created باشد:
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Date: Wed, 02 Jun 2021 00:34:12 GMT
Content-Length: 116
{
"id": "4",
"title": "The Modern Sound of Betty Carter",
"artist": "Betty Carter",
"price": 49.99
}
۶. نوشتن یک Handler برای بازگرداندن یک آیتم خاص
وقتی کلاینت درخواست GET /albums/[id] ارسال میکند، میخواهیم آلبومی که ID آن با پارامتر مسیر مطابقت دارد برگردانیم.
نوشتن کد
تابع زیر را اضافه کنید:
// getAlbumByID آلبومی که ID آن با پارامتر id ارسالی کلاینت مطابقت دارد پیدا کرده و برمیگرداند.
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// پیمایش لیست آلبومها برای پیدا کردن ID مورد نظر
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
در این کد:
-
از
Context.Paramبرای گرفتن پارامترidاز URL استفاده میشود. -
لیست آلبومها پیمایش شده و در صورت یافتن، با کد وضعیت
200 OKبازگردانده میشود. -
در صورت پیدا نشدن آلبوم، خطای HTTP 404 با
http.StatusNotFoundو پیام مناسب برگردانده میشود.
تابع main را بهروزرسانی کنید تا مسیر /albums/:id نگاشت شود:
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
علامت دو نقطه (:) قبل از id در مسیر /albums/:id به Gin میفهماند که این بخش یک پارامتر مسیر (Path Parameter) است.
اجرای کد
برنامه را مجددا اجرا کنید و با دستور زیر آلبوم با ID برابر ۲ را درخواست کنید:
$ curl http://localhost:8080/albums/2
خروجی:
{
"id": "2",
"title": "Jeru",
"artist": "Gerry Mulligan",
"price": 17.99
}
کد کامل پروژه
در نهایت سورسکد کامل فایل main.go به شکل زیر خواهد بود:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// album نشاندهنده دادههای مربوط به یک آلبوم موسیقی است.
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// اسلایس albums برای دادههای اولیه آلبومها.
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.GET("/albums/:id", getAlbumByID)
router.POST("/albums", postAlbums)
router.Run("localhost:8080")
}
// getAlbums لیست تمام آلبومها را به صورت JSON پاسخ میدهد.
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
// postAlbums یک آلبوم جدید را از روی JSON دریافتی در بدنه درخواست اضافه میکند.
func postAlbums(c *gin.Context) {
var newAlbum album
// فراخوانی BindJSON برای متصل کردن JSON دریافتی به newAlbum
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// اضافه کردن آلبوم جدید به اسلایس
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
// getAlbumByID آلبومی که ID آن با پارامتر id ارسالی کلاینت مطابقت دارد پیدا کرده و برمیگرداند.
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
// پیمایش لیست آلبومها برای پیدا کردن ID مورد نظر
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: منبع مستندات