gin
context中断原理
const abortIndex int8 = math.MaxInt8 / 2
// Abort prevents pending handlers from being called. Note that this will not stop the current handler.
// Let's say you have an authorization middleware that validates that the current request is authorized.
// If the authorization fails (ex: the password does not match), call Abort to ensure the remaining handlers
// for this request are not called.
func (c *Context) Abort() {
// c.index赋值很大值,从导致下次Next都不执行,达到阻断执行链目的
c.index = abortIndex
}
// Next should be used only inside middleware.
// It executes the pending handlers in the chain inside the calling handler.
// See example in GitHub.
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
memcache中间件
import (
"bytes"
"net/http"
"sync"
"github.com/bradfitz/gomemcache/memcache"
"github.com/gin-gonic/gin"
)
var memcacheClientMux sync.Mutex
var memcacheClient *memcache.Client
// TODO,要不要带上http头部,例如:数据类型html/json/js,Date,Cache-control等等
type middleResp struct {
// 匿名包括,变相继承
gin.ResponseWriter
isOK bool
key string
expiration int32
// 如果匿名包括,变相继承会和ResponseWriter冲突,导致接口重复写
b bytes.Buffer
}
var StoreErrHandler func(error)
func (mr *middleResp) writeStore() {
if mr.b.Len() > 0 {
err := memcacheClient.Set(&memcache.Item{
Key: mr.key,
Flags: 0,
Expiration: mr.expiration,
Value: mr.b.Bytes(),
})
if StoreErrHandler != nil {
StoreErrHandler(err)
}
}
}
func (mr *middleResp) Write(body []byte) (int, error) {
if mr.isOK && memcacheClient != nil {
_, err := mr.b.Write(body)
if StoreErrHandler != nil {
StoreErrHandler(err)
}
}
return mr.ResponseWriter.Write(body)
}
func (mr *middleResp) WriteHeader(statusCode int) {
mr.isOK = statusCode == http.StatusOK
mr.ResponseWriter.WriteHeader(statusCode)
}
// Writes the string into the response body.
func (mr *middleResp) WriteString(s string) (int, error) {
return mr.ResponseWriter.WriteString(s)
}
func MemcacheStore(server string, expireSecond int32) gin.HandlerFunc {
return MemcacheWrap(server, expireSecond, func(c *gin.Context) { c.Next() })
}
func MemcacheWrap(server string, expireSecond int32, handler gin.HandlerFunc) gin.HandlerFunc {
memcacheClientMux.Lock()
defer memcacheClientMux.Unlock()
if memcacheClient == nil {
memcacheClient = memcache.New(server)
}
return func(c *gin.Context) {
mr := &middleResp{
ResponseWriter: c.Writer,
key: c.Request.RequestURI,
expiration: expireSecond,
}
c.Writer = mr
handler(c)
mr.writeStore()
}
}
main使用
import (
"fmt"
"math/rand"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(gin.Logger())
r.GET("/gin/wrap", MemcacheWrap("127.0.0.1:11211", 100, wrap))
group := r.Group("/gin/group")
group.Use(MemcacheStore("127.0.0.1:11211", 100))
group.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
group.GET("/gin", func(c *gin.Context) {
c.Data(200, "text/html",
[]byte(fmt.Sprintf("<H1>gin,%v</H1>", rand.Int())),
)
})
r.Run("0.0.0.0:5050")
}
func wrap(c *gin.Context) {
c.Data(200, "text/html",
[]byte(fmt.Sprintf("<H1>warp,%v</H1>", rand.Int())),
)
}