主頁 > 軟體設計 > 【博客414】Go gin框架

【博客414】Go gin框架

2021-05-06 12:08:49 軟體設計

內容:golang 的gin框架是一個比原生更高性能的web開發框架

Hello world

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

g.H

gin.H 是 map[string]interface{} 的一個快捷名稱

type H map[string]interface{}

g.Engine

Engine是框架的入口,通過Engine物件來定義服務路由資訊、組裝插件、運行服務,
正如Engine的中文意思「引擎」一樣,它就是框架的核心發動機,整個Web服務的都是由它來驅動的,

Engine 的本質只是對內置的HTTP服務器的包裝,讓它使用起來更加便捷,

gin.Default() 函式會生成一個默認的 Engine 物件,里面包含了 2 個默認的常用插件,
分別是Logger和Recovery,Logger用于輸出請求日志,Recovery確保單個請求發生panic時
記錄例外堆疊日志,輸出統一的錯誤回應,

func Default() *Engine {
    engine := New()
    engine.Use(Logger(), Recovery())
    return engine
}

route tree:

路由樹:
在 Gin 框架中,路由規則被分成了最多9棵前綴樹,每一個 HTTP Method對應一棵「前綴樹」,
樹的節點按照 URL 中的 / 符號進行層級劃分,URL 支持 :name 形式的名稱匹配,
還支持 *subpath 形式的路徑通配符 ,

每個節點都會掛接若干請求處理函式構成一個請求處理鏈 HandlersChain,
當一個請求到來時,在這棵樹上找到請求 URL 對應的節點,拿到對應的請求處理鏈來執行


type Engine struct {
  ...
  trees methodTrees
  ...
}

type methodTrees []methodTree

type methodTree struct {
    method string
    root   *node  // 樹根
}

type node struct {
  path string // 當前節點的路徑
  ...
  handlers HandlersChain // 請求處理鏈
  ...
}

type HandlerFunc func(*Context)

type HandlersChain []HandlerFunc


Engine 物件包含一個 addRoute 方法用于添加URL請求處理器,它會將對應的路徑和處理器
掛接到相應的請求樹中:
func (e *Engine) addRoute(method, path string, handlers HandlersChain)

g.RouterGroup

RouterGroup 是對路由樹的包裝,所有的路由規則最終都是由它來進行管理,
Engine結構體繼承了RouterGroup ,所以Engine直接具備了RouterGroup所有的路由管理功能,
這是為什么在 Hello World 的例子中,可以直接使用 Engine 物件來定義路由規則,
同時 RouteGroup 物件里面還會包含一個 Engine 的指標,這樣Engine和RouteGroup就成了
「你中有我我中有你」的關系,

type Engine struct {
  RouterGroup
  ...
}

type RouterGroup struct {
  ...
  engine *Engine
  ...
}

RouterGroup 實作了 IRouter 介面,暴露了一系列路由方法,這些方法最終都是通過呼叫 Engine.addRoute 方法將請求處理器掛接到路由樹中,

GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
// 匹配所有 HTTP Method
Any(string, ...HandlerFunc) IRoutes

RouterGroup 內部有一個前綴路徑屬性,它會將所有的子路徑都加上這個前綴再放進路由樹中,
有了這個前綴路徑,就可以實作URL分組功能,Engine 物件內嵌的RouterGroup物件的前綴路徑是/,
它表示根路徑,RouterGroup 支持分組嵌套,使用Group方法就可以讓分組下面再掛分組

func main() {
    router := gin.Default()

    v1 := router.Group("/v1")
    {
        v1.POST("/login", loginEndpoint)
        v1.POST("/submit", submitEndpoint)
        v1.POST("/read", readEndpoint)
    }

    v2 := router.Group("/v2")
    {
        v2.POST("/login", loginEndpoint)
        v2.POST("/submit", submitEndpoint)
        v2.POST("/read", readEndpoint)
    }

    router.Run(":8080")
}

Engine物件里面的RouterGroup物件就是第一層分組,也就是根分組,v1和v2都是根分組的子分組,

g.Context

這個物件里保存了請求的背景關系資訊,它是所有請求處理器的入口引數,

type HandlerFunc func(*Context)

type Context struct {
  ...
  Request *http.Request // 請求物件
  Writer ResponseWriter // 回應物件
  Params Params // URL匹配引數
  ...
  Keys map[string]interface{} // 自定義背景關系資訊
  ...
}


Context 物件提供了非常豐富的方法用于獲取當前請求的背景關系資訊,如果需要獲取請求中的URL引數、
Cookie、Header 都可以通過Context物件來獲取,這一系列方法是對http.Request物件的包裝,


// 獲取 URL 匹配引數  /book/:id
func (c *Context) Param(key string) string
// 獲取 URL 查詢引數 /book?id=123&page=10
func (c *Context) Query(key string) string
// 獲取 POST 表單引數
func (c *Context) PostForm(key string) string
// 獲取上傳的檔案物件
func (c *Context) FormFile(name string) (*multipart.FileHeader, error)
// 獲取請求Cookie
func (c *Context) Cookie(name string) (string, error) 
...

Context 物件提供了很多內置的回應形式,JSON、HTML、Protobuf 、MsgPack、Yaml 等,
它會為每一種形式都單獨定制一個渲染器,通常這些內置渲染器已經足夠應付絕大多數場景,
如果你覺得不夠,還可以自定義渲染器,

func (c *Context) JSON(code int, obj interface{})
func (c *Context) Protobuf(code int, obj interface{})
func (c *Context) YAML(code int, obj interface{})

...
// 自定義渲染
func (c *Context) Render(code int, r render.Render)

// 渲染器通用介面
type Render interface {
    Render(http.ResponseWriter) error
    WriteContentType(w http.ResponseWriter)
}

所有的渲染器最侄訓是需要呼叫內置的http.ResponseWriter(Context.Writer)將回應物件
轉換成位元組流寫到套接字中,

type ResponseWriter interface {
 // 容納所有的回應頭
 Header() Header
 // 寫Body
 Write([]byte) (int, error)
 // 寫Header
 WriteHeader(statusCode int)
}

插件與請求鏈:


我們撰寫業務代碼時一般也就是一個處理函式,為什么路由節點需要掛接一個函式鏈呢?

type node struct {
  path string // 當前節點的路徑
  ...
  handlers HandlersChain // 請求處理鏈
  ...
}
type HandlerFunc func(*Context)
type HandlersChain []HandlerFunc

這是因為Gin提供了插件,只有函式鏈的尾部是業務處理,前面的部分都是插件函式,
在Gin中插件和業務處理函式形式是一樣的,都是 func(*Context),
當我們定義路由時,Gin會將插件函式和業務處理函式合并在一起形成一個鏈條結構,

type Context struct {
  ...
  index uint8 // 當前的業務邏輯位于函式鏈的位置
  handlers HandlersChain // 函式鏈
  ...
}

// 挨個呼叫鏈條中的處理函式
func (c *Context) Next() {
    c.index++
    for s := int8(len(c.handlers)); c.index < s; c.index++ {
        c.handlers[c.index](c)
    }
}

Gin 在接收到客戶端請求時,找到相應的處理鏈,構造一個 Context 物件,
再呼叫它的 Next() 方法就正式進入了請求處理的全流程,

在這里插入圖片描述

Gin還支持 Abort() 方法中斷請求鏈的執行,它的原理是將 Context.index調整到一個比較大
的數字,這樣 Next() 方法中的呼叫回圈就會立即結束,
需要注意的 Abort() 方法并不是通過 panic 的方式中斷執行流,執行 Abort() 方法之后,
當前函式內后面的代碼邏輯還會繼續執行,

const abortIndex = 127
func (c *Context) Abort() {
    c.index = abortIndex
}

func SomePlugin(c *Context) {
  ...

  if condition {
    c.Abort()
    // continue executing
  }
  ...
}

如果在插件中顯示呼叫 Next() 方法,那么它就改變了正常的順序執行流,換個角度來理解,
正常的執行流就是后續的處理器是在前一個處理器的尾部執行,而嵌套執行流是讓后續的處理器
在前一個處理器進行到一半時執行,待后續處理器完成執行后,再回到前一個處理器繼續往下執行,

在這里插入圖片描述

RouterGroup 提供了 Use() 方法來注冊插件,因為 RouterGroup 是一層套一層,
不同層級的路由可能會注冊不一樣的插件,最終不同的路由節點掛接的處理函式鏈也不盡相同,

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
    group.Handlers = append(group.Handlers, middleware...)
    return group.returnObj()
}

// 注冊 Get 請求
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
    return group.handle("GET", relativePath, handlers)
}

func (g *RouterGroup) handle(method, path string, handlers HandlersChain) 
 IRoutes {
 // 合并URL (RouterGroup有URL前綴)
    absolutePath := group.calculateAbsolutePath(relativePath)
    // 合并處理鏈條
 handlers = group.combineHandlers(handlers)
    // 注冊路由樹
 group.engine.addRoute(httpMethod, absolutePath, handlers)
    return group.returnObj()
}

HTTP ERROR:

當URL請求對應的路徑不能在路由樹里找到時,就需要處理404 NotFound錯誤,
當URL的請求路徑在路由樹里找到,但是Method不匹配,就需要處理405 MethodNotAllowed錯誤,

Engine 物件為這兩個錯誤提供了處理器注冊的入口

func (engine *Engine) NoMethod(handlers ...HandlerFunc)
func (engine *Engine) NoRoute(handlers ...HandlerFunc)

例外處理器和普通處理器一樣,也需要和插件函陣列合在一起形成一個呼叫鏈,
如果沒有提供例外處理器,Gin 就會使用內置的簡易錯誤處理器,

注意這兩個錯誤處理器是定義在 Engine 全域物件上,而不是 RouterGroup,
對于非 404405 錯誤,需要用戶自定義插件來處理,對于 panic 拋出來的例外需要也
需要使用插件來處理,

表單:

當請求引數數量比較多時,使用 Context.Query() 和 Context.PostForm() 方法來獲取
引數就會顯得比較繁瑣,Gin 框架也支持表單處理,將表單引數和結構體欄位進行直接映射,


package main

import (
    "github.com/gin-gonic/gin"
)

type LoginForm struct {
    User     string `form:"user" binding:"required"`
    Password string `form:"password" binding:"required"`
}

func main() {
    router := gin.Default()
    router.POST("/login", func(c *gin.Context) {
  var form LoginForm
        if c.ShouldBind(&form) == nil {
            if form.User == "user" && form.Password == "password" {
                c.JSON(200, gin.H{"status": "you are logged in"})
            } else {
                c.JSON(401, gin.H{"status": "unauthorized"})
            }
        }
    })
    router.Run(":8080")
}

Context.ShouldBind 方法遇到校驗不通過時,會回傳一個錯誤物件告知呼叫者校驗失敗的原因,
它支持多種資料系結型別,如 XML、JSON、Query、Uri、MsgPack、Protobuf等,根據請求的
 Content-Type 頭來決定使用何種資料系結方法,

func (c *Context) ShouldBind(obj interface{}) error {
 // 獲取系結器
    b := binding.Default(c.Request.Method, c.ContentType())
    // 執行系結
 return c.ShouldBindWith(obj, b)
}

默認內置的表單校驗功能很強大,它通過結構體欄位 tag 標注來選擇相應的校驗器進行校驗,
Gin還提供了注冊自定義校驗器的入口,支持用戶自定義一些通用的特殊校驗邏輯,

Context.ShouldBind 是比較柔和的校驗方法,它只負責校驗,并將校驗結果以回傳值的形式
傳遞給上層,Context 還有另外一個比較暴力的校驗方法 Context.Bind,它和ShouldBind
的呼叫形式一摸一樣,區別是當校驗錯誤發生時,它會呼叫 Abort() 方法中斷呼叫鏈的執行,
向客戶端回傳一個 HTTP 400 Bad Request 錯誤,


轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/283073.html

標籤:其他

上一篇:反碎片技術和虛擬可移動區域

下一篇:系列學習 docker 之第 4 篇 —— 構建專案的 docker 鏡像并發布專案到 docker

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more