RouterGroup
Run(ip:port)
直接启动
LoadHTMLFiles
{{}}
StaticXXX
方法,主要是绑定静态资源
HandlerChain
,管理handler在go和go框架中,上下文是一个非常核心的概念,上下文具有线程安全和局部可见的特点,当然和原生Context有点不同
上下文对象同样是gin的基础,在gin中context方法非常的多
//GET系列
Get(key string) (value any, exists bool)
MustGet(key string) any
GetString(key string) (s string)
GetBool(key string) (b bool)
GetInt(key string) (i int)
GetInt64(key string) (i64 int64)
GetUint(key string) (ui uint)
GetUint64(key string) (ui64 uint64)
GetFloat64(key string) (f64 float64)
GetTime(key string) (t time.Time)
GetDuration(key string) (d time.Duration)
GetStringSlice(key string) (ss []string)
GetStringMap(key string) (sm map[string]any)
GetStringMapString(key string) (sms map[string]string)
GetStringMapStringSlice(key string) (smss map[string][]string)
GetQuery(key string) (string, bool)
GetQueryArray(key string) (values []string, ok bool)
GetQueryMap(key string) (map[string]string, bool)
GetHeader(key string) string
GetRawData() ([]byte, error)
//params
Param(key string) string
AddParam(key string, value string)
//form
PostForm(key string) (value string)
DefaultPostForm(key string, defaultValue string) string
GetPostForm(key string) (string, bool)
PostFormArray(key string) (values []string)
initFormCache()
GetPostFormArray(key string) (values []string, ok bool)
PostFormMap(key string) (dicts map[string]string)
GetPostFormMap(key string) (map[string]string, bool)
FormFile(name string) (*multipart.FileHeader, error)
MultipartForm() (*multipart.Form, error)
//获取cookie
Cookie(name string) (string, error)
//query查询是否存在
Query(key string) (value string)
DefaultQuery(key string, defaultValue string) string
QueryArray(key string) (values []string)
initQueryCache()
QueryMap(key string) (dicts map[string]string)
//handler处理器链
reset()
Copy() *Context
HandlerName() string
HandlerNames() []string
Handler() HandlerFunc
FullPath() string
//下一个
Next()
//终止
IsAborted() bool
Abort()
AbortWithStatus(code int)
AbortWithStatusJSON(code int, jsonObj any)
AbortWithError(code int, err error) *Error
Error(err error) *Error
Set(key string, value any)
//参数继续引擎绑定,必须成功(BindXXXX),可以失败(ShouldBindXXX)
Bind(obj any) error
BindJSON(obj any) error
BindXML(obj any) error
BindQuery(obj any) error
BindYAML(obj any) error
BindTOML(obj interface{}) error
BindHeader(obj any) error
BindUri(obj any) error
MustBindWith(obj any, b binding.Binding) error
BindWith(obj any, b binding.Binding) error
ShouldBind(obj any) error
ShouldBindJSON(obj any) error
ShouldBindXML(obj any) error
ShouldBindQuery(obj any) error
ShouldBindYAML(obj any) error
ShouldBindTOML(obj interface{}) error
ShouldBindHeader(obj any) error
ShouldBindUri(obj any) error
ShouldBindWith(obj any, b binding.Binding) error
ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error)
//响应
//重定向
Redirect(code int, location string)
//header、code、message、cookie
Status(code int)
Header(key string, value string)
SetSameSite(samesite http.SameSite)
SetCookie(name string, value string, maxAge int, path string, domain string, secure bool, httpOnly bool)
Render(code int, r render.Render)
//data,将数据转为对应格式的data在写入resp
HTML(code int, name string, obj any)
IndentedJSON(code int, obj any)
SecureJSON(code int, obj any)
JSONP(code int, obj any)
JSON(code int, obj any)
AsciiJSON(code int, obj any)
PureJSON(code int, obj any)
XML(code int, obj any)
YAML(code int, obj any)
TOML(code int, obj interface{})
ProtoBuf(code int, obj any)
String(code int, format string, values ...any)
DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string)
File(filepath string)
FileFromFS(filepath string, fs http.FileSystem)
FileAttachment(filepath string, filename string)
Stream(step func(w io.Writer) bool) bool
//信息
ClientIP() string
RemoteIP() string
ContentType() string
IsWebsocket() bool
SSEvent(name string, message any)
Negotiate(code int, config Negotiate)
NegotiateFormat(offered ...string) string
SetAccepted(formats ...string)
SaveUploadedFile(file *multipart.FileHeader, dst string) error
//go Context
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
engine
绑定host、路由、handler//实例
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
//结构体解析
type User struct {
Name string `json:"userName"`
Password string `json:"password"`
Other string `json:"other"`
}
//统一封装返回
type Result struct {
Code int
Message string
Data interface{}
}
func init() {
}
func login(c *gin.Context) {
var user User
//绑定JSON解析
err := c.BindJSON(&user)
if err != nil {
return
}
fmt.Println(user)
result := Result{
Code: 200,
Message: "SUCCESS",
Data: "ok",
}
//自动将JSON作为返回参数
c.JSON(200, &result)
//执行下一条语句
c.Next()
}
func printHello(c *gin.Context) {
//获取路径参数
fmt.Println(c.Param("hello"))
c.Next()
}
func printUser(c *gin.Context) {
fmt.Println("user" + c.RemoteIP())
c.Next()
}
func globalPrint(c *gin.Context) {
fmt.Println("global")
c.Next()
}
func main() {
engine := gin.New()
//绑定全局处理器
engine.Use(globalPrint)
//绑定静态资源
engine.Static("/img", "./resource/static/img/*")
//绑定全局Html前缀路径
engine.LoadHTMLGlob("web/route/resource/web/*")
engine.GET("/", func(context *gin.Context) {
//html,通过模板引擎gin.H渲染 ---->//在index默认通过{{.name}}获取,可以自定义标识符替换{{}}
context.HTML(200, "index.html", gin.H{
"name": "lili",
})
})
//绑定路由组
group := engine.Group("/user", printUser)
//:hello为restful风格的参数传递
group.GET("/say/:hello", printHello)
//表单提交
engine.POST("/login", login)
//运行
err := engine.Run(":8888")
if err != nil {
fmt.Println("error")
}
}
type Engine struct {
RouterGroup
//是否自动重定向(即/a/,自动重定向为/a;默认为true
RedirectTrailingSlash bool
// 是否对书写(多余/、字母大小写不符)的自动重定向
RedirectFixedPath bool
// 是否对请求的方法不正确(405),通过NotMethod处理
HandleMethodNotAllowed bool
// 请求IP;默认为true
ForwardedByClientIP bool
// 开启路径参数
UseRawPath bool
// 路径参数转移;默认为true
UnescapePathValues bool
// 以从 URL 中解析参数,即使带有额外的斜杠。
RemoveExtraSlash bool
//允许的IP请求头
RemoteIPHeaders []string
// 信任的请求的平台
TrustedPlatform string
// 传入的参数最大占用空间
MaxMultipartMemory int64
//是否支持HTTP2
UseH2C bool
// Context是否可以回退
ContextWithFallback bool
//公共请求和响应设置(模板引擎、对象池、参数等)
delims render.Delims
secureJSONPrefix string
HTMLRender render.HTMLRender
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
trees methodTrees
maxParams uint16
maxSections uint16
trustedProxies []string
trustedCIDRs []*net.IPNet
}
New方法
Default
还是New
获取Engine都会调用New
方法
Default
绑定了默认的日志和全局异常处理New
方法
RouteGroup
Run方法
http.ListenAndServe
func (engine *Engine) Run(addr ...string) (err error) {
address := resolveAddress(addr)
//注入engine作为http的handler,即作为监听的端口的所有请求的handler;
//engine.Handler()方法就是返回http1.x或者http2.0的engine
err = http.ListenAndServe(address, engine.Handler())
}
final、value、children数组/map(以及全量单词)
,显然前缀树的增删改查都是logn路由表的压缩前缀树
//压缩前缀树节点
type node struct {
path string //value
indices string //节点与子节点的分裂的第一个字符
wildChild bool //是否为参数节点
nType nodeType //节点类型:根节点、参数节点、通配符、普通节点
priority uint32 //handler数量
children []*node //子节点
handlers HandlersChain //处理器链
fullPath string //全量路径
}
//封装的获取到压缩前缀书查询结果
type nodeValue struct {
handlers HandlersChain
params *Params
tsr bool
fullPath string
}
//生成路由
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
//路由路径(加入前缀)
absolutePath := group.calculateAbsolutePath(relativePath)
//处理器链
handlers = group.combineHandlers(handlers)
//加入路由表
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
//获取根路由
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
//根据方法类型获取根路径
root := engine.trees.get(method)
if root == nil {
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
//加入路由表
root.addRoute(path, handlers)
}
//添加路由路径
func (n *node) addRoute(path string, handlers HandlersChain) {
fullPath := path
n.priority++
// 空树,直接使用压缩前缀即可
if len(n.path) == 0 && len(n.children) == 0 {
n.insertChild(path, fullPath, handlers)
n.nType = root
return
}
parentFullPathIndex := 0
walk:
for {
//最长公共路径(排除通配符和参数匹配)
i := longestCommonPrefix(path, n.path)
//生成节点
if i < len(n.path) {
child := node{
//...
}
//修改n
n.children = []*node{&child}
// []byte for proper unicode char conversion, see #65
n.indices = bytesconv.BytesToString([]byte{n.path[i]})
n.path = path[:i]
n.handlers = nil
n.wildChild = false
n.fullPath = fullPath[:parentFullPathIndex+i]
}
// 去除参数、通配符、多余/
if i < len(path) {
path = path[i:]
c := path[0]
// '/' after param
if n.nType == param && c == '/' && len(n.children) == 1 {
parentFullPathIndex += len(n.path)
n = n.children[0]
n.priority++
continue walk
}
// Check if a child with the next path byte exists
for i, max := 0, len(n.indices); i < max; i++ {
if c == n.indices[i] {
parentFullPathIndex += len(n.path)
i = n.incrementChildPrio(i)
n = n.children[i]
continue walk
}
}
if c != ':' && c != '*' && n.nType != catchAll {
n.indices += bytesconv.BytesToString([]byte{c})
child := &node{
fullPath: fullPath,
}
n.addChild(child)
n.incrementChildPrio(len(n.indices) - 1)
n = child
}
//插入子节点
n.insertChild(path, fullPath, handlers)
return
}
n.handlers = handlers
n.fullPath = fullPath
return
}
}
ServeHTTP
方法,通过handleHTTPRequest
统一处理请求handleHTTPRequest
**主要就是根据请求方法+路径确定处理器链和参数、然后调用处理器链进行处理