Ai
1 Star 0 Fork 0

unsafe-rust/gin

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
context.go 39.85 KB
一键复制 编辑 原始数据 按行查看 历史
unsafe-rust 提交于 2021-05-07 21:21 +08:00 . update
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233
package gin
import (
"errors"
"fmt"
"gitee.com/gopher2011/gin/bind"
"gitee.com/gopher2011/gin/render"
"gitee.com/gopher2011/gin/sse"
"io"
"io/ioutil"
"math"
"mime/multipart"
"net"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"
)
// 最常见的数据格式的 Content-Type MIME。
const (
MiMeJSON = bind.MiMeJSON
MiMeHTML = bind.MiMeHTML
MiMeXML = bind.MiMeXML
MiMeXML2 = bind.MiMeXML2
MiMePlain = bind.MiMePlain
MiMePOSTForm = bind.MiMePOSTForm
MiMeMultipartPOSTForm = bind.MiMeMultipartPOSTForm
MiMeYAML = bind.MiMeYAML
)
const (
// BodyBytesKey 表示默认的主体字节密钥。
BodyBytesKey = "_gin-gonic/gin/bodybyteskey"
abortIndex int8 = math.MaxInt8 / 2
)
type (
// HandlerFunc 是(http.ResponseWriter,*http.Request)函数签名的扩展。
HandlerFunc func(*Context)
// HandlersChain 是 HandlerFunc 的集合。
// 即: HandlersChain 是一个容器,里面放的都是 func(*Context)。
HandlersChain []HandlerFunc
)
// Context 封装 *http.Request 和 http.ResponseWriter。
// 1、请求和响应相关的逻辑都由 Context 模块承载。
//
// 2、获取 URL路径 中的请求参数: Param() Query() QueryDefault() QueryArray() QueryMap() 以及 GetXXX()系列方法。这些方法可以用在 GET、POST 请求中。
//
// 3、获取 Request.Body表单 中的请求参数: PostForm() PostFormDefault() PostFormArray() PostFormMap() FormFile() FormMultipart()。这些方法都不能用在 GET 请求中。
//
// 4、解析 请求参数到结构体中(只有3个): Parse() ParseBody() ParseUri()
type Context struct {
Request *http.Request // go 标准库中的 *http.Request
Writer ResponseWriter
Params Params
Keys map[string]interface{} // Keys 键是专门用于每个请求上下文的键值对。
Errors errorMsgs // Errors 使用此上下文的所有处理程序中间件附带的错误列表。
Accepted []string // Accepted 用于内容协商的手动接受格式的列表。
handlers HandlersChain
writer responseWriter // 自定义的 responseWriter 结构体
index int8
fullPath string
engine *Engine
params *Params
mu sync.RWMutex // 该互斥锁保护键映射
queryCache url.Values // queryCache 使用url.ParseQuery从c.Request.URL.Query()缓存参数查询结果
formCache url.Values // formCache 使用url.ParseQuery缓存的PostForm包含从POST,PATCH或PUT正文参数解析的表单数据。
sameSite http.SameSite // SameSite允许服务器定义cookie属性,从而使浏览器无法将cookie与跨站点请求一起发送。
}
/************************************/
/********** CONTEXT CREATION ********/
/************************************/
func (c *Context) reset() {
c.Writer = &c.writer
c.Params = c.Params[0:0]
c.handlers = nil
c.index = -1
c.fullPath = ""
c.Keys = nil
c.Errors = c.Errors[0:0]
c.Accepted = nil
c.queryCache = nil
c.formCache = nil
*c.params = (*c.params)[0:0]
}
// Copy 返回当前 *Context 的副本,该副本可以在请求范围之外安全地使用。
// 当 *Context 必须传递给 goroutine 时,必须使用此方法。
func (c *Context) Copy() *Context {
cp := Context{
writer: c.writer,
Request: c.Request,
Params: c.Params,
engine: c.engine,
}
cp.writer.ResponseWriter = nil
cp.Writer = &cp.writer
cp.index = abortIndex
cp.handlers = nil
cp.Keys = map[string]interface{}{}
for k, v := range c.Keys {
cp.Keys[k] = v
}
paramCopy := make([]Param, len(cp.Params))
copy(paramCopy, cp.Params)
cp.Params = paramCopy
return &cp
}
// Last 返回 HandlersChain 链(请求处理链)中的最后一个 HandlerFunc (请求处理程序),最后一个 HandlerFunc (请求处理程序)是主要的处理程序。
func (c HandlersChain) Last() HandlerFunc {
if length := len(c); length > 0 {
return c[length-1]
}
return nil
}
// HandlerName 返回主处理程序的名称。例如,如果处理程序为"handleGetUsers()",则此函数将返回"main.handleGetUsers"。
func (c *Context) HandlerName() string {
return nameOfFunction(c.handlers.Last())
}
// HandlerNames 按照 HandlerName()的语义,以降序返回此 *Context 的所有已注册处理程序的列表。
func (c *Context) HandlerNames() []string {
hn := make([]string, 0, len(c.handlers))
for _, val := range c.handlers {
hn = append(hn, nameOfFunction(val))
}
return hn
}
// Handler 返回主处理程序。
func (c *Context) Handler() HandlerFunc {
return c.handlers.Last()
}
// FullPath 返回匹配的路由完整路径。对于未找到的路由,返回一个空字符串。
// router.GET("/user/:id", func(c *gin.Context) {
// c.FullPath() == "/user/:id" // true
// })
func (c *Context) FullPath() string {
return c.fullPath
}
/************************************/
/*********** FLOW CONTROL ***********/
/************************************/
// Next 这个方法只在中间件内部使用。
// 它在调用处理程序内的链中执行挂起的处理程序。
// See example in GitHub.
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
// IsAborted 如果当前 *Context 中止,则返回true。
func (c *Context) IsAborted() bool {
return c.index >= abortIndex
}
// Abort 防止挂起的处理程序被调用。请注意,这不会停止当前的处理程序。
// 假设您有一个授权中间件,用于验证当前请求是否得到授权。
// 如果授权失败(例如:密码不匹配),则调用Abort以确保不调用此请求的其余处理程序。
func (c *Context) Abort() {
c.index = abortIndex
}
// AbortWithStatus 调用 Abort() 并使用指定的状态代码写入标头。
// 例如,对请求进行身份验证的失败尝试可以使用:context.AbortWithStatus(401)。
func (c *Context) AbortWithStatus(code int) {
c.Status(code)
c.Writer.WriteHeaderNow()
c.Abort()
}
// AbortWithStatusJSON 在内部调用 *Context.Abort(),然后调用 *Context.JSON()。
// 此方法停止链,写入状态代码并返回JSON正文。还将 Content-Type 设置为" application/json"。
func (c *Context) AbortWithStatusJSON(code int, jsonObj interface{}) {
c.Abort()
c.JSON(code, jsonObj)
}
// AbortWithError 内部调用 *Context.AbortWithStatus() 和 *Context.Error()。
// 此方法停止链,写入状态代码,并将指定的错误推送到 *Context.Error()。有关更多详细信息,请参见 Context.Error()。
func (c *Context) AbortWithError(code int, err error) *Error {
c.AbortWithStatus(code)
return c.Error(err)
}
/************************************/
/********* ERROR MANAGEMENT *********/
/************************************/
// Error 将错误附加到当前 *Context。错误被推送到错误列表。
// 对于请求解析期间发生的每个错误,最好都调用 Error。
// 中间件可用于收集所有错误并将它们一起推送到数据库,打印日志或将其附加到HTTP响应中。
// 如果err为nil,err 将 panic。
func (c *Context) Error(err error) *Error {
if err == nil {
panic("err is nil")
}
parsedError, ok := err.(*Error)
if !ok {
parsedError = &Error{
Err: err,
Type: ErrorTypePrivate,
}
}
c.Errors = append(c.Errors, parsedError)
return parsedError
}
/************************************/
/******** Metadata Management********/
/************************************/
// Set 用于为此 *Context 专门存储新的键值对。
// 如果以前没有使用过c.Keys,它也会延迟初始化。
func (c *Context) Set(key string, value interface{}) {
c.mu.Lock()
if c.Keys == nil {
c.Keys = make(map[string]interface{})
}
c.Keys[key] = value
c.mu.Unlock()
}
// Get 返回给定键的值,即:(值,true)。
// 如果该值不存在,则返回(nil,false)
func (c *Context) Get(key string) (value interface{}, exists bool) {
c.mu.RLock()
value, exists = c.Keys[key]
c.mu.RUnlock()
return
}
// MustGet 返回给定键的值(如果存在),否则会 panic。
func (c *Context) MustGet(key string) interface{} {
if value, exists := c.Get(key); exists {
return value
}
panic("Key \"" + key + "\" does not exist")
}
// GetString 以字符串形式返回与键关联的值。
func (c *Context) GetString(key string) (s string) {
if val, ok := c.Get(key); ok && val != nil {
s, _ = val.(string)
}
return
}
// GetBool 返回与键关联的值作为布尔值。
func (c *Context) GetBool(key string) (b bool) {
if val, ok := c.Get(key); ok && val != nil {
b, _ = val.(bool)
}
return
}
// GetInt 以整数形式返回与键关联的值。
func (c *Context) GetInt(key string) (i int) {
if val, ok := c.Get(key); ok && val != nil {
i, _ = val.(int)
}
return
}
// GetInt64 returns the value associated with the key as an integer.
func (c *Context) GetInt64(key string) (i64 int64) {
if val, ok := c.Get(key); ok && val != nil {
i64, _ = val.(int64)
}
return
}
// GetUint returns the value associated with the key as an unsigned integer.
func (c *Context) GetUint(key string) (ui uint) {
if val, ok := c.Get(key); ok && val != nil {
ui, _ = val.(uint)
}
return
}
// GetUint64 returns the value associated with the key as an unsigned integer.
func (c *Context) GetUint64(key string) (ui64 uint64) {
if val, ok := c.Get(key); ok && val != nil {
ui64, _ = val.(uint64)
}
return
}
// GetFloat64 returns the value associated with the key as a float64.
func (c *Context) GetFloat64(key string) (f64 float64) {
if val, ok := c.Get(key); ok && val != nil {
f64, _ = val.(float64)
}
return
}
// GetTime returns the value associated with the key as time.
func (c *Context) GetTime(key string) (t time.Time) {
if val, ok := c.Get(key); ok && val != nil {
t, _ = val.(time.Time)
}
return
}
// GetDuration returns the value associated with the key as a duration.
func (c *Context) GetDuration(key string) (d time.Duration) {
if val, ok := c.Get(key); ok && val != nil {
d, _ = val.(time.Duration)
}
return
}
// GetStringSlice returns the value associated with the key as a slice of strings.
func (c *Context) GetStringSlice(key string) (ss []string) {
if val, ok := c.Get(key); ok && val != nil {
ss, _ = val.([]string)
}
return
}
// GetStringMap returns the value associated with the key as a map of interfaces.
func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {
if val, ok := c.Get(key); ok && val != nil {
sm, _ = val.(map[string]interface{})
}
return
}
// GetStringMapString returns the value associated with the key as a map of strings.
func (c *Context) GetStringMapString(key string) (sms map[string]string) {
if val, ok := c.Get(key); ok && val != nil {
sms, _ = val.(map[string]string)
}
return
}
// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {
if val, ok := c.Get(key); ok && val != nil {
smss, _ = val.(map[string][]string)
}
return
}
/************************************/
/************ Input Data ************/
/************************************/
// Param 获取URL路径中的 URL参数。
// 1、既可以用在GET请求中,也可以用在POST请求中。
//
// 2、如果存在,则返回<key>的URL的参数值,否则返回一个空字符串。
//
// 3、注意:这并不是获取查询参数。
//
// router.GET("/user/:id", func(c *gin.Context) {
// id := c.Param("id")
// })
//
// 详细文档: https://www.kancloud.cn/shuangdeyu/gin_book/949416
func (c *Context) Param(key string) string {
return c.Params.ByName(key)
}
// Query: 获取URL路径中的 查询参数。
// 1、既可以用在GET请求中,也可以用在POST请求中。
//
// 2、如果存在,则返回<key>的URL查询值,否则返回一个空字符串。
//
// 3、注意:这并不是获取URL参数。
//
// GET /path?id=1234&name=Manu&value=
// c.Query("id") == "1234"
// c.Query("name") == "Manu"
// c.Query("value") == ""
// c.Query("wtf") == ""
func (c *Context) Query(key string) string {
value, _ := c.getQuery(key)
return value
}
// QueryDefault 获取 GET 请求中的参数。返回键入的url查询值(如果存在),否则返回指定的defaultValue字符串。
//
// 例如:
// GET /?name=Manu&lastname=
// c.DefaultQuery("name", "unknown") == "Manu"
// c.DefaultQuery("id", "none") == "none"
// c.DefaultQuery("lastname", "none") == ""
//
// 详细文档: https://www.kancloud.cn/shuangdeyu/gin_book/949417
func (c *Context) QueryDefault(key, defaultValue string) string {
if value, ok := c.getQuery(key); ok {
return value
}
return defaultValue
}
// getQuery 就像Query()一样。
// 如果存在(value,true)(即使该值是一个空字符串),它也会返回键控的url查询值,否则将返回(" ",false)。
// 这是 *Context.Request.URL.Query().Get(key) 的快捷方式。
// GET /?name=Manu&lastname=
// ("Manu", true) == c.GetQuery("name")
// ("", false) == c.GetQuery("id")
// ("", true) == c.GetQuery("lastname")
func (c *Context) getQuery(key string) (string, bool) {
if values, ok := c.getQueryArray(key); ok {
return values[0], ok
}
return "", false
}
// QueryArray 获取 GET 请求中的参数。返回给定查询键的字符串切片。切片的长度取决于给定关键字的参数数量。
// URL查询参数,就是一个数组,例如: URL是这样 ?a=b&a=c&a=d,key值都一样,但是对应的value不一样。
//
// 例如:
// URL: /?media=blog&media=wechat,它们的 key 都是 media。
//
// 返回: ["blog","wechat"]
//
// 详细文档: https://www.flysnow.org/2019/12/18/golang-gin-query-parameters-array-map.html
func (c *Context) QueryArray(key string) []string {
values, _ := c.getQueryArray(key)
return values
}
// QueryMap 获取 GET 请求中的参数。返回给定查询键的字符串字典。
// 其实就是把满足一定格式的URL查询参数,转换为一个map。
//
// 例如:
// URL: /?ids[a]=123&ids[b]=456&ids[c]=789
//
// 返回: {"a":"123","b":"456","c":"789"}
//
// 详细文档: https://www.flysnow.org/2019/12/18/golang-gin-query-parameters-array-map.html
func (c *Context) QueryMap(key string) map[string]string {
dict, _ := c.getQueryMap(key)
return dict
}
func (c *Context) initQueryCache() {
if c.queryCache == nil {
if c.Request != nil {
c.queryCache = c.Request.URL.Query()
} else {
c.queryCache = url.Values{}
}
}
}
// GetQueryArray returns a slice of strings for a given query key, plus
// a boolean value whether at least one value exists for the given key.
func (c *Context) getQueryArray(key string) ([]string, bool) {
c.initQueryCache()
if values, ok := c.queryCache[key]; ok && len(values) > 0 {
return values, true
}
return []string{}, false
}
// GetQueryMap returns a map for a given query key, plus a boolean value
// whether at least one value exists for the given key.
func (c *Context) getQueryMap(key string) (map[string]string, bool) {
c.initQueryCache()
return c.get(c.queryCache, key)
}
// PostForm 获取 POST 请求中的表单参数。注意: 这个方法不能用于 GET 请求。
// 如果存在,则以 POST url/encoded 形式或多部分形式返回 key对应的value,否则返回一个空字符串。
//
// 详细文档: https://www.kancloud.cn/shuangdeyu/gin_book/949418
func (c *Context) PostForm(key string) string {
value, _ := c.getPostForm(key)
return value
}
// PostFormDefault 获取 POST 请求中的参数。注意: 这个方法不能用于 GET 请求。
// 如果存在,则以 POST urlencoded 形式或多部分形式返回 key对应的value,否则返回指定的 defaultValue 字符串。
func (c *Context) PostFormDefault(key, defaultValue string) string {
if value, ok := c.getPostForm(key); ok {
return value
}
return defaultValue
}
// getPostForm 就像 *Context.PostForm(key)。
// 当它存在 (value,true)(即使值是一个空字符串)时,它从POST url/encode形式或多部分形式返回指定的键,否则返回(" ",false)。
// 例如,在PATCH请求中更新用户的电子邮件期间:
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
// email= --> ("", true) := GetPostForm("email") // set email to ""
// --> ("", false) := GetPostForm("email") // do nothing with email
func (c *Context) getPostForm(key string) (string, bool) {
if values, ok := c.getPostFormArray(key); ok {
return values[0], ok
}
return "", false
}
// PostFormArray 获取 POST 请求中的参数。注意: 这个方法不能用于 GET 请求。
// 返回给定表单键的字符串切片。切片的长度取决于具有给定键的参数的数量。
func (c *Context) PostFormArray(key string) []string {
values, _ := c.getPostFormArray(key)
return values
}
// PostFormMap 获取 POST 请求中的参数。注意: 这个方法不能用于 GET 请求。
// 返回给定表单键的映射。
func (c *Context) PostFormMap(key string) map[string]string {
dict, _ := c.getPostFormMap(key)
return dict
}
func (c *Context) initFormCache() {
if c.formCache == nil {
c.formCache = make(url.Values)
req := c.Request
if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
if err != http.ErrNotMultipart {
debugPrint("error on parse multipart form array: %v", err)
}
}
c.formCache = req.PostForm
}
}
// GetPostFormArray 返回给定形式键的字符串切片,以及一个布尔值(无论给定键是否存在至少一个值)。
func (c *Context) getPostFormArray(key string) ([]string, bool) {
c.initFormCache()
if values := c.formCache[key]; len(values) > 0 {
return values, true
}
return []string{}, false
}
// GetPostFormMap 返回给定表单键的映射,以及一个布尔值(无论该给定键是否存在至少一个值),然后返回一个布尔值。
func (c *Context) getPostFormMap(key string) (map[string]string, bool) {
c.initFormCache()
return c.get(c.formCache, key)
}
// get 是一种内部方法,并返回满足条件的map。
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
dict := make(map[string]string)
exist := false
for k, v := range m {
if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key {
if j := strings.IndexByte(k[i+1:], ']'); j >= 1 {
exist = true
dict[k[i+1:][:j]] = v[0]
}
}
}
return dict, exist
}
// FormFile 获取 POST 请求中的单个文件。每次只能获取一个文件。这个方法不能用于 GET 请求。
// 详细文档: https://www.kancloud.cn/shuangdeyu/gin_book/949420
func (c *Context) FormFile(name string) (*multipart.FileHeader, error) {
if c.Request.MultipartForm == nil {
if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
return nil, err
}
}
f, fh, err := c.Request.FormFile(name)
if err != nil {
return nil, err
}
f.Close()
return fh, err
}
// FormMultipart (原MultipartForm) 获取 POST 请求中的多部分表单,包括多个文件上传。每次能获取多个文件。
// 详细文档: https://www.kancloud.cn/shuangdeyu/gin_book/949420
func (c *Context) FormMultipart() (*multipart.Form, error) {
err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory)
return c.Request.MultipartForm, err
}
// SaveUploadedFile 将表单文件上传到特定的 <dst>。
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, src)
return err
}
// bind (原Bind方法) 检查 Content-Type 以自动选择绑定引擎。根据 "Content-Type" 标头,使用不同的绑定:
// "application/json" --> JSON binding
// "application/xml" --> XML binding
// 否则->返回错误。
// 如果 Content-Type == "application/json"使用JSON或XML作为JSON输入,它将请求的主体解析为JSON格式的数据。
// 它将json有效负载解码为指定为指针的结构。
// 如果输入无效,它将写入400错误并在响应中设置 Content-Type 标头 "text/plain"
// 此方法不够灵活,建议使用 Parse 方法。
func (c *Context) bind(obj interface{}) error {
b := bind.Default(c.Request.Method, c.ContentType())
return c.mustBindWith(obj, b)
}
// BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON).
func (c *Context) bindJSON(obj interface{}) error {
return c.mustBindWith(obj, bind.JSON)
}
// BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML).
func (c *Context) bindXML(obj interface{}) error {
return c.mustBindWith(obj, bind.XML)
}
// BindQuery is a shortcut for c.MustBindWith(obj, binding.Query).
func (c *Context) bindQuery(obj interface{}) error {
return c.mustBindWith(obj, bind.Query)
}
// BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML).
func (c *Context) bindYAML(obj interface{}) error {
return c.mustBindWith(obj, bind.YAML)
}
// BindHeader is a shortcut for c.MustBindWith(obj, binding.Header).
func (c *Context) bindHeader(obj interface{}) error {
return c.mustBindWith(obj, bind.Header)
}
// bindUri (原BindUri方法) 使用binding.Uri绑定传递的struct指针。
// 如果发生任何错误,它将使用 HTTP 400终止请求。
func (c *Context) bindUri(obj interface{}) error {
if err := c.ParseUri(obj); err != nil {
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
return err
}
return nil
}
// mustBindWith (原MustBindWith方法)使用指定的绑定引擎绑定传递的struct指针。
// 如果发生任何错误,它将使用HTTP 400终止请求。此方法不够灵活,不建议使用。
func (c *Context) mustBindWith(obj interface{}, b bind.IBind) error {
if err := c.parseOnce(obj, b); err != nil {
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) // nolint: errcheck
return err
}
return nil
}
// ParseUri (ShouldBindUri) 将 request(请求)中的uri数据解析到obj中。
func (c *Context) ParseUri(obj interface{}) error {
m := make(map[string][]string)
for _, v := range c.Params {
m[v.Key] = []string{v.Value}
}
return bind.Uri.BindUri(m, obj)
}
// Parse (原ShouldBind方法)将请求参数解析到<obj>中,参数<obj>必须是指针类型!
// 1、检查 Content-Type 以自动选择解析引擎。根据 "Content-Type"标头,使用不同的解析器:
// "application/json" --> JSON bind
// "application/xml" --> XML bind
// 否则->返回错误。
//
// 2、如果 Content-Type == " application/json" 则:它将请求的主体解析为JSON格式的数据。
//
// 3、保存在 <obj>中的请求参数,不能重用。该方法使用一次后,<obj>中的数据就被清空了。
// 如果要调用多次,并且要重用<obj>的请求参数,请使用 ParseBody
func (c *Context) Parse(obj interface{}) error {
b := bind.Default(c.Request.Method, c.ContentType())
return c.parseOnce(obj, b)
}
// ParseBody 将请求参数解析到<obj>中,适用于重复解析。参数<obj>必须是指针类型!
// 1、ParseBody 与 Parse 相似,唯一区别: ParseBody 会将请求正文存储到上下文中,并在再次调用时重用。
//
// 2、注意: 此方法在解析之前读取正文。因此,如果只需要调用一次,则应使用 Parse 以获得更好的性能。
// (原ShouldBindBodyWith方法)测试用例 https://www.jianshu.com/p/0f0bf53bedd2
func (c *Context) ParseBody(obj interface{}) (err error){
var body []byte
if cb, ok := c.Get(BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
body = cbb
}
}
if body == nil {
body, err = ioutil.ReadAll(c.Request.Body)
if err != nil {
return err
}
c.Set(BodyBytesKey, body)
}
b := bind.Default(c.Request.Method, c.ContentType())
bb := bind.DefaultBody(b.Name())
return bb.BindBody(body, obj)
}
// ContentType 返回请求的Content-Type标头。
func (c *Context) ContentType() string {
return filterFlags(c.requestHeader("Content-Type"))
}
// parseOnce (原ShouldBindWith方法) 使用指定的绑定引擎绑定传递的struct指针。
// 1、用户可以自行选择绑定器,自行对出错处理。自行选择绑定器,这也意味着用户可以自己实现绑定器。
// 例如: 嫌弃默认的json处理是用官方的json处理包,嫌弃它慢,可以自己实现Binding接口。
//
// 2、保存在 <obj>中的请求参数,不能重用。该方法使用一次后,<obj>中的数据就被清空了。
// 如果要调用多次,并且要重用<obj>的请求参数,请使用 ParseBody
func (c *Context) parseOnce(obj interface{}, b bind.IBind) error {
return b.Bind(c.Request, obj)
}
// parseBody (原ShouldBindBodyWith方法) 将请求参数解析到<obj>中,适用于重复绑定。
// 1、parseBody与 parseOnce 相似,唯一区别: parseBody 会将请求正文存储到上下文中,并在再次调用时重用。
//
// 2、注意: 此方法在绑定之前读取正文。因此,如果只需要调用一次,则应使用 parseOnce 以获得更好的性能。
//
func (c *Context) parseBody(obj interface{}, bb bind.IBindBody) (err error) {
var body []byte
if cb, ok := c.Get(BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
body = cbb
}
}
if body == nil {
body, err = ioutil.ReadAll(c.Request.Body)
if err != nil {
return err
}
c.Set(BodyBytesKey, body)
}
return bb.BindBody(body, obj)
}
// ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON).
func (c *Context) shouldBindJSON(obj interface{}) error {
return c.parseOnce(obj, bind.JSON)
}
// ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML).
func (c *Context) shouldBindXML(obj interface{}) error {
return c.parseOnce(obj, bind.XML)
}
// ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query).
func (c *Context) shouldBindQuery(obj interface{}) error {
return c.parseOnce(obj, bind.Query)
}
// ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML).
func (c *Context) shouldBindYAML(obj interface{}) error {
return c.parseOnce(obj, bind.YAML)
}
// ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header).
func (c *Context) shouldBindHeader(obj interface{}) error {
return c.parseOnce(obj, bind.Header)
}
// ClientIP 以返回真实的客户端IP
// 它在后台调用了c.RemoteIP(),以检查远程IP是否是受信任的代理。
// 如果是,它将尝试解析在Engine.RemoteIPHeaders中定义的标头(默认为[X-Forwarded-For,X-Real-Ip])。
// 如果标头在语法上无效,或者远程IP与信任的代理不对应,则返回远程IP(来自Request.RemoteAddr)。
func (c *Context) ClientIP() string {
if c.engine.AppEngine {
if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" {
return addr
}
}
remoteIP, trusted := c.RemoteIP()
if remoteIP == nil {
return ""
}
if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil {
for _, headerName := range c.engine.RemoteIPHeaders {
ip, valid := validateHeader(c.requestHeader(headerName))
if valid {
return ip
}
}
}
return remoteIP.String()
}
// RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port).
// It also checks if the remoteIP is a trusted proxy or not.
// In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks
// defined in Engine.TrustedProxies
func (c *Context) RemoteIP() (net.IP, bool) {
ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr))
if err != nil {
return nil, false
}
remoteIP := net.ParseIP(ip)
if remoteIP == nil {
return nil, false
}
if c.engine.trustedCIDRs != nil {
for _, cidr := range c.engine.trustedCIDRs {
if cidr.Contains(remoteIP) {
return remoteIP, true
}
}
}
return remoteIP, false
}
func validateHeader(header string) (clientIP string, valid bool) {
if header == "" {
return "", false
}
items := strings.Split(header, ",")
for i, ipStr := range items {
ipStr = strings.TrimSpace(ipStr)
ip := net.ParseIP(ipStr)
if ip == nil {
return "", false
}
// We need to return the first IP in the list, but,
// we should not early return since we need to validate that
// the rest of the header is syntactically valid
if i == 0 {
clientIP = ipStr
valid = true
}
}
return
}
// IsWebsocket 如果请求标头指示客户端正在发起 Websocket 握手,则返回true。
func (c *Context) IsWebsocket() bool {
if strings.Contains(strings.ToLower(c.requestHeader("Connection")), "upgrade") &&
strings.EqualFold(c.requestHeader("Upgrade"), "websocket") {
return true
}
return false
}
func (c *Context) requestHeader(key string) string {
return c.Request.Header.Get(key)
}
/************************************/
/******** 响应==>渲染 ********/
/************************************/
// bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function.
func bodyAllowedForStatus(status int) bool {
switch {
case status >= 100 && status <= 199:
return false
case status == http.StatusNoContent:
return false
case status == http.StatusNotModified:
return false
}
return true
}
// Status 设置HTTP响应代码。
func (c *Context) Status(code int) {
c.Writer.WriteHeader(code)
}
// Header 是 *Context.Writer.Header().Set(key,value) 的智能快捷方式。
// 它在响应中写入标头。如果value ==" ",则此方法删除标头 *Context.Writer.Header().Del(key)
func (c *Context) Header(key, value string) {
if value == "" {
c.Writer.Header().Del(key)
return
}
c.Writer.Header().Set(key, value)
}
// GetHeader 从请求标头返回值。
func (c *Context) GetHeader(key string) string {
return c.requestHeader(key)
}
// GetRawData 返回流数据。其内部代码 ioutil.ReadAll(c.Request.Body)
func (c *Context) GetRawData() ([]byte, error) {
return ioutil.ReadAll(c.Request.Body)
}
// SetSameSite with cookie
func (c *Context) SetSameSite(samesite http.SameSite) {
c.sameSite = samesite
}
// SetCookie 将 Set-Cookie 标头添加到 ResponseWriter 的标头中。
// 提供的cookie必须具有有效的名称。无效的cookie可能会被静默删除。
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) {
if path == "" {
path = "/"
}
http.SetCookie(c.Writer, &http.Cookie{
Name: name,
Value: url.QueryEscape(value),
MaxAge: maxAge,
Path: path,
Domain: domain,
SameSite: c.sameSite,
Secure: secure,
HttpOnly: httpOnly,
})
}
// Cookie 返回请求中提供的命名 cookie,如果找不到,则返回ErrNoCookie。
// 并返回已命名的cookie,并且不对其进行转义。如果多个Cookie与给定名称匹配,则仅返回一个Cookie。
func (c *Context) Cookie(name string) (string, error) {
cookie, err := c.Request.Cookie(name)
if err != nil {
return "", err
}
val, _ := url.QueryUnescape(cookie.Value)
return val, nil
}
// Render 编写响应标头并调用 render.Render() 渲染数据。
func (c *Context) Render(code int, r render.Render) {
c.Status(code)
if !bodyAllowedForStatus(code) {
r.WriteContentType(c.Writer)
c.Writer.WriteHeaderNow()
return
}
if err := r.Render(c.Writer); err != nil {
panic(err)
}
}
// HTML 渲染由其文件名指定的HTTP模板。
// 它还会更新HTTP代码,并将 Content-Type 设置为 "text/html"。
// See http://golang.org/doc/articles/wiki/
func (c *Context) HTML(code int, name string, obj interface{}) {
instance := c.engine.HTMLRender.Instance(name, obj)
c.Render(code, instance)
}
// IndentedJSON 将给定结构体序列化为漂亮的JSON(缩进+结束行)到响应主体中。
// 还将 Content-Type设置为 "application/json"。
// 警告:我们建议仅将其用于开发目的,因为打印漂亮的JSON会占用更多的CPU和带宽。请改用 Context.JSON()。
func (c *Context) IndentedJSON(code int, obj interface{}) {
c.Render(code, render.IndentedJSON{Data: obj})
}
// SecureJSON 将给定的结构体作为Secure JSON序列化到响应主体中。
// 如果给定的结构体是数组值,则默认值在响应主体前加上 "while(1)"。还将 Content-Type 设置为 "application/json"。
func (c *Context) SecureJSON(code int, obj interface{}) {
c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj})
}
// JSONP 将给定结构体作为JSON序列化到响应主体中。
// 它将填充添加到响应主体,以从位于与客户端不同的域中的服务器请求数据。
// 还将 Content-Type 设置为 "application/javascript"。
func (c *Context) JSONP(code int, obj interface{}) {
callback := c.QueryDefault("callback", "")
if callback == "" {
c.Render(code, render.JSON{Data: obj})
return
}
c.Render(code, render.JsonpJSON{Callback: callback, Data: obj})
}
// JSON 将给定结构体作为JSON序列化到响应主体中。
// 还将 Content-Type 设置为 "application/json"。
func (c *Context) JSON(code int, obj interface{}) {
c.Render(code, render.JSON{Data: obj})
}
// AsciiJSON 使用 Unicode 到 ASCII 字符串将给定结构体以JSON序列化到响应主体中。
// 还将 Content-Type 设置为 "application/json"。
func (c *Context) AsciiJSON(code int, obj interface{}) {
c.Render(code, render.AsciiJSON{Data: obj})
}
// PureJSON 将给定结构体作为JSON序列化到响应主体中。
// *Context.PureJSON() 与 *Context.JSON() 不同,PureJSON 不会用其unicode实体替换特殊的html字符。
func (c *Context) PureJSON(code int, obj interface{}) {
c.Render(code, render.PureJSON{Data: obj})
}
// XML 将给定的结构体作为XML序列化到响应主体中。
// 还将 Content-Type 设置为 "application/xml"。
func (c *Context) XML(code int, obj interface{}) {
c.Render(code, render.XML{Data: obj})
}
// YAML 将给定的结构体作为YAML序列化到响应主体中。
func (c *Context) YAML(code int, obj interface{}) {
c.Render(code, render.YAML{Data: obj})
}
// ProtoBuf 将给定的结构体作为ProtoBuf序列化到响应主体中。
func (c *Context) ProtoBuf(code int, obj interface{}) {
c.Render(code, render.ProtoBuf{Data: obj})
}
// String 将给定的字符串写入响应主体。
func (c *Context) String(code int, format string, values ...interface{}) {
c.Render(code, render.String{Format: format, Data: values})
}
// Redirect 返回到特定位置的HTTP重定向。
func (c *Context) Redirect(code int, location string) {
c.Render(-1, render.Redirect{
Code: code,
Location: location,
Request: c.Request,
})
}
// Data 将一些数据写入主体流并更新HTTP代码。
func (c *Context) Data(code int, contentType string, data []byte) {
c.Render(code, render.Data{
ContentType: contentType,
Data: data,
})
}
// DataFromReader 将指定的Render写入主体流并更新HTTP代码。
func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) {
c.Render(code, render.Reader{
Headers: extraHeaders,
ContentType: contentType,
ContentLength: contentLength,
Reader: reader,
})
}
// File 以一种有效的方式将指定的文件写入主体流。
func (c *Context) File(filepath string) {
http.ServeFile(c.Writer, c.Request, filepath)
}
// FileFromFS 将http.FileSystem中的指定文件以高效的方式写入主体流。
func (c *Context) FileFromFS(filepath string, fs http.FileSystem) {
defer func(old string) {
c.Request.URL.Path = old
}(c.Request.URL.Path)
c.Request.URL.Path = filepath
http.FileServer(fs).ServeHTTP(c.Writer, c.Request)
}
// FileAttachment 以一种有效的方式将指定的文件写入主体流中在客户端,通常将使用给定的文件名下载该文件。
func (c *Context) FileAttachment(filepath, filename string) {
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
http.ServeFile(c.Writer, c.Request, filepath)
}
// SSEvent 将服务器发送的事件写入主体流。
func (c *Context) SSEvent(name string, message interface{}) {
c.Render(-1, sse.Event{
Event: name,
Data: message,
})
}
// Stream 发送流式响应并返回布尔值,指示 "Is client disconnected in middle of stream"。
func (c *Context) Stream(step func(w io.Writer) bool) bool {
w := c.Writer
clientGone := w.CloseNotify()
for {
select {
case <-clientGone:
return true
default:
keepOpen := step(w)
w.Flush()
if !keepOpen {
return false
}
}
}
}
/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/
// Negotiate contains all negotiations data.
type Negotiate struct {
Offered []string
HTMLName string
HTMLData interface{}
JSONData interface{}
XMLData interface{}
YAMLData interface{}
Data interface{}
}
// Negotiate 根据可接受的接受格式调用不同的渲染器。
func (c *Context) Negotiate(code int, config Negotiate) {
switch c.NegotiateFormat(config.Offered...) {
case bind.MiMeJSON:
data := chooseData(config.JSONData, config.Data)
c.JSON(code, data)
case bind.MiMeHTML:
data := chooseData(config.HTMLData, config.Data)
c.HTML(code, config.HTMLName, data)
case bind.MiMeXML:
data := chooseData(config.XMLData, config.Data)
c.XML(code, data)
case bind.MiMeYAML:
data := chooseData(config.YAMLData, config.Data)
c.YAML(code, data)
default:
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) // nolint: errcheck
}
}
// NegotiateFormat 返回可接受的接受格式。
func (c *Context) NegotiateFormat(offered ...string) string {
assert1(len(offered) > 0, "you must provide at least one offer")
if c.Accepted == nil {
c.Accepted = parseAccept(c.requestHeader("Accept"))
}
if len(c.Accepted) == 0 {
return offered[0]
}
for _, accepted := range c.Accepted {
for _, offer := range offered {
// According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers,
// therefore we can just iterate over the string without casting it into []rune
i := 0
for ; i < len(accepted); i++ {
if accepted[i] == '*' || offer[i] == '*' {
return offer
}
if accepted[i] != offer[i] {
break
}
}
if i == len(accepted) {
return offer
}
}
}
return ""
}
// SetAccepted 设置接受标头数据。
func (c *Context) SetAccepted(formats ...string) {
c.Accepted = formats
}
/************************************/
/***** GOLANG.ORG/X/NET/CONTEXT *****/
/************************************/
// Deadline 总是返回没有截止日期(ok == false),也许您想使用 Request.Context().Deadline() 代替。
func (c *Context) Deadline() (deadline time.Time, ok bool) {
return
}
// Done 总是返回nil (chan将永远等待),如果要在关闭连接时中止工作,则应改用 Request.Context().Done()。
func (c *Context) Done() <-chan struct{} {
return nil
}
// Err 总是返回 nil,也许您想使用 Request.Context().Err()代替。
func (c *Context) Err() error {
return nil
}
// Value 返回与此 *Context 关联的键值;如果没有值与键关联,则返回nil。使用相同的键连续调用Value会返回相同的结果。
func (c *Context) Value(key interface{}) interface{} {
if key == 0 {
return c.Request
}
if keyAsString, ok := key.(string); ok {
val, _ := c.Get(keyAsString)
return val
}
return nil
}
// IsAjax 判断当前请求是否是Ajax请求
func (c *Context)IsAjax()bool{
return strings.EqualFold(c.Request.Header.Get("X-Requested-With"), "XMLHttpRequest")
}
// 处理跨域请求,支持options访问
func(c *Context)Cors()HandlerFunc{
return func(r *Context) {
method := r.Request.Method
r.Header("Access-Control-Allow-Origin", "*")
r.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,X-Token,X-User-Id")
r.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
r.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
r.Header("Access-Control-Allow-Credentials", "true")
if method == "OPTIONS" {// 放行所有OPTIONS方法
r.AbortWithStatus(http.StatusNoContent)
}
r.Next()// 处理请求
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/unsafe-rust/gin.git
git@gitee.com:unsafe-rust/gin.git
unsafe-rust
gin
gin
v1.7.2

搜索帮助