Merge tag 'tags/v1.11.0'
This commit is contained in:
+161
-187
@@ -6,7 +6,9 @@ package gin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"math"
|
||||
"mime/multipart"
|
||||
@@ -71,7 +73,7 @@ type Context struct {
|
||||
mu sync.RWMutex
|
||||
|
||||
// Keys is a key/value pair exclusively for the context of each request.
|
||||
Keys map[string]any
|
||||
Keys map[any]any
|
||||
|
||||
// Errors is a list of errors attached to all the handlers/middlewares who used this context.
|
||||
Errors errorMsgs
|
||||
@@ -128,7 +130,7 @@ func (c *Context) Copy() *Context {
|
||||
cp.fullPath = c.fullPath
|
||||
|
||||
cKeys := c.Keys
|
||||
cp.Keys = make(map[string]any, len(cKeys))
|
||||
cp.Keys = make(map[any]any, len(cKeys))
|
||||
c.mu.RLock()
|
||||
for k, v := range cKeys {
|
||||
cp.Keys[k] = v
|
||||
@@ -186,10 +188,9 @@ func (c *Context) FullPath() string {
|
||||
func (c *Context) Next() {
|
||||
c.index++
|
||||
for c.index < int8(len(c.handlers)) {
|
||||
if c.handlers[c.index] == nil {
|
||||
continue
|
||||
if c.handlers[c.index] != nil {
|
||||
c.handlers[c.index](c)
|
||||
}
|
||||
c.handlers[c.index](c)
|
||||
c.index++
|
||||
}
|
||||
}
|
||||
@@ -215,6 +216,14 @@ func (c *Context) AbortWithStatus(code int) {
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
// AbortWithStatusPureJSON calls `Abort()` and then `PureJSON` internally.
|
||||
// This method stops the chain, writes the status code and return a JSON body without escaping.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
func (c *Context) AbortWithStatusPureJSON(code int, jsonObj any) {
|
||||
c.Abort()
|
||||
c.PureJSON(code, jsonObj)
|
||||
}
|
||||
|
||||
// AbortWithStatusJSON calls `Abort()` and then `JSON` internally.
|
||||
// This method stops the chain, writes the status code and return a JSON body.
|
||||
// It also sets the Content-Type as "application/json".
|
||||
@@ -264,11 +273,11 @@ func (c *Context) Error(err error) *Error {
|
||||
|
||||
// Set is used to store a new key/value pair exclusively for this context.
|
||||
// It also lazy initializes c.Keys if it was not used previously.
|
||||
func (c *Context) Set(key string, value any) {
|
||||
func (c *Context) Set(key any, value any) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.Keys == nil {
|
||||
c.Keys = make(map[string]any)
|
||||
c.Keys = make(map[any]any)
|
||||
}
|
||||
|
||||
c.Keys[key] = value
|
||||
@@ -276,7 +285,7 @@ func (c *Context) Set(key string, value any) {
|
||||
|
||||
// Get returns the value for the given key, ie: (value, true).
|
||||
// If the value does not exist it returns (nil, false)
|
||||
func (c *Context) Get(key string) (value any, exists bool) {
|
||||
func (c *Context) Get(key any) (value any, exists bool) {
|
||||
c.mu.RLock()
|
||||
defer c.mu.RUnlock()
|
||||
value, exists = c.Keys[key]
|
||||
@@ -284,255 +293,178 @@ func (c *Context) Get(key string) (value any, exists bool) {
|
||||
}
|
||||
|
||||
// MustGet returns the value for the given key if it exists, otherwise it panics.
|
||||
func (c *Context) MustGet(key string) any {
|
||||
func (c *Context) MustGet(key any) any {
|
||||
if value, exists := c.Get(key); exists {
|
||||
return value
|
||||
}
|
||||
panic("Key \"" + key + "\" does not exist")
|
||||
panic(fmt.Sprintf("key %v does not exist", key))
|
||||
}
|
||||
|
||||
func getTyped[T any](c *Context, key any) (res T) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
res, _ = val.(T)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetString returns the value associated with the key as a string.
|
||||
func (c *Context) GetString(key string) (s string) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
s, _ = val.(string)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetString(key any) (s string) {
|
||||
return getTyped[string](c, key)
|
||||
}
|
||||
|
||||
// GetBool returns the value associated with the key as a boolean.
|
||||
func (c *Context) GetBool(key string) (b bool) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
b, _ = val.(bool)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetBool(key any) (b bool) {
|
||||
return getTyped[bool](c, key)
|
||||
}
|
||||
|
||||
// GetInt returns the value associated with the key as an integer.
|
||||
func (c *Context) GetInt(key string) (i int) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i, _ = val.(int)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetInt(key any) (i int) {
|
||||
return getTyped[int](c, key)
|
||||
}
|
||||
|
||||
// GetInt8 returns the value associated with the key as an integer 8.
|
||||
func (c *Context) GetInt8(key string) (i8 int8) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i8, _ = val.(int8)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetInt8(key any) (i8 int8) {
|
||||
return getTyped[int8](c, key)
|
||||
}
|
||||
|
||||
// GetInt16 returns the value associated with the key as an integer 16.
|
||||
func (c *Context) GetInt16(key string) (i16 int16) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i16, _ = val.(int16)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetInt16(key any) (i16 int16) {
|
||||
return getTyped[int16](c, key)
|
||||
}
|
||||
|
||||
// GetInt32 returns the value associated with the key as an integer 32.
|
||||
func (c *Context) GetInt32(key string) (i32 int32) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i32, _ = val.(int32)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetInt32(key any) (i32 int32) {
|
||||
return getTyped[int32](c, key)
|
||||
}
|
||||
|
||||
// GetInt64 returns the value associated with the key as an integer 64.
|
||||
func (c *Context) GetInt64(key string) (i64 int64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i64, _ = val.(int64)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetInt64(key any) (i64 int64) {
|
||||
return getTyped[int64](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetUint(key any) (ui uint) {
|
||||
return getTyped[uint](c, key)
|
||||
}
|
||||
|
||||
// GetUint8 returns the value associated with the key as an unsigned integer 8.
|
||||
func (c *Context) GetUint8(key string) (ui8 uint8) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui8, _ = val.(uint8)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetUint8(key any) (ui8 uint8) {
|
||||
return getTyped[uint8](c, key)
|
||||
}
|
||||
|
||||
// GetUint16 returns the value associated with the key as an unsigned integer 16.
|
||||
func (c *Context) GetUint16(key string) (ui16 uint16) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui16, _ = val.(uint16)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetUint16(key any) (ui16 uint16) {
|
||||
return getTyped[uint16](c, key)
|
||||
}
|
||||
|
||||
// GetUint32 returns the value associated with the key as an unsigned integer 32.
|
||||
func (c *Context) GetUint32(key string) (ui32 uint32) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui32, _ = val.(uint32)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetUint32(key any) (ui32 uint32) {
|
||||
return getTyped[uint32](c, key)
|
||||
}
|
||||
|
||||
// GetUint64 returns the value associated with the key as an unsigned integer 64.
|
||||
func (c *Context) GetUint64(key string) (ui64 uint64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui64, _ = val.(uint64)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetUint64(key any) (ui64 uint64) {
|
||||
return getTyped[uint64](c, key)
|
||||
}
|
||||
|
||||
// GetFloat32 returns the value associated with the key as a float32.
|
||||
func (c *Context) GetFloat32(key string) (f32 float32) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
f32, _ = val.(float32)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetFloat32(key any) (f32 float32) {
|
||||
return getTyped[float32](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetFloat64(key any) (f64 float64) {
|
||||
return getTyped[float64](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetTime(key any) (t time.Time) {
|
||||
return getTyped[time.Time](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetDuration(key any) (d time.Duration) {
|
||||
return getTyped[time.Duration](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetIntSlice(key string) (is []int) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
is, _ = val.([]int)
|
||||
}
|
||||
return
|
||||
// GetIntSlice returns the value associated with the key as a slice of integers.
|
||||
func (c *Context) GetIntSlice(key any) (is []int) {
|
||||
return getTyped[[]int](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetInt8Slice(key string) (i8s []int8) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i8s, _ = val.([]int8)
|
||||
}
|
||||
return
|
||||
// GetInt8Slice returns the value associated with the key as a slice of int8 integers.
|
||||
func (c *Context) GetInt8Slice(key any) (i8s []int8) {
|
||||
return getTyped[[]int8](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetInt16Slice(key string) (i16s []int16) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i16s, _ = val.([]int16)
|
||||
}
|
||||
return
|
||||
// GetInt16Slice returns the value associated with the key as a slice of int16 integers.
|
||||
func (c *Context) GetInt16Slice(key any) (i16s []int16) {
|
||||
return getTyped[[]int16](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetInt32Slice(key string) (i32s []int32) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i32s, _ = val.([]int32)
|
||||
}
|
||||
return
|
||||
// GetInt32Slice returns the value associated with the key as a slice of int32 integers.
|
||||
func (c *Context) GetInt32Slice(key any) (i32s []int32) {
|
||||
return getTyped[[]int32](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetInt64Slice(key string) (i64s []int64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
i64s, _ = val.([]int64)
|
||||
}
|
||||
return
|
||||
// GetInt64Slice returns the value associated with the key as a slice of int64 integers.
|
||||
func (c *Context) GetInt64Slice(key any) (i64s []int64) {
|
||||
return getTyped[[]int64](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetUintSlice(key string) (uis []uint) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
uis, _ = val.([]uint)
|
||||
}
|
||||
return
|
||||
// GetUintSlice returns the value associated with the key as a slice of unsigned integers.
|
||||
func (c *Context) GetUintSlice(key any) (uis []uint) {
|
||||
return getTyped[[]uint](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetUint8Slice(key string) (ui8s []uint8) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui8s, _ = val.([]uint8)
|
||||
}
|
||||
return
|
||||
// GetUint8Slice returns the value associated with the key as a slice of uint8 integers.
|
||||
func (c *Context) GetUint8Slice(key any) (ui8s []uint8) {
|
||||
return getTyped[[]uint8](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetUint16Slice(key string) (ui16s []uint16) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui16s, _ = val.([]uint16)
|
||||
}
|
||||
return
|
||||
// GetUint16Slice returns the value associated with the key as a slice of uint16 integers.
|
||||
func (c *Context) GetUint16Slice(key any) (ui16s []uint16) {
|
||||
return getTyped[[]uint16](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetUint32Slice(key string) (ui32s []uint32) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui32s, _ = val.([]uint32)
|
||||
}
|
||||
return
|
||||
// GetUint32Slice returns the value associated with the key as a slice of uint32 integers.
|
||||
func (c *Context) GetUint32Slice(key any) (ui32s []uint32) {
|
||||
return getTyped[[]uint32](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetUint64Slice(key string) (ui64s []uint64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
ui64s, _ = val.([]uint64)
|
||||
}
|
||||
return
|
||||
// GetUint64Slice returns the value associated with the key as a slice of uint64 integers.
|
||||
func (c *Context) GetUint64Slice(key any) (ui64s []uint64) {
|
||||
return getTyped[[]uint64](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetFloat32Slice(key string) (f32s []float32) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
f32s, _ = val.([]float32)
|
||||
}
|
||||
return
|
||||
// GetFloat32Slice returns the value associated with the key as a slice of float32 numbers.
|
||||
func (c *Context) GetFloat32Slice(key any) (f32s []float32) {
|
||||
return getTyped[[]float32](c, key)
|
||||
}
|
||||
|
||||
func (c *Context) GetFloat64Slice(key string) (f64s []float64) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
f64s, _ = val.([]float64)
|
||||
}
|
||||
return
|
||||
// GetFloat64Slice returns the value associated with the key as a slice of float64 numbers.
|
||||
func (c *Context) GetFloat64Slice(key any) (f64s []float64) {
|
||||
return getTyped[[]float64](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetStringSlice(key any) (ss []string) {
|
||||
return getTyped[[]string](c, key)
|
||||
}
|
||||
|
||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||
func (c *Context) GetStringMap(key string) (sm map[string]any) {
|
||||
if val, ok := c.Get(key); ok && val != nil {
|
||||
sm, _ = val.(map[string]any)
|
||||
}
|
||||
return
|
||||
func (c *Context) GetStringMap(key any) (sm map[string]any) {
|
||||
return getTyped[map[string]any](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetStringMapString(key any) (sms map[string]string) {
|
||||
return getTyped[map[string]string](c, key)
|
||||
}
|
||||
|
||||
// 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
|
||||
func (c *Context) GetStringMapStringSlice(key any) (smss map[string][]string) {
|
||||
return getTyped[map[string][]string](c, key)
|
||||
}
|
||||
|
||||
/************************************/
|
||||
@@ -641,7 +573,7 @@ func (c *Context) QueryMap(key string) (dicts map[string]string) {
|
||||
// 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)
|
||||
return getMapFromFormData(c.queryCache, key)
|
||||
}
|
||||
|
||||
// PostForm returns the specified key from a POST urlencoded form or multipart form
|
||||
@@ -714,22 +646,32 @@ func (c *Context) PostFormMap(key string) (dicts map[string]string) {
|
||||
// whether at least one value exists for the given key.
|
||||
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
|
||||
c.initFormCache()
|
||||
return c.get(c.formCache, key)
|
||||
return getMapFromFormData(c.formCache, key)
|
||||
}
|
||||
|
||||
// get is an internal method and returns a map which satisfies conditions.
|
||||
func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) {
|
||||
dicts := make(map[string]string)
|
||||
exist := false
|
||||
// getMapFromFormData return a map which satisfies conditions.
|
||||
// It parses from data with bracket notation like "key[subkey]=value" into a map.
|
||||
func getMapFromFormData(m map[string][]string, key string) (map[string]string, bool) {
|
||||
d := make(map[string]string)
|
||||
found := false
|
||||
keyLen := len(key)
|
||||
|
||||
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
|
||||
dicts[k[i+1:][:j]] = v[0]
|
||||
}
|
||||
if len(k) < keyLen+3 { // key + "[" + at least one char + "]"
|
||||
continue
|
||||
}
|
||||
|
||||
if k[:keyLen] != key || k[keyLen] != '[' {
|
||||
continue
|
||||
}
|
||||
|
||||
if j := strings.IndexByte(k[keyLen+1:], ']'); j > 0 {
|
||||
found = true
|
||||
d[k[keyLen+1:keyLen+1+j]] = v[0]
|
||||
}
|
||||
}
|
||||
return dicts, exist
|
||||
|
||||
return d, found
|
||||
}
|
||||
|
||||
// FormFile returns the first file for the provided form key.
|
||||
@@ -754,14 +696,22 @@ func (c *Context) MultipartForm() (*multipart.Form, error) {
|
||||
}
|
||||
|
||||
// SaveUploadedFile uploads the form file to specific dst.
|
||||
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
|
||||
func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string, perm ...fs.FileMode) error {
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil {
|
||||
var mode os.FileMode = 0o750
|
||||
if len(perm) > 0 {
|
||||
mode = perm[0]
|
||||
}
|
||||
dir := filepath.Dir(dst)
|
||||
if err = os.MkdirAll(dir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = os.Chmod(dir, mode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -838,8 +788,19 @@ func (c *Context) BindUri(obj any) error {
|
||||
// It will abort the request with HTTP 400 if any error occurs.
|
||||
// See the binding package.
|
||||
func (c *Context) MustBindWith(obj any, b binding.Binding) error {
|
||||
if err := c.ShouldBindWith(obj, b); err != nil {
|
||||
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
|
||||
err := c.ShouldBindWith(obj, b)
|
||||
if err != nil {
|
||||
var maxBytesErr *http.MaxBytesError
|
||||
|
||||
// Note: When using sonic or go-json as JSON encoder, they do not propagate the http.MaxBytesError error
|
||||
// https://github.com/goccy/go-json/issues/485
|
||||
// https://github.com/bytedance/sonic/issues/800
|
||||
switch {
|
||||
case errors.As(err, &maxBytesErr):
|
||||
c.AbortWithError(http.StatusRequestEntityTooLarge, err).SetType(ErrorTypeBind) //nolint: errcheck
|
||||
default:
|
||||
c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -958,7 +919,7 @@ func (c *Context) ShouldBindBodyWithPlain(obj any) error {
|
||||
|
||||
// ClientIP implements one best effort algorithm to return the real client IP.
|
||||
// It calls c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not.
|
||||
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]).
|
||||
// If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-IP]).
|
||||
// If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy,
|
||||
// the remote IP (coming from Request.RemoteAddr) is returned.
|
||||
func (c *Context) ClientIP() string {
|
||||
@@ -1096,6 +1057,19 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string,
|
||||
})
|
||||
}
|
||||
|
||||
// SetCookieData adds a Set-Cookie header to the ResponseWriter's headers.
|
||||
// It accepts a pointer to http.Cookie structure for more flexibility in setting cookie attributes.
|
||||
// The provided cookie must have a valid Name. Invalid cookies may be silently dropped.
|
||||
func (c *Context) SetCookieData(cookie *http.Cookie) {
|
||||
if cookie.Path == "" {
|
||||
cookie.Path = "/"
|
||||
}
|
||||
if cookie.SameSite == http.SameSiteDefaultMode {
|
||||
cookie.SameSite = c.sameSite
|
||||
}
|
||||
http.SetCookie(c.Writer, cookie)
|
||||
}
|
||||
|
||||
// Cookie returns the named cookie provided in the request or
|
||||
// ErrNoCookie if not found. And return the named cookie is unescaped.
|
||||
// If multiple cookies match the given name, only one cookie will
|
||||
|
||||
Reference in New Issue
Block a user