mirror of
https://github.com/infinite-iroha/touka.git
synced 2026-02-02 16:31:11 +08:00
add wanf
This commit is contained in:
parent
3590a77f90
commit
3ffde5742c
4 changed files with 63 additions and 25 deletions
61
context.go
61
context.go
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/WJQSERVER/wanf"
|
||||||
"github.com/fenthope/reco"
|
"github.com/fenthope/reco"
|
||||||
"github.com/go-json-experiment/json"
|
"github.com/go-json-experiment/json"
|
||||||
|
|
||||||
|
|
@ -42,7 +43,7 @@ type Context struct {
|
||||||
index int8 // 当前执行到处理链的哪个位置
|
index int8 // 当前执行到处理链的哪个位置
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
Keys map[string]interface{} // 用于在中间件之间传递数据
|
Keys map[string]any // 用于在中间件之间传递数据
|
||||||
|
|
||||||
Errors []error // 用于收集处理过程中的错误
|
Errors []error // 用于收集处理过程中的错误
|
||||||
|
|
||||||
|
|
@ -77,20 +78,18 @@ func (c *Context) reset(w http.ResponseWriter, req *http.Request) {
|
||||||
} else {
|
} else {
|
||||||
c.Writer = newResponseWriter(w)
|
c.Writer = newResponseWriter(w)
|
||||||
}
|
}
|
||||||
//c.Writer = newResponseWriter(w)
|
|
||||||
|
|
||||||
c.Request = req
|
c.Request = req
|
||||||
c.Params = c.Params[:0] // 清空 Params 切片,而不是重新分配,以复用底层数组
|
c.Params = c.Params[:0] // 清空 Params 切片,而不是重新分配,以复用底层数组
|
||||||
c.handlers = nil
|
c.handlers = nil
|
||||||
c.index = -1 // 初始为 -1,`Next()` 将其设置为 0
|
c.index = -1 // 初始为 -1,`Next()` 将其设置为 0
|
||||||
c.Keys = make(map[string]interface{}) // 每次请求重新创建 map,避免数据污染
|
c.Keys = make(map[string]any) // 每次请求重新创建 map,避免数据污染
|
||||||
c.Errors = c.Errors[:0] // 清空 Errors 切片
|
c.Errors = c.Errors[:0] // 清空 Errors 切片
|
||||||
c.queryCache = nil // 清空查询参数缓存
|
c.queryCache = nil // 清空查询参数缓存
|
||||||
c.formCache = nil // 清空表单数据缓存
|
c.formCache = nil // 清空表单数据缓存
|
||||||
c.ctx = req.Context() // 使用请求的上下文,继承其取消信号和值
|
c.ctx = req.Context() // 使用请求的上下文,继承其取消信号和值
|
||||||
c.sameSite = http.SameSiteDefaultMode // 默认 SameSite 模式
|
c.sameSite = http.SameSiteDefaultMode // 默认 SameSite 模式
|
||||||
c.MaxRequestBodySize = c.engine.GlobalMaxRequestBodySize
|
c.MaxRequestBodySize = c.engine.GlobalMaxRequestBodySize
|
||||||
// c.HTTPClient 和 c.engine 保持不变,它们引用 Engine 实例的成员
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next 在处理链中执行下一个处理函数
|
// Next 在处理链中执行下一个处理函数
|
||||||
|
|
@ -122,10 +121,10 @@ func (c *Context) AbortWithStatus(code int) {
|
||||||
|
|
||||||
// Set 将一个键值对存储到 Context 中
|
// Set 将一个键值对存储到 Context 中
|
||||||
// 这是一个线程安全的操作,用于在中间件之间传递数据
|
// 这是一个线程安全的操作,用于在中间件之间传递数据
|
||||||
func (c *Context) Set(key string, value interface{}) {
|
func (c *Context) Set(key string, value any) {
|
||||||
c.mu.Lock() // 加写锁
|
c.mu.Lock() // 加写锁
|
||||||
if c.Keys == nil {
|
if c.Keys == nil {
|
||||||
c.Keys = make(map[string]interface{})
|
c.Keys = make(map[string]any)
|
||||||
}
|
}
|
||||||
c.Keys[key] = value
|
c.Keys[key] = value
|
||||||
c.mu.Unlock() // 解写锁
|
c.mu.Unlock() // 解写锁
|
||||||
|
|
@ -133,7 +132,7 @@ func (c *Context) Set(key string, value interface{}) {
|
||||||
|
|
||||||
// Get 从 Context 中获取一个值
|
// Get 从 Context 中获取一个值
|
||||||
// 这是一个线程安全的操作
|
// 这是一个线程安全的操作
|
||||||
func (c *Context) Get(key string) (value interface{}, exists bool) {
|
func (c *Context) Get(key string) (value any, exists bool) {
|
||||||
c.mu.RLock() // 加读锁
|
c.mu.RLock() // 加读锁
|
||||||
value, exists = c.Keys[key]
|
value, exists = c.Keys[key]
|
||||||
c.mu.RUnlock() // 解读锁
|
c.mu.RUnlock() // 解读锁
|
||||||
|
|
@ -208,7 +207,7 @@ func (c *Context) GetDuration(key string) (value time.Duration, exists bool) {
|
||||||
|
|
||||||
// MustGet 从 Context 中获取一个值,如果不存在则 panic
|
// MustGet 从 Context 中获取一个值,如果不存在则 panic
|
||||||
// 适用于确定值一定存在的场景
|
// 适用于确定值一定存在的场景
|
||||||
func (c *Context) MustGet(key string) interface{} {
|
func (c *Context) MustGet(key string) any {
|
||||||
if value, exists := c.Get(key); exists {
|
if value, exists := c.Get(key); exists {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +268,7 @@ func (c *Context) Raw(code int, contentType string, data []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// String 向响应写入格式化的字符串
|
// String 向响应写入格式化的字符串
|
||||||
func (c *Context) String(code int, format string, values ...interface{}) {
|
func (c *Context) String(code int, format string, values ...any) {
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
|
c.Writer.Write([]byte(fmt.Sprintf(format, values...)))
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +282,7 @@ func (c *Context) Text(code int, text string) {
|
||||||
|
|
||||||
// JSON 向响应写入 JSON 数据
|
// JSON 向响应写入 JSON 数据
|
||||||
// 设置 Content-Type 为 application/json
|
// 设置 Content-Type 为 application/json
|
||||||
func (c *Context) JSON(code int, obj interface{}) {
|
func (c *Context) JSON(code int, obj any) {
|
||||||
c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
c.Writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
if err := json.MarshalWrite(c.Writer, obj); err != nil {
|
if err := json.MarshalWrite(c.Writer, obj); err != nil {
|
||||||
|
|
@ -295,7 +294,7 @@ func (c *Context) JSON(code int, obj interface{}) {
|
||||||
|
|
||||||
// GOB 向响应写入GOB数据
|
// GOB 向响应写入GOB数据
|
||||||
// 设置 Content-Type 为 application/octet-stream
|
// 设置 Content-Type 为 application/octet-stream
|
||||||
func (c *Context) GOB(code int, obj interface{}) {
|
func (c *Context) GOB(code int, obj any) {
|
||||||
c.Writer.Header().Set("Content-Type", "application/octet-stream") // 设置合适的 Content-Type
|
c.Writer.Header().Set("Content-Type", "application/octet-stream") // 设置合适的 Content-Type
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
// GOB 编码
|
// GOB 编码
|
||||||
|
|
@ -307,11 +306,25 @@ func (c *Context) GOB(code int, obj interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WANF向响应写入WANF数据
|
||||||
|
// 设置 application/vnd.wjqserver.wanf; charset=utf-8
|
||||||
|
func (c *Context) WANF(code int, obj any) {
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/vnd.wjqserver.wanf; charset=utf-8")
|
||||||
|
c.Writer.WriteHeader(code)
|
||||||
|
// WANF 编码
|
||||||
|
encoder := wanf.NewStreamEncoder(c.Writer)
|
||||||
|
if err := encoder.Encode(obj); err != nil {
|
||||||
|
c.AddError(fmt.Errorf("failed to encode WANF: %w", err))
|
||||||
|
c.ErrorUseHandle(http.StatusInternalServerError, fmt.Errorf("failed to encode WANF: %w", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HTML 渲染 HTML 模板
|
// HTML 渲染 HTML 模板
|
||||||
// 如果 Engine 配置了 HTMLRender,则使用它进行渲染
|
// 如果 Engine 配置了 HTMLRender,则使用它进行渲染
|
||||||
// 否则,会进行简单的字符串输出
|
// 否则,会进行简单的字符串输出
|
||||||
// 预留接口,可以扩展为支持多种模板引擎
|
// 预留接口,可以扩展为支持多种模板引擎
|
||||||
func (c *Context) HTML(code int, name string, obj interface{}) {
|
func (c *Context) HTML(code int, name string, obj any) {
|
||||||
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
c.Writer.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
c.Writer.WriteHeader(code)
|
c.Writer.WriteHeader(code)
|
||||||
|
|
||||||
|
|
@ -342,7 +355,7 @@ func (c *Context) Redirect(code int, location string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldBindJSON 尝试将请求体绑定到 JSON 对象
|
// ShouldBindJSON 尝试将请求体绑定到 JSON 对象
|
||||||
func (c *Context) ShouldBindJSON(obj interface{}) error {
|
func (c *Context) ShouldBindJSON(obj any) error {
|
||||||
if c.Request.Body == nil {
|
if c.Request.Body == nil {
|
||||||
return errors.New("request body is empty")
|
return errors.New("request body is empty")
|
||||||
}
|
}
|
||||||
|
|
@ -353,10 +366,28 @@ func (c *Context) ShouldBindJSON(obj interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldBindWANF
|
||||||
|
func (c *Context) ShouldBindWANF(obj any) error {
|
||||||
|
if c.Request.Body == nil {
|
||||||
|
return errors.New("request body is empty")
|
||||||
|
}
|
||||||
|
decoder, err := wanf.NewStreamDecoder(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create WANF decoder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := decoder.Decode(obj); err != nil {
|
||||||
|
return fmt.Errorf("WANF binding error: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: This function is a reserved placeholder for future API extensions
|
||||||
|
// and is not yet implemented. It will either be properly defined or removed in v2.0.0. Do not use.
|
||||||
// ShouldBind 尝试将请求体绑定到各种类型(JSON, Form, XML 等)
|
// ShouldBind 尝试将请求体绑定到各种类型(JSON, Form, XML 等)
|
||||||
// 这是一个复杂的通用绑定接口,通常根据 Content-Type 或其他头部来判断绑定方式
|
// 这是一个复杂的通用绑定接口,通常根据 Content-Type 或其他头部来判断绑定方式
|
||||||
// 预留接口,可根据项目需求进行扩展
|
// 预留接口,可根据项目需求进行扩展
|
||||||
func (c *Context) ShouldBind(obj interface{}) error {
|
func (c *Context) ShouldBind(obj any) error {
|
||||||
// TODO: 完整的通用绑定逻辑
|
// TODO: 完整的通用绑定逻辑
|
||||||
// 可以根据 c.Request.Header.Get("Content-Type") 来判断是 JSON, Form, XML 等
|
// 可以根据 c.Request.Header.Get("Content-Type") 来判断是 JSON, Form, XML 等
|
||||||
// 例如:
|
// 例如:
|
||||||
|
|
@ -409,7 +440,7 @@ func (c *Context) Err() error {
|
||||||
// Value returns the value associated with this context for key, or nil if no
|
// Value returns the value associated with this context for key, or nil if no
|
||||||
// value is associated with key.
|
// value is associated with key.
|
||||||
// 可以用于从 Context 中获取与特定键关联的值,包括 Go 原生 Context 的值和 Touka Context 的 Keys
|
// 可以用于从 Context 中获取与特定键关联的值,包括 Go 原生 Context 的值和 Touka Context 的 Keys
|
||||||
func (c *Context) Value(key interface{}) interface{} {
|
func (c *Context) Value(key any) any {
|
||||||
if keyAsString, ok := key.(string); ok {
|
if keyAsString, ok := key.(string); ok {
|
||||||
if val, exists := c.Get(keyAsString); exists {
|
if val, exists := c.Get(keyAsString); exists {
|
||||||
return val
|
return val
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ package touka
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -19,13 +18,19 @@ var allowedFileServerMethods = map[string]struct{}{
|
||||||
http.MethodHead: {},
|
http.MethodHead: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInputFSisNil = errors.New("input FS is nil")
|
||||||
|
ErrMethodNotAllowed = errors.New("method not allowed")
|
||||||
|
)
|
||||||
|
|
||||||
// FileServer方式, 返回一个HandleFunc, 统一化处理
|
// FileServer方式, 返回一个HandleFunc, 统一化处理
|
||||||
func FileServer(fs http.FileSystem) HandlerFunc {
|
func FileServer(fs http.FileSystem) HandlerFunc {
|
||||||
if fs == nil {
|
if fs == nil {
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
c.ErrorUseHandle(500, errors.New("Input FileSystem is nil"))
|
c.ErrorUseHandle(http.StatusInternalServerError, ErrInputFSisNil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileServerInstance := http.FileServer(fs)
|
fileServerInstance := http.FileServer(fs)
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
FileServerHandleServe(c, fileServerInstance)
|
FileServerHandleServe(c, fileServerInstance)
|
||||||
|
|
@ -37,7 +42,6 @@ func FileServer(fs http.FileSystem) HandlerFunc {
|
||||||
|
|
||||||
func FileServerHandleServe(c *Context, fsHandle http.Handler) {
|
func FileServerHandleServe(c *Context, fsHandle http.Handler) {
|
||||||
if fsHandle == nil {
|
if fsHandle == nil {
|
||||||
ErrInputFSisNil := errors.New("Input FileSystem Handle is nil")
|
|
||||||
c.AddError(ErrInputFSisNil)
|
c.AddError(ErrInputFSisNil)
|
||||||
// 500
|
// 500
|
||||||
c.ErrorUseHandle(http.StatusInternalServerError, ErrInputFSisNil)
|
c.ErrorUseHandle(http.StatusInternalServerError, ErrInputFSisNil)
|
||||||
|
|
@ -59,7 +63,7 @@ func FileServerHandleServe(c *Context, fsHandle http.Handler) {
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
// 否则,返回 405 Method Not Allowed
|
// 否则,返回 405 Method Not Allowed
|
||||||
c.engine.errorHandle.handler(c, http.StatusMethodNotAllowed, fmt.Errorf("Method %s is Not Allowed on FileServer", c.Request.Method))
|
c.engine.errorHandle.handler(c, http.StatusMethodNotAllowed, ErrMethodNotAllowed)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Next()
|
c.Next()
|
||||||
|
|
|
||||||
5
go.mod
5
go.mod
|
|
@ -5,11 +5,12 @@ go 1.24.5
|
||||||
require (
|
require (
|
||||||
github.com/WJQSERVER-STUDIO/go-utils/iox v0.0.2
|
github.com/WJQSERVER-STUDIO/go-utils/iox v0.0.2
|
||||||
github.com/WJQSERVER-STUDIO/httpc v0.8.2
|
github.com/WJQSERVER-STUDIO/httpc v0.8.2
|
||||||
|
github.com/WJQSERVER/wanf v0.0.0-20250810023226-e51d9d0737ee
|
||||||
github.com/fenthope/reco v0.0.4
|
github.com/fenthope/reco v0.0.4
|
||||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2
|
github.com/go-json-experiment/json v0.0.0-20250813233538-9b1f9ea2e11b
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
golang.org/x/net v0.42.0 // indirect
|
golang.org/x/net v0.43.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
10
go.sum
10
go.sum
|
|
@ -2,11 +2,13 @@ github.com/WJQSERVER-STUDIO/go-utils/iox v0.0.2 h1:AiIHXP21LpK7pFfqUlUstgQEWzjbe
|
||||||
github.com/WJQSERVER-STUDIO/go-utils/iox v0.0.2/go.mod h1:mCLqYU32bTmEE6dpj37MKKiZgz70Jh/xyK9vVbq6pok=
|
github.com/WJQSERVER-STUDIO/go-utils/iox v0.0.2/go.mod h1:mCLqYU32bTmEE6dpj37MKKiZgz70Jh/xyK9vVbq6pok=
|
||||||
github.com/WJQSERVER-STUDIO/httpc v0.8.2 h1:PFPLodV0QAfGEP6915J57vIqoKu9cGuuiXG/7C9TNUk=
|
github.com/WJQSERVER-STUDIO/httpc v0.8.2 h1:PFPLodV0QAfGEP6915J57vIqoKu9cGuuiXG/7C9TNUk=
|
||||||
github.com/WJQSERVER-STUDIO/httpc v0.8.2/go.mod h1:8WhHVRO+olDFBSvL5PC/bdMkb6U3vRdPJ4p4pnguV5Y=
|
github.com/WJQSERVER-STUDIO/httpc v0.8.2/go.mod h1:8WhHVRO+olDFBSvL5PC/bdMkb6U3vRdPJ4p4pnguV5Y=
|
||||||
|
github.com/WJQSERVER/wanf v0.0.0-20250810023226-e51d9d0737ee h1:tJ31DNBn6UhWkk8fiikAQWqULODM+yBcGAEar1tzdZc=
|
||||||
|
github.com/WJQSERVER/wanf v0.0.0-20250810023226-e51d9d0737ee/go.mod h1:q2Pyg+G+s1acMWxrbI4CwS/Yk76/BzLREEdZ8iFwUNE=
|
||||||
github.com/fenthope/reco v0.0.4 h1:yo2g3aWwdoMpaZWZX4SdZOW7mCK82RQIU/YI8ZUQThM=
|
github.com/fenthope/reco v0.0.4 h1:yo2g3aWwdoMpaZWZX4SdZOW7mCK82RQIU/YI8ZUQThM=
|
||||||
github.com/fenthope/reco v0.0.4/go.mod h1:eMyS8HpdMVdJ/2WJt6Cvt8P1EH9Igzj5lSJrgc+0jeg=
|
github.com/fenthope/reco v0.0.4/go.mod h1:eMyS8HpdMVdJ/2WJt6Cvt8P1EH9Igzj5lSJrgc+0jeg=
|
||||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2 h1:iizUGZ9pEquQS5jTGkh4AqeeHCMbfbjeb0zMt0aEFzs=
|
github.com/go-json-experiment/json v0.0.0-20250813233538-9b1f9ea2e11b h1:6Q4zRHXS/YLOl9Ng1b1OOOBWMidAQZR3Gel0UKPC/KU=
|
||||||
github.com/go-json-experiment/json v0.0.0-20250725192818-e39067aee2d2/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
github.com/go-json-experiment/json v0.0.0-20250813233538-9b1f9ea2e11b/go.mod h1:TiCD2a1pcmjd7YnhGH0f/zKNcCD06B029pHhzV23c2M=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue