func (ctx *Context) QueryDefault(key, def string) string {
	value := ctx.Query(key)
	if value == "" {
		return def
	}
	return value
}
QueryDefault get the query parameter of url. If it is empty, return the default.
package context
import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"math"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
	"time"
	"github.com/GoAdminGroup/go-admin/modules/constant"
)
const abortIndex int8 = math.MaxInt8 / 2
type Context struct {
	Request   *http.Request
	Response  *http.Response
	UserValue map[string]interface{}
	index     int8
	handlers  Handlers
}
type Path struct {
	URL    string
	Method string
}
type RouterMap map[string]Router
func (r RouterMap) Get(name string) Router {
	return r[name]
}
type Router struct {
	Methods []string
	Patten  string
}
func (r Router) Method() string {
	return r.Methods[0]
}
func (r Router) GetURL(value ...string) string {
	u := r.Patten
	for i := 0; i < len(value); i += 2 {
		u = strings.ReplaceAll(u, ":__"+value[i], value[i+1])
	}
	return u
}
type NodeProcessor func(...Node)
type Node struct {
	Path     string
	Method   string
	Handlers []Handler
	Value    map[string]interface{}
}
func (ctx *Context) SetUserValue(key string, value interface{}) {
	ctx.UserValue[key] = value
}
func (ctx *Context) Path() string {
	return ctx.Request.URL.Path
}
func (ctx *Context) Abort() {
	ctx.index = abortIndex
}
func (ctx *Context) Next() {
	ctx.index++
	for s := int8(len(ctx.handlers)); ctx.index < s; ctx.index++ {
		ctx.handlers[ctx.index](ctx)
	}
}
func (ctx *Context) SetHandlers(handlers Handlers) *Context {
	ctx.handlers = handlers
	return ctx
}
func (ctx *Context) Method() string {
	return ctx.Request.Method
}
func NewContext(req *http.Request) *Context {
	return &Context{
		Request:   req,
		UserValue: make(map[string]interface{}),
		Response: &http.Response{
			StatusCode: http.StatusOK,
			Header:     make(http.Header),
		},
		index: -1,
	}
}
const (
	HeaderContentType = "Content-Type"
	HeaderLastModified    = "Last-Modified"
	HeaderIfModifiedSince = "If-Modified-Since"
	HeaderCacheControl    = "Cache-Control"
	HeaderETag            = "ETag"
	HeaderContentDisposition = "Content-Disposition"
	HeaderContentLength      = "Content-Length"
	HeaderContentEncoding    = "Content-Encoding"
	GzipHeaderValue      = "gzip"
	HeaderAcceptEncoding = "Accept-Encoding"
	HeaderVary           = "Vary"
)
func (ctx *Context) BindJSON(data interface{}) error {
	if ctx.Request.Body != nil {
		b, err := ioutil.ReadAll(ctx.Request.Body)
		if err == nil {
			return json.Unmarshal(b, data)
		}
		return err
	}
	return errors.New("empty request body")
}
func (ctx *Context) MustBindJSON(data interface{}) {
	if ctx.Request.Body != nil {
		b, err := ioutil.ReadAll(ctx.Request.Body)
		if err != nil {
			panic(err)
		}
		err = json.Unmarshal(b, data)
		if err != nil {
			panic(err)
		}
	}
	panic("empty request body")
}
func (ctx *Context) Write(code int, header map[string]string, Body string) {
	ctx.Response.StatusCode = code
	for key, head := range header {
		ctx.AddHeader(key, head)
	}
	ctx.Response.Body = ioutil.NopCloser(strings.NewReader(Body))
}
func (ctx *Context) JSON(code int, Body map[string]interface{}) {
	ctx.Response.StatusCode = code
	ctx.SetContentType("application/json")
	BodyStr, err := json.Marshal(Body)
	if err != nil {
		panic(err)
	}
	ctx.Response.Body = ioutil.NopCloser(bytes.NewReader(BodyStr))
}
func (ctx *Context) DataWithHeaders(code int, header map[string]string, data []byte) {
	ctx.Response.StatusCode = code
	for key, head := range header {
		ctx.AddHeader(key, head)
	}
	ctx.Response.Body = ioutil.NopCloser(bytes.NewBuffer(data))
}
func (ctx *Context) Data(code int, contentType string, data []byte) {
	ctx.Response.StatusCode = code
	ctx.SetContentType(contentType)
	ctx.Response.Body = ioutil.NopCloser(bytes.NewBuffer(data))
}
func (ctx *Context) Redirect(path string) {
	ctx.Response.StatusCode = http.StatusFound
	ctx.SetContentType("text/html; charset=utf-8")
	ctx.AddHeader("Location", path)
}
func (ctx *Context) HTML(code int, body string) {
	ctx.SetContentType("text/html; charset=utf-8")
	ctx.SetStatusCode(code)
	ctx.WriteString(body)
}
func (ctx *Context) HTMLByte(code int, body []byte) {
	ctx.SetContentType("text/html; charset=utf-8")
	ctx.SetStatusCode(code)
	ctx.Response.Body = ioutil.NopCloser(bytes.NewBuffer(body))
}
func (ctx *Context) WriteString(body string) {
	ctx.Response.Body = ioutil.NopCloser(strings.NewReader(body))
}
func (ctx *Context) SetStatusCode(code int) {
	ctx.Response.StatusCode = code
}
func (ctx *Context) SetContentType(contentType string) {
	ctx.AddHeader(HeaderContentType, contentType)
}
func (ctx *Context) SetLastModified(modtime time.Time) {
	if !IsZeroTime(modtime) {
		ctx.AddHeader(HeaderLastModified, modtime.UTC().Format(http.TimeFormat))  
	}
}
var unixEpochTime = time.Unix(0, 0)
func IsZeroTime(t time.Time) bool {
	return t.IsZero() || t.Equal(unixEpochTime)
}
var ParseTime = func(text string) (t time.Time, err error) {
	t, err = time.Parse(http.TimeFormat, text)
	if err != nil {
		return http.ParseTime(text)
	}
	return
}
func (ctx *Context) WriteNotModified() {
	delete(ctx.Response.Header, HeaderContentType)
	delete(ctx.Response.Header, HeaderContentLength)
	if ctx.Headers(HeaderETag) != "" {
		delete(ctx.Response.Header, HeaderLastModified)
	}
	ctx.SetStatusCode(http.StatusNotModified)
}
func (ctx *Context) CheckIfModifiedSince(modtime time.Time) (bool, error) {
	if method := ctx.Method(); method != http.MethodGet && method != http.MethodHead {
		return false, errors.New("skip: method")
	}
	ims := ctx.Headers(HeaderIfModifiedSince)
	if ims == "" || IsZeroTime(modtime) {
		return false, errors.New("skip: zero time")
	}
	t, err := ParseTime(ims)
	if err != nil {
		return false, errors.New("skip: " + err.Error())
	}
	if modtime.UTC().Before(t.Add(1 * time.Second)) {
		return false, nil
	}
	return true, nil
}
func (ctx *Context) LocalIP() string {
	xForwardedFor := ctx.Request.Header.Get("X-Forwarded-For")
	ip := strings.TrimSpace(strings.Split(xForwardedFor, ",")[0])
	if ip != "" {
		return ip
	}
	ip = strings.TrimSpace(ctx.Request.Header.Get("X-Real-Ip"))
	if ip != "" {
		return ip
	}
	if ip, _, err := net.SplitHostPort(strings.TrimSpace(ctx.Request.RemoteAddr)); err == nil {
		return ip
	}
	return "127.0.0.1"
}
func (ctx *Context) SetCookie(cookie *http.Cookie) {
	if v := cookie.String(); v != "" {
		ctx.AddHeader("Set-Cookie", v)
	}
}
func (ctx *Context) Query(key string) string {
	return ctx.Request.URL.Query().Get(key)
}
func (ctx *Context) QueryAll(key string) []string {
	return ctx.Request.URL.Query()[key]
}