Go Developer(gopher-academy.ir)
Gin Web Framework part 9
- درود دوستان امروز میخوام در مورد بخش هشتم از سری مقالات فرم ورک Gin صحبت کنیم
- فهرست بخش هشتم از این مقاله
- Run multiple service using Gin
- Graceful shutdown or restart
- Build a single binary with templates
- Bind form-data request with custom struct
- Try to bind body into different structs
- http2 server push
- Define format for the log of routes
- Set and get a cookie
- Testing
میریم واسه توضیح هر بخش
قسمت اول Run multiple service using Gin
اگه چندتا سرویس داشته باشید به صورت زیر باید عمل کند. توضیحات در ادامه این کد
package main
import (
"log"
"net/http"
"time"
"github.com/gin-gonic/gin"
"golang.org/x/sync/errgroup"
)
var (
g errgroup.Group
)
func r01() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 01",
},
)
})
return e
}
func r02() http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.GET("/", func(c *gin.Context) {
c.JSON(
http.StatusOK,
gin.H{
"code": http.StatusOK,
"error": "Welcome server 02",
},
)
})
return e
}
func main() {
server01 := &http.Server{
Addr: ":8080",
Handler: r01(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
server02 := &http.Server{
Addr: ":8081",
Handler: r02(),
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
}
g.Go(func() error {
err := server01.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
return err
})
g.Go(func() error {
err := server02.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
return err
})
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
اول از همه برای اجرا کردن چندین سرویس باید از کتابحانه x/sync/errgroup استفاده کرد که توی خط ۱۲ اومدیم یه نمونه ازش ساختیم. بعدش اومدیم دوتا روت تعریف کردیم به اسم های r01 و r02 توی تابع اصلی اومدیم برای هرکدومشون یه پورت جداگانه درنظر گرفتیم و بعدش در همین تابع اصلی با استفاده از کتابخانه ای که در این فایل تعریف کردیم یعنی x/sync/errgroup اومدیم تابعی به این صورت g.Go(func() error تعریف کردیم از نمونه ای که از این کتابخانه توی خط ۱۲ تعریف کرده بودیم توی بتن این تابع اومدیم نوع سرویس های که شخصی کردیم رو بهش پاس میدیم جهت اجرا شدن
قسمت دوم Graceful shutdown or restart
چگونه سیگنالهای ورودی سیستم عامل را برای انجام graceful shutdown در یک وب سرور نوشتهمیشوند و ما آنها را به زبان Go مدیریت کنیم.
واسه توضیحات بیشتر این مقاله ویرگولی رو بخونید.
چندتا کتابخونه توی این زمینه هستن که عبارتنداز:
- manners: A polite Go HTTP server that shuts down gracefully.
- graceful: Graceful is a Go package enabling graceful shutdown of an http.Handler server.
- grace: Graceful restart & zero downtime deploy for Go servers.
- endless: Zero downtime restarts for go servers (Drop in replacement for http.ListenAndServe)
قسمت سوم Build a single binary with templates
واسه اینکه بخواید از فایل html خود یه فایل باینری بسازید کافیه مراحل زیر رو به ترتیب برید. برای اطلاعات بیشتر و همچنین یه مثال ساده به این لینک یه سری بزنید
Prepare Packages
go get github.com/gin-gonic/gin
go get github.com/jessevdk/go-assets-builder
Generate assets.go
go-assets-builder html -o assets.go
Build the server
go build -o assets-in-binary
Run
./assets-in-binary
قسمت چهارم Bind form-data request with custom struct
اگه بخواید استراکچر خودتون رو بسازید به صورت زیر می تونید استراکچر شخصی برای اطلاعات بسازید
کد زیر دیگه واضح هست انتظار دارم دیگه اینو بلد باشید
type StructA struct {
FieldA string `form:"field_a"`
}
type StructB struct {
NestedStruct StructA
FieldB string `form:"field_b"`
}
type StructC struct {
NestedStructPointer *StructA
FieldC string `form:"field_c"`
}
type StructD struct {
NestedAnonyStruct struct {
FieldX string `form:"field_x"`
}
FieldD string `form:"field_d"`
}
func GetDataB(c *gin.Context) {
var b StructB
c.Bind(&b)
c.JSON(200, gin.H{
"a": b.NestedStruct,
"b": b.FieldB,
})
}
func GetDataC(c *gin.Context) {
var b StructC
c.Bind(&b)
c.JSON(200, gin.H{
"a": b.NestedStructPointer,
"c": b.FieldC,
})
}
func GetDataD(c *gin.Context) {
var b StructD
c.Bind(&b)
c.JSON(200, gin.H{
"x": b.NestedAnonyStruct,
"d": b.FieldD,
})
}
func main() {
r := gin.Default()
r.GET("/getb", GetDataB)
r.GET("/getc", GetDataC)
r.GET("/getd", GetDataD)
r.Run()
}
نحوه اجرا:
$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
{"a":{"FieldA":"hello"},"b":"world"}
$ curl "http://localhost:8080/getc?field_a=hello&field_c=world"
{"a":{"FieldA":"hello"},"c":"world"}
$ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
{"d":"world","x":{"FieldX":"hello"}}
قسمت پنجم Try to bind body into different structs
فرض کنید یه استراکچر دارید که تایپ های مختلف برای ارسال رو پوشش میده و میخواید هم از json و هم از xml اون استراکچر استفاده کنید. برای این کار از متد c.ShouldBindBodyWith استفاده می کنیم
به صورت کد زیر:
type formA struct {
Foo string `json:"foo" xml:"foo" binding:"required"`
}
type formB struct {
Bar string `json:"bar" xml:"bar" binding:"required"`
}
func SomeHandler(c *gin.Context) {
objA := formA{}
objB := formB{}
if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
c.String(http.StatusOK, `the body should be formA`)
} else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
c.String(http.StatusOK, `the body should be formB JSON`)
} else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
c.String(http.StatusOK, `the body should be formB XML`)
} else {
// TODO : YOUR CODE
}
}
قسمت شیشم http2 server push
این روش در واقع طراحی شده http2 هست که در شکل زیر نماینگر این ارتباط از نوع http2 هست
در شکل این توضبح دیگه خیلی واضح هست پس من توضیح نمیدم میرم سر اصل قضیه
اینم نمونه کد:
package main
import (
"html/template"
"log"
"github.com/gin-gonic/gin"
)
var html = template.Must(template.New("https").Parse(`
<html>
<head>
<title>Https Test</title>
<script src="/assets/app.js">
</head>
<body>
<h1 style="color:red;">Welcome, Ginner!</h1>
</body>
</html>
`))
func main() {
r := gin.Default()
r.Static("/assets", "./assets")
r.SetHTMLTemplate(html)
r.GET("/", func(c *gin.Context) {
if pusher := c.Writer.Pusher(); pusher != nil {
if err := pusher.Push("/assets/app.js", nil); err != nil {
log.Printf("Failed to push: %v", err)
}
}
c.HTML(200, "https", gin.H{
"status": "success",
})
})
r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
}
برای استفاده از چنین قابلیتی از متد c.Writer.Pusher استفاده می کنیم اگه به شکل نگاه کنید می فهمید نخوه ارتباط با اجزای مختلف یه سایت که چگونه اونا رو برای کاربر نمایش میده یعنی با یه req ما می تونیم از سمت سرور چندین res رو داشته باشیم.
قسمت هفتم Define format for the log of routes
برای اینکه بتونیم یه لاگ اختصاصی از عملکرد سرویس هامون داشته باشیم یعنی زمانی که اجرا می کنید توی کنسول چنین چیزی داشته باشید
[GIN-debug] POST /foo --> main.main.func1 (3 handlers)
[GIN-debug] GET /bar --> main.main.func2 (3 handlers)
[GIN-debug] GET /status --> main.main.func3 (3 handlers)
باید از متد gin.DebugPrintRouteFunc استفاده کنیم که توی کد زیر نشون دادیم
import (
"log"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
}
r.POST("/foo", func(c *gin.Context) {
c.JSON(http.StatusOK, "foo")
})
r.GET("/bar", func(c *gin.Context) {
c.JSON(http.StatusOK, "bar")
})
r.GET("/status", func(c *gin.Context) {
c.JSON(http.StatusOK, "ok")
})
// Listen and Server in http://0.0.0.0:8080
r.Run()
}
قسمت هشتم Set and get a cookie
نحوه ست کردن و دریافت کوگی
import (
"fmt"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/cookie", func(c *gin.Context) {
cookie, err := c.Cookie("gin_cookie")
if err != nil {
cookie = "NotSet"
c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
}
fmt.Printf("Cookie value: %s \n", cookie)
})
router.Run()
}
برای ست کردن کوکی در خط ۱۰ ما یه کوکی تعریف کردیم و بهش یه نام اختصاص دادیم توی خط ۱۲ یع مقدار بهش دادیم و توی ۱۳ اومدیم گفتیم که این کوکی رو توی این آدرس و توی این پورت ست کن
مطلبی دیگر از این انتشارات
اگر نتونستی کارآموز برنامه نویسی در یک شرکت شوی،ناراحت نباش
مطلبی دیگر از این انتشارات
مفهوم Open Source یا دنیای باز چیست ؟!
مطلبی دیگر از این انتشارات
مهندسی کامپیوتر- مقایسه ابزار ها GO,Rust,Java