2023-04-23 17:57:36 +08:00
|
|
|
package http
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
2023-05-06 16:15:04 +08:00
|
|
|
"net"
|
2023-04-23 17:57:36 +08:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
defaultBinder = &DefaultBinder{}
|
|
|
|
)
|
|
|
|
|
2023-07-07 09:53:37 +08:00
|
|
|
var (
|
|
|
|
realIPHeaders = []string{
|
|
|
|
"Cf-Connecting-Ip",
|
|
|
|
"True-Client-IP",
|
|
|
|
"X-Forwarded-For",
|
|
|
|
"X-Real-Ip",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2023-04-23 17:57:36 +08:00
|
|
|
type Context struct {
|
|
|
|
ctx context.Context
|
|
|
|
req *http.Request
|
|
|
|
res http.ResponseWriter
|
|
|
|
params map[string]string
|
2023-09-20 10:16:05 +08:00
|
|
|
user *Userinfo
|
2023-04-23 17:57:36 +08:00
|
|
|
statusCode int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) reset(req *http.Request, res http.ResponseWriter, ps map[string]string) {
|
|
|
|
ctx.statusCode = http.StatusOK
|
2023-09-20 10:16:05 +08:00
|
|
|
ctx.user = nil
|
2023-04-23 17:57:36 +08:00
|
|
|
ctx.req, ctx.res, ctx.params = req, res, ps
|
|
|
|
}
|
|
|
|
|
2023-09-20 10:16:05 +08:00
|
|
|
func (ctx *Context) User() *Userinfo {
|
|
|
|
return ctx.user
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) SetUser(ui *Userinfo) {
|
|
|
|
ctx.user = ui
|
|
|
|
}
|
|
|
|
|
2023-05-31 11:14:15 +08:00
|
|
|
func (ctx *Context) RealIp() string {
|
2023-07-07 09:53:37 +08:00
|
|
|
var (
|
2023-08-15 17:42:32 +08:00
|
|
|
s string
|
2023-07-07 09:53:37 +08:00
|
|
|
pos int
|
|
|
|
ipaddr string
|
|
|
|
)
|
|
|
|
for _, h := range realIPHeaders {
|
|
|
|
if ipaddr = ctx.Request().Header.Get(h); ipaddr != "" {
|
|
|
|
goto __end
|
2023-05-06 16:15:04 +08:00
|
|
|
}
|
|
|
|
}
|
2023-07-07 09:53:37 +08:00
|
|
|
ipaddr, _, _ = net.SplitHostPort(ctx.Request().RemoteAddr)
|
|
|
|
__end:
|
2023-08-15 17:42:32 +08:00
|
|
|
for {
|
|
|
|
if pos = strings.IndexByte(ipaddr, ','); pos > -1 {
|
|
|
|
s = strings.TrimSpace(ipaddr[:pos])
|
2023-09-20 10:16:05 +08:00
|
|
|
if netAddr := net.ParseIP(s); netAddr != nil && !netAddr.IsPrivate() {
|
2023-08-15 17:42:32 +08:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
ipaddr = ipaddr[pos+1:]
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
2023-05-06 16:15:04 +08:00
|
|
|
}
|
2023-08-16 09:57:42 +08:00
|
|
|
return strings.TrimSpace(ipaddr)
|
2023-05-06 16:15:04 +08:00
|
|
|
}
|
|
|
|
|
2023-04-23 17:57:36 +08:00
|
|
|
func (ctx *Context) Request() *http.Request {
|
|
|
|
return ctx.req
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Response() http.ResponseWriter {
|
|
|
|
return ctx.res
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Context() context.Context {
|
|
|
|
if ctx.Request().Context() != nil {
|
|
|
|
return ctx.Request().Context()
|
|
|
|
}
|
|
|
|
return ctx.ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Bind(v any) (err error) {
|
|
|
|
return defaultBinder.Bind(v, ctx.Request())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Query(k string) string {
|
2023-09-20 10:16:05 +08:00
|
|
|
qs := ctx.Request().URL.Query()
|
|
|
|
if qs == nil {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return qs.Get(k)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Form(k string) string {
|
2023-04-23 17:57:36 +08:00
|
|
|
return ctx.Request().FormValue(k)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Param(k string) string {
|
|
|
|
var (
|
|
|
|
ok bool
|
2023-09-20 10:16:05 +08:00
|
|
|
s string
|
2023-04-23 17:57:36 +08:00
|
|
|
)
|
2023-09-20 10:16:05 +08:00
|
|
|
if s, ok = ctx.params[k]; ok {
|
|
|
|
return s
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
2023-09-20 10:16:05 +08:00
|
|
|
s = ctx.Query(k)
|
|
|
|
if s == "" {
|
|
|
|
s = ctx.Form(k)
|
|
|
|
}
|
|
|
|
return s
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
|
|
|
|
2023-07-07 09:53:37 +08:00
|
|
|
func (ctx *Context) json(res responsePayload) (err error) {
|
2023-04-23 17:57:36 +08:00
|
|
|
ctx.Response().Header().Set("Content-Type", "application/json")
|
|
|
|
encoder := json.NewEncoder(ctx.Response())
|
2024-07-19 17:37:34 +08:00
|
|
|
encoder.SetEscapeHTML(false)
|
2023-04-23 17:57:36 +08:00
|
|
|
if strings.HasPrefix(ctx.Request().Header.Get("User-Agent"), "curl") {
|
|
|
|
encoder.SetIndent("", "\t")
|
|
|
|
}
|
|
|
|
return encoder.Encode(res)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Success(v any) (err error) {
|
2023-07-07 09:53:37 +08:00
|
|
|
return ctx.json(responsePayload{Data: v})
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Status(code int) {
|
|
|
|
ctx.statusCode = code
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Error(code int, reason string) (err error) {
|
2023-07-07 09:53:37 +08:00
|
|
|
return ctx.json(responsePayload{Code: code, Reason: reason})
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) Redirect(url string, code int) {
|
|
|
|
if code != http.StatusFound && code != http.StatusMovedPermanently {
|
2023-08-16 09:57:42 +08:00
|
|
|
code = http.StatusFound
|
2023-04-23 17:57:36 +08:00
|
|
|
}
|
|
|
|
http.Redirect(ctx.Response(), ctx.Request(), url, code)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) SetCookie(cookie *http.Cookie) {
|
|
|
|
http.SetCookie(ctx.Response(), cookie)
|
|
|
|
}
|
|
|
|
|
2023-09-20 10:16:05 +08:00
|
|
|
func (ctx *Context) GetCookie(name string) (*http.Cookie, error) {
|
|
|
|
return ctx.Request().Cookie(name)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) DeleteCookie(name string) {
|
|
|
|
cookie, err := ctx.GetCookie(name)
|
|
|
|
if err == nil {
|
|
|
|
cookie.MaxAge = -1
|
|
|
|
ctx.SetCookie(cookie)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) SetCookieValue(name, value, domain string) {
|
|
|
|
if domain == "" {
|
|
|
|
domain = ctx.Request().URL.Hostname()
|
|
|
|
}
|
|
|
|
if name == "" || value == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
ctx.SetCookie(&http.Cookie{
|
|
|
|
Name: name,
|
|
|
|
Value: value,
|
|
|
|
Path: "/",
|
|
|
|
Domain: domain,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) GetCookieValue(name string) string {
|
|
|
|
if name == "" {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
cookie, err := ctx.GetCookie(name)
|
|
|
|
if err == nil {
|
|
|
|
return cookie.Value
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2023-04-23 17:57:36 +08:00
|
|
|
func (ctx *Context) SendFile(filename string) (err error) {
|
|
|
|
var (
|
|
|
|
fi os.FileInfo
|
|
|
|
fp *os.File
|
|
|
|
)
|
|
|
|
if fi, err = os.Stat(filename); err == nil {
|
|
|
|
if fp, err = os.Open(filename); err == nil {
|
|
|
|
http.ServeContent(ctx.Response(), ctx.Request(), path.Base(filename), fi.ModTime(), fp)
|
|
|
|
err = fp.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|