Gin Web Framework part 9

Gin Web Framework part 9
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 (
	&quotlog&quot
	&quotnet/http&quot
	&quottime&quot
	&quotgithub.com/gin-gonic/gin&quot
	&quotgolang.org/x/sync/errgroup&quot
)

var (
	g errgroup.Group
)

func r01() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET(&quot/&quot, func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				&quotcode&quot:  http.StatusOK,
				&quoterror&quot: &quotWelcome server 01&quot,
			},
		)
	})
	return e
}

func r02() http.Handler {
	e := gin.New()
	e.Use(gin.Recovery())
	e.GET(&quot/&quot, func(c *gin.Context) {
		c.JSON(
			http.StatusOK,
			gin.H{
				&quotcode&quot:  http.StatusOK,
				&quoterror&quot: &quotWelcome server 02&quot,
			},
		)
	})
	return e
}

func main() {
	server01 := &http.Server{
		Addr:         &quot:8080&quot,
		Handler:      r01(),
		ReadTimeout:  5 * time.Second,
		WriteTimeout: 10 * time.Second,
	}

	server02 := &http.Server{
		Addr:         &quot:8081&quot,
		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:&quotfield_a&quot`
}

type StructB struct {
    NestedStruct StructA
    FieldB string `form:&quotfield_b&quot`
}

type StructC struct {
    NestedStructPointer *StructA
    FieldC string `form:&quotfield_c&quot`
}

type StructD struct {
    NestedAnonyStruct struct {
        FieldX string `form:&quotfield_x&quot`
    }
    FieldD string `form:&quotfield_d&quot`
}

func GetDataB(c *gin.Context) {
    var b StructB
    c.Bind(&b)
    c.JSON(200, gin.H{
        &quota&quot: b.NestedStruct,
        &quotb&quot: b.FieldB,
    })
}

func GetDataC(c *gin.Context) {
    var b StructC
    c.Bind(&b)
    c.JSON(200, gin.H{
        &quota&quot: b.NestedStructPointer,
        &quotc&quot: b.FieldC,
    })
}

func GetDataD(c *gin.Context) {
    var b StructD
    c.Bind(&b)
    c.JSON(200, gin.H{
        &quotx&quot: b.NestedAnonyStruct,
        &quotd&quot: b.FieldD,
    })
}

func main() {
    r := gin.Default()
    r.GET(&quot/getb&quot, GetDataB)
    r.GET(&quot/getc&quot, GetDataC)
    r.GET(&quot/getd&quot, GetDataD)

    r.Run()
}

نحوه اجرا:

$ curl &quothttp://localhost:8080/getb?field_a=hello&field_b=world&quot
{&quota&quot:{&quotFieldA&quot:&quothello&quot},&quotb&quot:&quotworld&quot}

$ curl &quothttp://localhost:8080/getc?field_a=hello&field_c=world&quot
{&quota&quot:{&quotFieldA&quot:&quothello&quot},&quotc&quot:&quotworld&quot}

$ curl &quothttp://localhost:8080/getd?field_x=hello&field_d=world&quot
{&quotd&quot:&quotworld&quot,&quotx&quot:{&quotFieldX&quot:&quothello&quot}}


قسمت پنجم Try to bind body into different structs

فرض کنید یه استراکچر دارید که تایپ های مختلف برای ارسال رو پوشش میده و میخواید هم از json و هم از xml اون استراکچر استفاده کنید. برای این کار از متد c.ShouldBindBodyWith استفاده می کنیم

به صورت کد زیر:

type formA struct {
  Foo string `json:&quotfoo&quot xml:&quotfoo&quot binding:&quotrequired&quot`
}

type formB struct {
  Bar string `json:&quotbar&quot xml:&quotbar&quot binding:&quotrequired&quot`
} 
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 هست

https://blog.golang.org/h2push
https://blog.golang.org/h2push

در شکل این توضبح دیگه خیلی واضح هست پس من توضیح نمیدم میرم سر اصل قضیه

اینم نمونه کد:

package main

import (
	&quothtml/template&quot
	&quotlog&quot
	&quotgithub.com/gin-gonic/gin&quot
)

var html = template.Must(template.New(&quothttps&quot).Parse(`
<html>
<head>
  <title>Https Test</title>
  <script src=&quot/assets/app.js&quot>
</head>
<body>
  <h1 style=&quotcolor:red;&quot>Welcome, Ginner!</h1>
</body>
</html>
`))

func main() {
	r := gin.Default()
	r.Static(&quot/assets&quot, &quot./assets&quot)
	r.SetHTMLTemplate(html)
	r.GET(&quot/&quot, func(c *gin.Context) {
		if pusher := c.Writer.Pusher(); pusher != nil {
			if err := pusher.Push(&quot/assets/app.js&quot, nil); err != nil {
				log.Printf(&quotFailed to push: %v&quot, err)
			}
		}
		c.HTML(200, &quothttps&quot, gin.H{
			&quotstatus&quot: &quotsuccess&quot,
		})
	})
	r.RunTLS(&quot:8080&quot, &quot./testdata/server.pem&quot, &quot./testdata/server.key&quot)
}

برای استفاده از چنین قابلیتی از متد 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 (
	&quotlog&quot
	&quotnet/http&quot
	&quotgithub.com/gin-gonic/gin&quot
)

func main() {
	r := gin.Default()
	gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
		log.Printf(&quotendpoint %v %v %v %v\n&quot, httpMethod, absolutePath, handlerName, nuHandlers)
	}

	r.POST(&quot/foo&quot, func(c *gin.Context) {
		c.JSON(http.StatusOK, &quotfoo&quot)
	})

	r.GET(&quot/bar&quot, func(c *gin.Context) {
		c.JSON(http.StatusOK, &quotbar&quot)
	})

	r.GET(&quot/status&quot, func(c *gin.Context) {
		c.JSON(http.StatusOK, &quotok&quot)
	})

	// Listen and Server in http://0.0.0.0:8080
	r.Run()
}


قسمت هشتم Set and get a cookie

نحوه ست کردن و دریافت کوگی

import (
    &quotfmt&quot
    &quotgithub.com/gin-gonic/gin&quot
)

func main() {

    router := gin.Default()
    router.GET(&quot/cookie&quot, func(c *gin.Context) {
        cookie, err := c.Cookie(&quotgin_cookie&quot)
        if err != nil {
            cookie = &quotNotSet&quot
            c.SetCookie(&quotgin_cookie&quot, &quottest&quot, 3600, &quot/&quot, &quotlocalhost&quot, false, true)
        }
        fmt.Printf(&quotCookie value: %s \n&quot, cookie)
    })
    router.Run()
}

برای ست کردن کوکی در خط ۱۰ ما یه کوکی تعریف کردیم و بهش یه نام اختصاص دادیم توی خط ۱۲ یع مقدار بهش دادیم و توی ۱۳ اومدیم گفتیم که این کوکی رو توی این آدرس و توی این پورت ست کن

پایان اموزش فرم ورک gin اگه سوالی داشتید زیر هر پست برامون کامنت کنید