1.ジンの紹介
Gin は、 Go で書かれたHTTP Web フレームワークです。これは、martini に似た API フレームワークですが、パフォーマンスが優れており、httprouter よりも優れており、ほぼ 40 倍高速です。 ここをクリックして、Gin 公式中国語ドキュメントにアクセスしてください。
2. インストール
1.ジンをインストールする
go get -u github.com/gin-gonic/gin
2. コードへの導入
import "github.com/gin-gonic/gin"
3. 簡単な例、検証
次の内容の新しいファイル main.go を作成します。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
// 1.创建路由
r := gin.Default()
// 2.绑定路由规则,执行的函数
// gin.Context,封装了request和response
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "hello World!")
})
// 3.监听端口,默认在8080
// Run("")里面不指定端口号默认为8080
r.Run(":8000")
}
実行後、 http://localhost:8000/にアクセスします。
3. クロスドメイン レンダリングのフロントエンドと構成
1. HTML テンプレートをレンダリングする
Gin は、HTML テンプレートのロード、テンプレート パラメーターに従って構成し、対応するデータを返すことをサポートしています。これは本質的には文字列の置換です。LoadHTMLGlob() メソッドは、テンプレート ファイルをロードできます (パラメーターはレンダリングされる HTML テンプレート ファイルです。パラメーターが相対パスの場合は、実行パスの相対パスです)。
a. 単一ファイルをレンダリングする
r.LoadHTMLGlob("web/index.html")
b. フォルダー内のすべてのファイルをレンダリングします。
r.LoadHTMLGlob("web/*")
c. フォルダー内の html サフィックスが付いたすべてのファイルをレンダリングします。
例:
r.LoadHTMLGlob("web/*.html")
2. テンプレートのセグメンテーションを定義する
r.Delims("<<<", ">>>")
最初のパラメータはテンプレート タグの開始タグ、
2 番目のパラメータはテンプレート タグの終了タグです。
3. 静的ファイルとディレクトリをレンダリングする
静的ファイルを導入する必要がある場合は、静的ファイルのディレクトリを定義する必要があります
r.Static("/assets", "./static/assets")
アセットのディレクトリ構造が
指定された
リソースは、
r がルーティング グループの場合、ルーティング グループのパスは、プレフィックスを含む資産の前に接続する必要があります。
4. リダイレクト
// 重定向两种默认应支持的首页访问方式
router.GET("/", func(c *gin.Context) {
//重定向到/index.html
c.Redirect(302, "/index.html")
})
router.GET("/index", func(c *gin.Context) {
//重定向到/index.html
c.Redirect(302, "/index.html")
})
router.GET("/index.html", func(c *gin.Context) {
//返回渲染的html模板中的index.html
c.HTML(http.StatusOK, "index.html", gin.H{
"baseUrl": "http://" + host,
})
})
5. クロスドメインの構成
次()
r.Use(Next())
クロスドメインを許可する
// 允许跨域
func Next() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers,Authorization,User-Agent, Keep-Alive, Content-Type, X-Requested-With,X-CSRF-Token,AccessToken,Token")
c.Header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, OPTIONS")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
// 放行所有OPTIONS方法
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusAccepted)
}
c.Next()
}
}
6. レスポンス形式 c.String() c.JSON() c.JSONP() c.XML() c.HTML()
(1) 文字列を返す
r.GET("/news", func(c *gin.Context) {
aid := c.Query("aid")
c.String(200, "aid=%s", aid)
})
(2) JSONデータを返す
//方法一:自己拼接JSON
r.GET("/json", func(c *gin.Context) {
//返回json数据,使用 map[string]interface
//c.JSON(返回的状态码, 任意类型的数据(如:map,struct,...)
c.JSON(200, map[string]interface{}{
"success": true,
"msg": "你好",
})
})
//方法二:gin中的H函数
r.GET("/json2", func(c *gin.Context) {
//返回json数据,使用gin中的H函数, gin.H 是 map[string]interface{}的缩写
c.JSON(200, gin.H{
"success": true,
"msg": "你好gin",
})
})
//方法三:使用结构体
r.GET("/json3", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题",
Desc: "说明",
Content: "内容",
}
c.JSON(200, a)
})
JSOPN: jsonp リクエストは主にクロスドメインの問題を解決するために使用されます。
//jsonp请求 主要用来解决跨域问题
//http://127.0.0.1:8080/jsonp?callback=call
//call({"title":"标题-jsonp","desc":"说明-jsonp","content":"内容-jsonp"});
r.GET("/jsonp", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题-jsonp",
Desc: "说明-jsonp",
Content: "内容-jsonp",
}
c.JSONP(200, a)
})
(3) XMLデータを返す
//方法一:使用gin.H返回
r.GET("/xml", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{
"success": true,
"msg": "成功xml",
})
})
//方法二:使用结构体
r.GET("/xmlStruct", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题-xmlStruct",
Desc: "说明-xmlStruct",
Content: "内容-xmlStruct",
}
c.XML(200, a)
})
(4) HTMLデータを返す
//初始化路由
r := gin.Default()
//加载templates文件中所有模板文件,以便后续c.HTML()渲染文件时使用
r.LoadHTMLGlob("templates/*")
r.GET("/news", func(c *gin.Context) {
//使用模板文件渲染HTML文件
//前提: r.LoadHTMLGlob("templates/*")
//HTML(状态码, 要渲染的文件名, 加载的参数)
c.HTML(http.StatusOK, "news.html", gin.H{
"title": "我是一个news",
})
})
完全なコード例は次のとおりです。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
type Article struct {
Title string `json:"title"`
Desc string `json:"desc"`
Content string `json:"content"`
}
func main() {
//初始化路由
r := gin.Default()
//加载templates文件中所有模板文件,以便后续c.HTML()渲染文件时使用
r.LoadHTMLGlob("templates/*")
//配置路由
r.GET("/", func(c *gin.Context) {
c.String(200, "首页")
})
r.GET("/json", func(c *gin.Context) {
//返回json数据,使用 map[string]interface
//c.JSON(返回的状态码, 任意类型的数据(如:map,struct,...)
c.JSON(200, map[string]interface{}{
"success": true,
"msg": "你好",
})
})
r.GET("/json2", func(c *gin.Context) {
//返回json数据,使用gin中的H函数
c.JSON(200, gin.H{
"success": true,
"msg": "你好gin",
})
})
r.GET("/json3", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题",
Desc: "说明",
Content: "内容",
}
c.JSON(200, a)
})
//jsonp请求 主要用来解决跨域问题
//http://127.0.0.1:8080/jsonp?callback=call
//call({"title":"标题-jsonp","desc":"说明-jsonp","content":"内容-jsonp"});
r.GET("/jsonp", func(c *gin.Context) {
//实例化一个结构体
a := &Article{
Title: "标题-jsonp",
Desc: "说明-jsonp",
Content: "内容-jsonp",
}
c.JSONP(200, a)
})
r.GET("/xml", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{
"success": true,
"msg": "成功xml",
})
})
r.GET("/news", func(c *gin.Context) {
//使用模板文件渲染HTML文件
//前提: r.LoadHTMLGlob("templates/*")
//HTML(状态码, 要渲染的文件名, 加载的参数)
c.HTML(http.StatusOK, "news.html", gin.H{
"title": "我是一个news",
})
})
r.GET("/goods", func(c *gin.Context) {
//使用模板文件渲染HTML文件
//前提: r.LoadHTMLGlob("templates/*")
//HTML(状态码, 要渲染的文件名, 加载的参数)
c.HTML(http.StatusOK, "goods.html", gin.H{
"title": "我是一个goods",
"price": 12.99,
})
})
r.Run() // 启动一个web服务
}
4. ルーティング関連
1.ルーティンググループを作成する
// 创建路由组
// 根据需要,可以为这种多级的路由组:r.Group("/v1/user")
userApi:= r.Group("/user")
// 创建用户
// 匹配POST请求的 /user
userApi.POST("", userCreate)
// 修改用户
// 匹配PUT请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.PUT("/:id", userUpdate)
// 获取用户
// 匹配GET请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.GET("/:id", userGet)
// 查询用户
// 匹配GET请求的 /user/list
userApi.GET("/list", userQuery)
// 删除用户
// 匹配DELETE请求的 /user/1 但不会匹配 /user/ 或者 /user
userApi.DELETE("/:id", userDelete)
休息スタイルの API
2. ルーティングパラメータを取得する
a. APIパラメータ
Context のParamメソッドを通じてAPI パラメータを取得する
userApi.GET("/:id/:name", userGet)
最初のパラメータ: URL パスパラメータの ID と名前の値を取得します。
2番目のパラメータ: userGet関数
func userGet(ctx *gin.Context) {
//api参数可以为单个或者多个也可以拼接多级
//ctx.Param()函数获取时参数需要与api中的名称一致才能获取到
id := ctx.Param("id")
name:= ctx.Param("name")
ctx.JSON(http.StatusOK, gin.H{"data": id,"name":name})
return
}
http://localhost:8000/user/1/admin
b. URLパラメータ
Context の Query メソッドと DefaultQuery メソッドを通じて URL パラメータを取得します
userApi.GET("/list", userQuery)
userQuery メソッド
func userQuery(ctx *gin.Context) {
//获取
id := ctx.Query("id")
//获取,第二个参数为获取为空的默认值,如果参数不存在则放回第二个参数
name := ctx.DefaultQuery("name", "user")
ctx.JSON(http.StatusOK, gin.H{"data": id, "name": name})
return
}
注: デフォルト値は、クライアントがリクエストにこのパラメータを含めない場合、サーバーが名前を取得し、デフォルト値は「user」です。
このインターフェイスにアクセスするには、http://localhost:8000/user/list の後ろにクエリ パラメータを結合します。
クライアントがパラメータを渡さない場合は、デフォルト値が使用されます。
クライアントがパラメータを渡した場合、デフォルト値は採用されません。
c. フォームパラメータ
フォームパラメータをテストしてリクエストの効果を観察するには、postman をインストールして、Context の PostForm メソッドを通じてフォームパラメータを取得する必要があります。
userApi.POST("", userCreate)
userCreate関数は
func userCreate(ctx *gin.Context) {
id := ctx.PostForm("id")
name := ctx.PostForm("name")
ctx.JSON(http.StatusOK, gin.H{"data": id, "name": name})
return
}
図に示すように、このインターフェイスにアクセスします
d. jsonパラメータ
json パラメーターをテストしてリクエストの効果を観察するには、postman をインストールして、Context のGetRawDataまたは ShouldBindJSON メソッドを通じてフォーム パラメーターを取得する必要があります。
userApi.PUT("/:id", userUpdate)
userUpdate関数は
GetRawData方法
func userUpdate(ctx *gin.Context) {
b, err := ctx.GetRawData() // 从ctx.Request.Body读取请求数据
if err !=nil{
fmt.print(err)
}
// 定义map或结构体
var m map[string]interface{}
// 反序列化
_ = json.Unmarshal(b, &m)
ctx.JSON(http.StatusOK, gin.H{"data": m["id"], "name": m["name"]})
return
}
ShouldBindJSON方法
// 先定义结构
type User struct {
Id string `form:"id" json:"id" binding:"required"`
Name string `form:"name" json:"name" binding:"required"`
}
// 函数实现
func userUpdate(ctx *gin.Context) {
var user User
if err := ctx.ShouldBindJSON(&user); err == nil {
ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})
return
} else {
ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
return
}
}
e. パラメータのバインディング
リクエスト関連のパラメータをより便利に取得し、開発効率を向上させるために、リクエストの Content-Type に基づいてリクエストのデータ型を識別し、リフレクション メカニズムを使用してリクエスト内のフォーム、JSON、XML、およびその他のパラメータを自動的に抽出できます。構造の中に。
コード例:
取得する構造を定義する
type User struct {
Id string `form:"id" json:"id" binding:"required"`
Name string `form:"name" json:"name" binding:"required"`
}
userCreate関数
func userCreate(ctx *gin.Context) {
// 实例化结构体对象
var user User
if err := ctx.ShouldBind(&user); err == nil {
// 响应体 json 格式
ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})
return
}else{
ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
return
}
}
userUpdate関数
func userUpdate(ctx *gin.Context) {
// 实例化结构体对象
var user User
if err := ctx.ShouldBind(&user); err == nil {
ctx.JSON(http.StatusOK, gin.H{"data": user.Id, "name": user.Name})
return
}else{
ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
return
}
}
3. ファイルをアップロードする
通常、パラメータはポストリクエストフォームで渡されます。
userApi.POST("/upload", userUpload)
userUpload関数
// 先定义结构
type FileUpload struct {
File *multipart.FileHeader `form:"file"`
Type string `form:"type"`
}
// 函数实现
func userUpload(ctx *gin.Context) {
var fileUpload FileUpload
//
if err := ctx.ShouldBind(&fileUpload); err == nil {
//获取运行路径
ex, err := os.Executable()
//
if err != nil {
ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
return
}
//定义接收文件的存放地址
path := filepath.Dir(ex) + string(os.PathSeparator) + fileUpload.File.Filename
//接收文件并保存到指定path
err = ctx.SaveUploadedFile(fileUpload.File, path)
ctx.JSON(http.StatusOK, gin.H{"data": path, "type": fileUpload.Type})
return
} else {
ctx.JSON(http.StatusOK, gin.H{"err": err.Error()})
return
}
}
5. ルーティンググループファイル
Gin フレームワークを使用して Go プロジェクトを作成する場合、すべてのルートを .go ファイルに記述すると、ルートの数が増えるにつれてルーティング ファイルが非常に肥大化します。
現時点では、ルートをファイルでグループ化して管理する必要があります。これにより、コードの編成とメンテナンスが容易になります。
Gin フレームワークを使用して Go プロジェクトを作成する場合、すべてのルートを .go ファイルに記述すると、ルートの数が増えるにつれてルーティング ファイルが非常に肥大化します。
現時点では、ルートをファイルでグループ化して管理する必要があります。これにより、コードの編成とメンテナンスが容易になります。
ルーティンググループ化の実装
グループ化されていない元のファイル
func main() {
router := gin.Default()
router.POST("/one/a", OneAFunc)
router.POST("/one/b", OneBFunc)
router.POST("/one/c", OneCFunc)
router.POST("/one/d", OneDFunc)
router.POST("/one/e", OneEFunc)
router.POST("/one/f", OneFFunc)
router.POST("/one/g", OneGFunc)
router.POST("/one/h", OneHFunc)
router.POST("/one/i", OneIFunc)
/*
省略n条路由
*/
router.POST("/one/x", OneXFunc)
router.POST("/one/y", OneYFunc)
router.POST("/one/z", OneZFunc)
router.POST("/two/a", TwoAFunc)
router.POST("/two/b", TwoBFunc)
router.POST("/two/c", TwoCFunc)
router.POST("/two/d", TwoDFunc)
router.POST("/two/e", TwoEFunc)
router.POST("/two/f", TwoFFunc)
router.POST("/two/g", TwoGFunc)
router.POST("/two/h", TwoHFunc)
router.POST("/two/i", TwoIFunc)
/*
省略n条路由
*/
router.POST("/two/x", TwoXFunc)
router.POST("/two/y", TwoYFunc)
router.POST("/two/z", TwoZFunc)
router.Run(":8080")
}
ルート論理グループ化
まず、プレフィックス /one を持つルーティング グループ 1 を作成します。
次に、POST メソッドを使用して、ルーティング グループ 1 に複数のルーティング処理関数 oneAFunc から oneZFunc を定義します。これらの関数は、/two/a から /two/z ルートの HTTP POST 要求をそれぞれ処理します。
リクエストパス
/1/a/
/トウ/ア/
// 路由分组第一组
one := router.Group("/one")
{
one.POST("/a", OneAFunc)
one.POST("/b", OneBFunc)
one.POST("/c", OneCFunc)
one.POST("/d", OneDFunc)
one.POST("/e", OneEFunc)
one.POST("/f", OneFFunc)
one.POST("/g", OneGFunc)
one.POST("/h", OneHFunc)
one.POST("/i", OneIFunc)
/*
省略n条路由
*/
one.POST("/x", OneXFunc)
one.POST("/y", OneYFunc)
one.POST("/z", OneZFunc)
}
//路由分组第二组
two := router.Group("/two")
{
two.POST("/a", twoAFunc)
two.POST("/b", twoBFunc)
two.POST("/c", twoCFunc)
two.POST("/d", twoDFunc)
two.POST("/e", twoEFunc)
two.POST("/f", twoFFunc)
two.POST("/g", twoGFunc)
two.POST("/h", twoHFunc)
two.POST("/i", twoIFunc)
/*
省略n条路由
*/
two.POST("/x", twoXFunc)
two.POST("/y", twoYFunc)
two.POST("/z", twoZFunc)
}
go コードをグループ化した後、ルートはルート グループ化を実装し、ルーティング ロジックは明確に見えますが、ルーティング ファイルは依然として 1 つのファイルに集中しており、ファイルは依然として大きいです。
ルーティングはグループ化され、ファイルに分割されて管理されます
ファイル構造
main.go
handlers/
├── one_handlers.go
└── two_handlers.go
メイン.ゴー
import (
"github.com/gin-gonic/gin"
"your_project/routers"
)
func main() {
// 创建路由
router := gin.Default()
//
routers.SetupRouter(router)
// 路由分组第一组
routers.TwoRoutersInit(router)
// 路由分组第二组
routers.OneRoutersInit(router)
// 启动服务
router.Run(":8080")
}
TwoRoutersInit() と OneRoutersInit() は、次の 2 つのファイルに対応します。
one_handlers.go
package routers
import "github.com/gin-gonic/gin"
func OneRoutersInit(engine *gin.Engine) {
one := engine.Group("/one")
{
one.POST("/a", OneAFunc)
one.POST("/b", OneBFunc)
one.POST("/c", OneCFunc)
one.POST("/d", OneDFunc)
one.POST("/e", OneEFunc)
one.POST("/f", OneFFunc)
/*
省略n条路由
*/
one.POST("/x", OneXFunc)
one.POST("/y", OneYFunc)
one.POST("/z", OneZFunc)
}
}
func OneAFunc(context *gin.Context) {
}
/*
省略N多方法
*/
func OneZFunc(context *gin.Context) {
}
two_handlers.go
package routers
import "github.com/gin-gonic/gin"
func TwoRoutersInit(engine *gin.Engine) {
two := engine.Group("/two")
{
two.POST("/a", twoAFunc)
two.POST("/b", twoBFunc)
two.POST("/c", twoCFunc)
two.POST("/d", twoDFunc)
two.POST("/e", twoEFunc)
two.POST("/f", twoFFunc)
two.POST("/g", twoGFunc)
two.POST("/h", twoHFunc)
two.POST("/i", twoIFunc)
/*
省略n条路由
*/
two.POST("/x", twoXFunc)
two.POST("/y", twoYFunc)
two.POST("/z", twoZFunc)
}
}
func twoAFunc(context *gin.Context) {
}
/*
省略n多方法
*/
func twoZFunc(context *gin.Context) {
}
注: 各ルートは {} 内に配置する必要があり、各ルートに対応するメソッドには固定コンテキスト *gin.Context があり、コンテキストの風状態のリクエストと応答は
公式の例:
メイン.ゴー
// main.go
package main
import (
"github.com/gin-gonic/gin"
"your_project/routers"
)
func main() {
r := gin.Default()
routers.SetupRouter(r)
r.Run() // listen and serve on 0.0.0.0:8080
}
ルーター/router.go
package routers
import (
"github.com/gin-gonic/gin"
)
func SetupRouter(r *gin.Engine) {
v1 := r.Group("/v1")
{
v1.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello",
})
})
}
v2 := r.Group("/v2")
{
v2.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello v2",
})
})
}
}
6. ミドルウェア
1. 統合登録ミドルウェア
グローバルルーティングまたは登録済みルーティンググループに対してミドルウェアを統一登録する
r.Use(CheckToken())
2. ミドルウェアを別途登録する
userApi.POST("/upload",CheckToken(),userUpload)
3. ミドルウェア機能の実装
func CheckToken() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证不通过直接跳出
//c.JSON(http.StatusBadRequest, gin.H{"msg": "need token"})
//c.Abort()
//return
// 验证通过继续
c.Next()
}
}
ミドルウェアのシナリオ
ゴージンJWT
https://blog.csdn.net/u013302168/article/details/132178429
7. go モジュールを使用して vscode にパッケージをインポートする方法
Go のバージョン管理は go モジュールを使用します
(1) 現在、2 つのパッケージ moduleA と moduleB があるとします。
moduleA パッケージは moduleB パッケージをインポートし、その New メソッドを使用します。
(注: 関数名は大文字で始まる場合にのみ引用符で囲むことができます)
moduleB/moduleB.go の内容は以下のとおりです。
package moduleB
import "fmt"
func New(){
fmt.Println("moduleB.New")
}
1. 2 つの状況で話し合います。
(1) 同一プロジェクト内
注: 1 つのプロジェクトで複数のパッケージを定義できます。
ディレクトリ構造は次のとおりです。
次に、moduleA の mod を作成し、統合ターミナルで開き、コマンド ラインに入力します。
go mod init moduleA
実行後、プロジェクトのルートディレクトリに mod.go ファイルが生成されるので、 go mod tiny を実行して依存関係をインストールします。
moduleA の go.mod の内容は次のとおりです。
module moduleA
go 1.17
modleA の main.go の内容は次のとおりです。
package main
import (
"fmt"
"moduleA/moduleB"
)
func main() {
moduleB.New()
fmt.Println("main")
}
(2) 別プロジェクトで実施
ここで最初のステップが完了し、次は第 2 ステップに進みます。
まず、moduleB のコードを取り出して、別のプロジェクトの下にあるパッケージへの参照を実装しましょう。
この時点ではmoduleBのパッケージ(フォルダ)にはmodファイルが存在しないので、自分でmodのinitを行う必要があります。
ターミナルを開いてコマンドラインに入力します
go mod init moduleB
この時点で、moduleB の go.mod は次のようになります。
module moduleA
go 1.17
require "moduleB" v0.0.0
replace "moduleB" => "../moduleB"
moduleB の moduleB.go ファイルは移動せず、上記と同じです。
これら 2 つのパッケージは同じプロジェクト パスにないため、ローカル パッケージをインポートする必要がありますが、これらのパッケージはリモート github または他のコード リポジトリ アドレスには公開されません。
現時点では、go.mod ファイル内の replace ディレクティブを使用する必要があります。
呼び出し元、つまり moduleA/go.mod で、次のように moduleB パッケージを見つけるための相対パスを指定します。この時点で、moduleA の go.mod ファイルは次のようになります。
module moduleA
go 1.17
require "moduleB" v0.0.0
replace "moduleB" => "../moduleB"
moduleA の main.go ファイルは次のようになります。
package main
import (
"fmt"
"moduleB"
)func main() {
moduleB.New()
fmt.Println("main")
}