-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathengine.go
More file actions
225 lines (186 loc) · 5.95 KB
/
engine.go
File metadata and controls
225 lines (186 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Package fox provides a lightweight web framework for building web applications.
package fox
import (
"embed"
"io"
"net/http"
"os"
"reflect"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
const (
// DebugMode indicates gin mode is debug.
DebugMode = gin.DebugMode
// ReleaseMode indicates gin mode is release.
ReleaseMode = gin.ReleaseMode
// TestMode indicates gin mode is test.
TestMode = gin.TestMode
)
var foxMode = DebugMode
// SetMode sets gin mode according to input string.
func SetMode(value string) {
gin.SetMode(value)
foxMode = value
}
// Mode returns current fox mode.
func Mode() string {
return foxMode
}
// DefaultWriter is the default io.Writer used by Gin for debug output and
// middleware output like Logger() or Recovery().
// Note that both Logger and Recovery provides custom ways to configure their
// output io.Writer.
// To support coloring in Windows use:
//
// import "github.com/mattn/go-colorable"
// gin.DefaultWriter = colorable.NewColorableStdout()
var DefaultWriter io.Writer = os.Stdout
// DefaultErrorWriter is the default io.Writer used by Gin to debug errors.
var DefaultErrorWriter io.Writer = os.Stderr
// ErrInvalidHandlerType is the error message for invalid handler type.
var ErrInvalidHandlerType = "invalid handler type: %s\n" +
"handler signature: %s\n" +
"Supported handler types:\n" +
"1. func()\n" +
"2. func(ctx *Context) T\n" +
"3. func(ctx *Context) (T, error)\n" +
"4. func(ctx *Context, args S) T\n" +
"5. func(ctx *Context, args S) (T, error)\n" +
"Where:\n" +
"- S can be struct or map type, S will be auto binding from request body\n" +
"- T can be any type, T will be auto render to response body\n" +
"- error can be any type that implements error interface"
// HandlerFunc is a function that can be registered to a route to handle HTTP requests.
// Like http.HandlerFunc, but support auto binding and auto render.
//
// Support handler types:
// 1. func(){}
// 2. func(ctx *Context) T { ... }
// 3. func(ctx *Context) (T, error) { ... }
// 4. func(ctx *Context, args S) T { ... }
// 5. func(ctx *Context, args S) (T, error) { ... }
//
// Where:
// - S can be struct or map type, S will be auto binding from request body
// - T can be any type, T will be auto render to response body
// - error can be any type that implements error interface
type HandlerFunc any
// HandlersChain defines a HandlerFunc slice.
type HandlersChain []HandlerFunc
var Recovery = gin.Recovery
// Last returns the last handler in the chain. i.e. the last handler is the main one.
func (c HandlersChain) Last() HandlerFunc {
if length := len(c); length > 0 {
return c[length-1]
}
return nil
}
type RenderErrorFunc func(ctx *Context, err error)
// Engine for server.
type Engine struct {
*gin.Engine
RouterGroup
// DefaultRenderErrorStatusCode is the default http status code used for automatic rendering
DefaultRenderErrorStatusCode int
RenderErrorFunc RenderErrorFunc
}
func init() {
// Change gin default validator.
binding.Validator = new(DefaultValidator)
}
// New return engine instance.
func New() *Engine {
engine := &Engine{
Engine: gin.New(),
DefaultRenderErrorStatusCode: http.StatusBadRequest,
}
// recommend default use context.Context to store request-scoped values
engine.ContextWithFallback = true
engine.router = &engine.Engine.RouterGroup
engine.engine = engine
return engine
}
// Default return an Engine instance with Logger and Recovery middleware already attached.
func Default() *Engine {
engine := New()
engine.Use(NewXResponseTimer(), Logger(), Recovery())
return engine
}
// Use middleware.
func (engine *Engine) Use(middleware ...HandlerFunc) {
engine.RouterGroup.Use(middleware...)
}
// NotFound adds handlers for NoRoute. It returns a 404 code by default.
func (engine *Engine) NotFound(handlers ...HandlerFunc) {
handlersChain := engine.handleWrapper(handlers...)
engine.Engine.NoRoute(handlersChain...)
}
func (engine *Engine) NoRoute(handlers ...HandlerFunc) {
handlersChain := engine.handleWrapper(handlers...)
engine.Engine.NoRoute(handlersChain...)
}
func (engine *Engine) NoMethod(handlers ...HandlerFunc) {
handlersChain := engine.handleWrapper(handlers...)
engine.Engine.NoMethod(handlersChain...)
}
// CORS config.
func (engine *Engine) CORS(config cors.Config) {
if config.Validate() == nil {
engine.Engine.Use(cors.New(config))
}
}
// RouterConfigFunc engine load router config func.
type RouterConfigFunc func(router *Engine, embedFS ...embed.FS)
// Load router config.
func (engine *Engine) Load(f RouterConfigFunc, fs ...embed.FS) {
f(engine, fs...)
}
// IsValidHandlerFunc checks if the handler matches the HandlerFunc type requirements.
func IsValidHandlerFunc(handler HandlerFunc) bool {
handlerType := reflect.TypeOf(handler)
// Check if it's a function typ
if handlerType.Kind() != reflect.Func {
return false
}
// Check number of parameters
numIn := handlerType.NumIn()
if numIn > 2 {
return false
}
// Check number of return values
numOut := handlerType.NumOut()
if numOut > 2 {
return false
}
// Check if first parameter is *Context
if numIn > 0 {
firstParam := handlerType.In(0)
if firstParam.Kind() != reflect.Ptr || firstParam.Elem().Name() != "Context" {
return false
}
}
// Check if second parameter is struct or map type
if numIn > 1 {
secondParam := handlerType.In(1)
// If it's a pointer type, get the type it points to
if secondParam.Kind() == reflect.Ptr {
secondParam = secondParam.Elem()
}
// Check if it's a struct or map type
if secondParam.Kind() != reflect.Struct && secondParam.Kind() != reflect.Map && secondParam.Kind() != reflect.Interface {
return false
}
}
// Check return value types
// First return value can be any type
if numOut > 1 {
// Second return value must implement error interface
secondReturn := handlerType.Out(1)
if !secondReturn.Implements(reflect.TypeOf((*error)(nil)).Elem()) {
return false
}
}
return true
}