From a2bab9cacf8914add852d9d5ada6ea2eac0ee140 Mon Sep 17 00:00:00 2001 From: lulz1 Date: Fri, 18 Oct 2024 17:21:19 +0800 Subject: [PATCH] first commit --- .idea/dictionaries/lulz1.xml | 7 + framework/container.go | 155 +++++ framework/gin/binding/default_validator.go | 3 +- framework/gin/context.go | 654 +++++++++++---------- framework/gin/deprecated.go | 4 +- framework/gin/gin.go | 7 +- framework/gin/hade_context.go | 27 + framework/gin/hade_request.go | 7 - framework/middleware/cost.go | 12 +- framework/middleware/recovery.go | 9 +- framework/middleware/timeout.go | 12 +- framework/provider.go | 23 + go.mod | 2 +- go.sum | 95 +++ main.go | 7 +- middleware.go | 2 +- provider/demo/contract.go | 1 + provider/demo/provider.go | 30 + provider/demo/service.go | 36 ++ route.go | 2 +- subject_controller.go | 11 +- user_controller.go | 3 +- 22 files changed, 744 insertions(+), 365 deletions(-) create mode 100644 .idea/dictionaries/lulz1.xml create mode 100644 framework/container.go create mode 100644 framework/provider.go create mode 100644 go.sum create mode 100644 provider/demo/contract.go create mode 100644 provider/demo/provider.go create mode 100644 provider/demo/service.go diff --git a/.idea/dictionaries/lulz1.xml b/.idea/dictionaries/lulz1.xml new file mode 100644 index 0000000..c83216d --- /dev/null +++ b/.idea/dictionaries/lulz1.xml @@ -0,0 +1,7 @@ + + + + Hade + + + \ No newline at end of file diff --git a/framework/container.go b/framework/container.go new file mode 100644 index 0000000..91e290e --- /dev/null +++ b/framework/container.go @@ -0,0 +1,155 @@ +package framework + +import ( + "errors" + "fmt" + "sync" +) + +type Container interface { + // Bind 绑定一个服务提供者,如果关键字凭证已经存在,会进行替换操作,返回 error + Bind(provider ServiceProvider) error + + // IsBind 关键字凭证是否已经绑定服务提供者 + IsBind(key string) bool + + // Make 根据关键字凭证获取一个服务, + Make(key string) (interface{}, error) + + // MustMake 根据关键字凭证获取一个服务,如果这个关键字凭证未绑定服务提供者,那么会 panic。 + //所以在使用这个接口的时候请保证服务容器已经为这个关键字凭证绑定了服务提供者。 + MustMake(key string) interface{} + + // MakeNew 根据关键字凭证获取一个服务,只是这个服务并不是单例模式的 + //它是根据服务提供者注册的启动函数和传递的 params 参数实例化出来的 + //这个函数在需要为不同参数启动不同实例的时候非常有用 + MakeNew(key string, params []interface{}) (interface{}, error) +} + +type HadeContainer struct { + // providers 存储注册的服务提供者,key 为字符串凭证 + providers map[string]ServiceProvider + // instance 存储具体的实例,key 为字符串凭证 + instances map[string]interface{} + // lock 用于锁住对容器的变更操作 + lock sync.RWMutex +} + +func NewHadeContainer() *HadeContainer { + return &HadeContainer{ + providers: make(map[string]ServiceProvider), + instances: make(map[string]interface{}), + lock: sync.RWMutex{}, + } +} + +// PrintProviders 输出服务容器中注册的关键字 +func (h *HadeContainer) PrintProviders() []string { + ret := []string{} + for _, provider := range h.providers { + name := provider.Name() + + line := fmt.Sprint(name) + ret = append(ret, line) + } + return ret +} + +func (h *HadeContainer) Bind(provider ServiceProvider) error { + //因为要对容器进行更改,先使用读写锁避免并发操作 + h.lock.Lock() + defer h.lock.Unlock() + + key := provider.Name() + h.providers[key] = provider + + if !provider.IsDefer() { + //如果服务者不需要延迟加载的话, 直接创建服务实例 + if err := provider.Boot(h); err == nil { + return err + } + params := provider.Params(h) + register := provider.Register(h) + instance, err := register(params) + if err != nil { + return err + } + h.instances[key] = instance + } + return nil +} + +func (h *HadeContainer) IsBind(key string) bool { + _, ok := h.instances[key] + return ok +} + +func (h *HadeContainer) Make(key string) (interface{}, error) { + return h.make(key, nil, false) +} + +func (h *HadeContainer) MustMake(key string) interface{} { + instance, err := h.make(key, nil, false) + if err != nil { + panic(err) + } + return instance +} + +func (h *HadeContainer) MakeNew(key string, params []interface{}) (interface{}, error) { + return h.make(key, params, true) +} + +func (h *HadeContainer) make(key string, params []interface{}, forceNew bool) (interface{}, error) { + //因为要对容器进行更改,先使用读写锁避免并发操作 + h.lock.RLock() + defer h.lock.RUnlock() + + provider, err := h.findServiceProvider(key) + if err != nil { + return nil, err + } + + //强制重新实例化 + if forceNew { + return h.newInstance(provider, params) + } + + // 不需要强制重新实例化,如果容器中已经实例化了,那么就直接使用容器中的实例 + if instance, ok := h.instances[key]; ok { + return instance, nil + } + + // 容器中还未实例化,则进行一次实例化 + instance, err := h.newInstance(provider, params) + if err != nil { + return nil, err + } + + h.instances[key] = instance + return instance, nil +} + +func (h *HadeContainer) findServiceProvider(key string) (ServiceProvider, error) { + provider := h.providers[key] + if provider == nil { + return nil, errors.New("no such provider: " + key) + } + return provider, nil +} + +func (h *HadeContainer) newInstance(provider ServiceProvider, params []interface{}) (interface{}, error) { + // force new a + if err := provider.Boot(h); err != nil { + return nil, err + } + if params == nil { + params = provider.Params(h) + } + register := provider.Register(h) + instance, err := register(params) + if err != nil { + return nil, err + } + return instance, nil +} diff --git a/framework/gin/binding/default_validator.go b/framework/gin/binding/default_validator.go index ac43d7c..a301827 100644 --- a/framework/gin/binding/default_validator.go +++ b/framework/gin/binding/default_validator.go @@ -6,11 +6,10 @@ package binding import ( "fmt" + "github.com/go-playground/validator/v10" "reflect" "strings" "sync" - - "github.com/go-playground/validator/v10" ) type defaultValidator struct { diff --git a/framework/gin/context.go b/framework/gin/context.go index 2c51f31..814e017 100644 --- a/framework/gin/context.go +++ b/framework/gin/context.go @@ -6,6 +6,7 @@ package gin import ( "errors" + "github.com/Superdanda/hade/framework" "io" "log" "math" @@ -53,6 +54,9 @@ const abortIndex int8 = math.MaxInt8 >> 1 // Context is the most important part of gin. It allows us to pass variables between middleware, // manage the flow, validate the JSON of a request and render a JSON response for example. type Context struct { + //新增 Hade框架的 容器 + container framework.Container + writermem responseWriter Request *http.Request Writer ResponseWriter @@ -94,47 +98,47 @@ type Context struct { /********** CONTEXT CREATION ********/ /************************************/ -func (c *Context) reset() { - c.Writer = &c.writermem - c.Params = c.Params[:0] - c.handlers = nil - c.index = -1 +func (ctx *Context) reset() { + ctx.Writer = &ctx.writermem + ctx.Params = ctx.Params[:0] + ctx.handlers = nil + ctx.index = -1 - c.fullPath = "" - c.Keys = nil - c.Errors = c.Errors[:0] - c.Accepted = nil - c.queryCache = nil - c.formCache = nil - c.sameSite = 0 - *c.params = (*c.params)[:0] - *c.skippedNodes = (*c.skippedNodes)[:0] + ctx.fullPath = "" + ctx.Keys = nil + ctx.Errors = ctx.Errors[:0] + ctx.Accepted = nil + ctx.queryCache = nil + ctx.formCache = nil + ctx.sameSite = 0 + *ctx.params = (*ctx.params)[:0] + *ctx.skippedNodes = (*ctx.skippedNodes)[:0] } // Copy returns a copy of the current context that can be safely used outside the request's scope. // This has to be used when the context has to be passed to a goroutine. -func (c *Context) Copy() *Context { +func (ctx *Context) Copy() *Context { cp := Context{ - writermem: c.writermem, - Request: c.Request, - engine: c.engine, + writermem: ctx.writermem, + Request: ctx.Request, + engine: ctx.engine, } cp.writermem.ResponseWriter = nil cp.Writer = &cp.writermem cp.index = abortIndex cp.handlers = nil - cp.fullPath = c.fullPath + cp.fullPath = ctx.fullPath - cKeys := c.Keys + cKeys := ctx.Keys cp.Keys = make(map[string]any, len(cKeys)) - c.mu.RLock() + ctx.mu.RLock() for k, v := range cKeys { cp.Keys[k] = v } - c.mu.RUnlock() + ctx.mu.RUnlock() - cParams := c.Params + cParams := ctx.Params cp.Params = make([]Param, len(cParams)) copy(cp.Params, cParams) @@ -143,23 +147,23 @@ func (c *Context) Copy() *Context { // HandlerName returns the main handler's name. For example if the handler is "handleGetUsers()", // this function will return "main.handleGetUsers". -func (c *Context) HandlerName() string { - return nameOfFunction(c.handlers.Last()) +func (ctx *Context) HandlerName() string { + return nameOfFunction(ctx.handlers.Last()) } // HandlerNames returns a list of all registered handlers for this context in descending order, // following the semantics of HandlerName() -func (c *Context) HandlerNames() []string { - hn := make([]string, 0, len(c.handlers)) - for _, val := range c.handlers { +func (ctx *Context) HandlerNames() []string { + hn := make([]string, 0, len(ctx.handlers)) + for _, val := range ctx.handlers { hn = append(hn, nameOfFunction(val)) } return hn } // Handler returns the main handler. -func (c *Context) Handler() HandlerFunc { - return c.handlers.Last() +func (ctx *Context) Handler() HandlerFunc { + return ctx.handlers.Last() } // FullPath returns a matched route full path. For not found routes @@ -168,8 +172,8 @@ func (c *Context) Handler() HandlerFunc { // router.GET("/user/:id", func(c *gin.Context) { // c.FullPath() == "/user/:id" // true // }) -func (c *Context) FullPath() string { - return c.fullPath +func (ctx *Context) FullPath() string { + return ctx.fullPath } /************************************/ @@ -179,49 +183,49 @@ func (c *Context) FullPath() string { // 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++ +func (ctx *Context) Next() { + ctx.index++ + for ctx.index < int8(len(ctx.handlers)) { + ctx.handlers[ctx.index](ctx) + ctx.index++ } } // IsAborted returns true if the current context was aborted. -func (c *Context) IsAborted() bool { - return c.index >= abortIndex +func (ctx *Context) IsAborted() bool { + return ctx.index >= abortIndex } // 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 = abortIndex +func (ctx *Context) Abort() { + ctx.index = abortIndex } // AbortWithStatus calls `Abort()` and writes the headers with the specified status code. // For example, a failed attempt to authenticate a request could use: context.AbortWithStatus(401). -func (c *Context) AbortWithStatus(code int) { - c.Status(code) - c.Writer.WriteHeaderNow() - c.Abort() +func (ctx *Context) AbortWithStatus(code int) { + ctx.Status(code) + ctx.Writer.WriteHeaderNow() + ctx.Abort() } // AbortWithStatusJSON calls `Abort()` and then `JSON` internally. // This method stops the chain, writes the status code and return a JSON body. // It also sets the Content-Type as "application/json". -func (c *Context) AbortWithStatusJSON(code int, jsonObj any) { - c.Abort() - c.JSON(code, jsonObj) +func (ctx *Context) AbortWithStatusJSON(code int, jsonObj any) { + ctx.Abort() + ctx.JSON(code, jsonObj) } // AbortWithError calls `AbortWithStatus()` and `Error()` internally. // This method stops the chain, writes the status code and pushes the specified error to `c.Errors`. // See Context.Error() for more details. -func (c *Context) AbortWithError(code int, err error) *Error { - c.AbortWithStatus(code) - return c.Error(err) +func (ctx *Context) AbortWithError(code int, err error) *Error { + ctx.AbortWithStatus(code) + return ctx.Error(err) } /************************************/ @@ -233,7 +237,7 @@ func (c *Context) AbortWithError(code int, err error) *Error { // A middleware can be used to collect all the errors and push them to a database together, // print a log, or append it in the HTTP response. // Error will panic if err is nil. -func (c *Context) Error(err error) *Error { +func (ctx *Context) Error(err error) *Error { if err == nil { panic("err is nil") } @@ -247,7 +251,7 @@ func (c *Context) Error(err error) *Error { } } - c.Errors = append(c.Errors, parsedError) + ctx.Errors = append(ctx.Errors, parsedError) return parsedError } @@ -257,132 +261,132 @@ func (c *Context) Error(err error) *Error { // Set is used to store a new key/value pair exclusively for this context. // It also lazy initializes c.Keys if it was not used previously. -func (c *Context) Set(key string, value any) { - c.mu.Lock() - defer c.mu.Unlock() - if c.Keys == nil { - c.Keys = make(map[string]any) +func (ctx *Context) Set(key string, value any) { + ctx.mu.Lock() + defer ctx.mu.Unlock() + if ctx.Keys == nil { + ctx.Keys = make(map[string]any) } - c.Keys[key] = value + ctx.Keys[key] = value } // Get returns the value for the given key, ie: (value, true). // If the value does not exist it returns (nil, false) -func (c *Context) Get(key string) (value any, exists bool) { - c.mu.RLock() - defer c.mu.RUnlock() - value, exists = c.Keys[key] +func (ctx *Context) Get(key string) (value any, exists bool) { + ctx.mu.RLock() + defer ctx.mu.RUnlock() + value, exists = ctx.Keys[key] return } // MustGet returns the value for the given key if it exists, otherwise it panics. -func (c *Context) MustGet(key string) any { - if value, exists := c.Get(key); exists { +func (ctx *Context) MustGet(key string) any { + if value, exists := ctx.Get(key); exists { return value } panic("Key \"" + key + "\" does not exist") } // GetString returns the value associated with the key as a string. -func (c *Context) GetString(key string) (s string) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetString(key string) (s string) { + if val, ok := ctx.Get(key); ok && val != nil { s, _ = val.(string) } return } // GetBool returns the value associated with the key as a boolean. -func (c *Context) GetBool(key string) (b bool) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetBool(key string) (b bool) { + if val, ok := ctx.Get(key); ok && val != nil { b, _ = val.(bool) } return } // GetInt returns the value associated with the key as an integer. -func (c *Context) GetInt(key string) (i int) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetInt(key string) (i int) { + if val, ok := ctx.Get(key); ok && val != nil { i, _ = val.(int) } return } // GetInt64 returns the value associated with the key as an integer. -func (c *Context) GetInt64(key string) (i64 int64) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetInt64(key string) (i64 int64) { + if val, ok := ctx.Get(key); ok && val != nil { i64, _ = val.(int64) } return } // GetUint returns the value associated with the key as an unsigned integer. -func (c *Context) GetUint(key string) (ui uint) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetUint(key string) (ui uint) { + if val, ok := ctx.Get(key); ok && val != nil { ui, _ = val.(uint) } return } // GetUint64 returns the value associated with the key as an unsigned integer. -func (c *Context) GetUint64(key string) (ui64 uint64) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetUint64(key string) (ui64 uint64) { + if val, ok := ctx.Get(key); ok && val != nil { ui64, _ = val.(uint64) } return } // GetFloat64 returns the value associated with the key as a float64. -func (c *Context) GetFloat64(key string) (f64 float64) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetFloat64(key string) (f64 float64) { + if val, ok := ctx.Get(key); ok && val != nil { f64, _ = val.(float64) } return } // GetTime returns the value associated with the key as time. -func (c *Context) GetTime(key string) (t time.Time) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetTime(key string) (t time.Time) { + if val, ok := ctx.Get(key); ok && val != nil { t, _ = val.(time.Time) } return } // GetDuration returns the value associated with the key as a duration. -func (c *Context) GetDuration(key string) (d time.Duration) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetDuration(key string) (d time.Duration) { + if val, ok := ctx.Get(key); ok && val != nil { d, _ = val.(time.Duration) } return } // GetStringSlice returns the value associated with the key as a slice of strings. -func (c *Context) GetStringSlice(key string) (ss []string) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetStringSlice(key string) (ss []string) { + if val, ok := ctx.Get(key); ok && val != nil { ss, _ = val.([]string) } return } // GetStringMap returns the value associated with the key as a map of interfaces. -func (c *Context) GetStringMap(key string) (sm map[string]any) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetStringMap(key string) (sm map[string]any) { + if val, ok := ctx.Get(key); ok && val != nil { sm, _ = val.(map[string]any) } return } // GetStringMapString returns the value associated with the key as a map of strings. -func (c *Context) GetStringMapString(key string) (sms map[string]string) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetStringMapString(key string) (sms map[string]string) { + if val, ok := ctx.Get(key); ok && val != nil { sms, _ = val.(map[string]string) } return } // GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. -func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) { - if val, ok := c.Get(key); ok && val != nil { +func (ctx *Context) GetStringMapStringSlice(key string) (smss map[string][]string) { + if val, ok := ctx.Get(key); ok && val != nil { smss, _ = val.(map[string][]string) } return @@ -401,8 +405,8 @@ func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) // // a GET request to /user/john/ // id := c.Param("id") // id == "/john/" // }) -func (c *Context) Param(key string) string { - return c.Params.ByName(key) +func (ctx *Context) Param(key string) string { + return ctx.Params.ByName(key) } // AddParam adds param to context and @@ -410,8 +414,8 @@ func (c *Context) Param(key string) string { // Example Route: "/user/:id" // AddParam("id", 1) // Result: "/user/1" -func (c *Context) AddParam(key, value string) { - c.Params = append(c.Params, Param{Key: key, Value: value}) +func (ctx *Context) AddParam(key, value string) { + ctx.Params = append(ctx.Params, Param{Key: key, Value: value}) } // Query returns the keyed url query value if it exists, @@ -423,8 +427,8 @@ func (c *Context) AddParam(key, value string) { // c.Query("name") == "Manu" // c.Query("value") == "" // c.Query("wtf") == "" -func (c *Context) Query(key string) (value string) { - value, _ = c.GetQuery(key) +func (ctx *Context) Query(key string) (value string) { + value, _ = ctx.GetQuery(key) return } @@ -436,8 +440,8 @@ func (c *Context) Query(key string) (value string) { // c.DefaultQuery("name", "unknown") == "Manu" // c.DefaultQuery("id", "none") == "none" // c.DefaultQuery("lastname", "none") == "" -func (c *Context) DefaultQuery(key, defaultValue string) string { - if value, ok := c.GetQuery(key); ok { +func (ctx *Context) DefaultQuery(key, defaultValue string) string { + if value, ok := ctx.GetQuery(key); ok { return value } return defaultValue @@ -452,8 +456,8 @@ func (c *Context) DefaultQuery(key, defaultValue string) string { // ("Manu", true) == c.GetQuery("name") // ("", false) == c.GetQuery("id") // ("", true) == c.GetQuery("lastname") -func (c *Context) GetQuery(key string) (string, bool) { - if values, ok := c.GetQueryArray(key); ok { +func (ctx *Context) GetQuery(key string) (string, bool) { + if values, ok := ctx.GetQueryArray(key); ok { return values[0], ok } return "", false @@ -461,54 +465,54 @@ func (c *Context) GetQuery(key string) (string, bool) { // QueryArray returns a slice of strings for a given query key. // The length of the slice depends on the number of params with the given key. -func (c *Context) QueryArray(key string) (values []string) { - values, _ = c.GetQueryArray(key) +func (ctx *Context) QueryArray(key string) (values []string) { + values, _ = ctx.GetQueryArray(key) return } -func (c *Context) initQueryCache() { - if c.queryCache == nil { - if c.Request != nil { - c.queryCache = c.Request.URL.Query() +func (ctx *Context) initQueryCache() { + if ctx.queryCache == nil { + if ctx.Request != nil { + ctx.queryCache = ctx.Request.URL.Query() } else { - c.queryCache = url.Values{} + ctx.queryCache = url.Values{} } } } // GetQueryArray returns a slice of strings for a given query key, plus // a boolean value whether at least one value exists for the given key. -func (c *Context) GetQueryArray(key string) (values []string, ok bool) { - c.initQueryCache() - values, ok = c.queryCache[key] +func (ctx *Context) GetQueryArray(key string) (values []string, ok bool) { + ctx.initQueryCache() + values, ok = ctx.queryCache[key] return } // QueryMap returns a map for a given query key. -func (c *Context) QueryMap(key string) (dicts map[string]string) { - dicts, _ = c.GetQueryMap(key) +func (ctx *Context) QueryMap(key string) (dicts map[string]string) { + dicts, _ = ctx.GetQueryMap(key) return } // GetQueryMap returns a map for a given query key, plus a boolean value // whether at least one value exists for the given key. -func (c *Context) GetQueryMap(key string) (map[string]string, bool) { - c.initQueryCache() - return c.get(c.queryCache, key) +func (ctx *Context) GetQueryMap(key string) (map[string]string, bool) { + ctx.initQueryCache() + return ctx.get(ctx.queryCache, key) } // PostForm returns the specified key from a POST urlencoded form or multipart form // when it exists, otherwise it returns an empty string `("")`. -func (c *Context) PostForm(key string) (value string) { - value, _ = c.GetPostForm(key) +func (ctx *Context) PostForm(key string) (value string) { + value, _ = ctx.GetPostForm(key) return } // DefaultPostForm returns the specified key from a POST urlencoded form or multipart form // when it exists, otherwise it returns the specified defaultValue string. // See: PostForm() and GetPostForm() for further information. -func (c *Context) DefaultPostForm(key, defaultValue string) string { - if value, ok := c.GetPostForm(key); ok { +func (ctx *Context) DefaultPostForm(key, defaultValue string) string { + if value, ok := ctx.GetPostForm(key); ok { return value } return defaultValue @@ -522,8 +526,8 @@ func (c *Context) DefaultPostForm(key, defaultValue string) string { // email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com" // email= --> ("", true) := GetPostForm("email") // set email to "" // --> ("", false) := GetPostForm("email") // do nothing with email -func (c *Context) GetPostForm(key string) (string, bool) { - if values, ok := c.GetPostFormArray(key); ok { +func (ctx *Context) GetPostForm(key string) (string, bool) { + if values, ok := ctx.GetPostFormArray(key); ok { return values[0], ok } return "", false @@ -531,47 +535,47 @@ func (c *Context) GetPostForm(key string) (string, bool) { // PostFormArray returns a slice of strings for a given form key. // The length of the slice depends on the number of params with the given key. -func (c *Context) PostFormArray(key string) (values []string) { - values, _ = c.GetPostFormArray(key) +func (ctx *Context) PostFormArray(key string) (values []string) { + values, _ = ctx.GetPostFormArray(key) return } -func (c *Context) initFormCache() { - if c.formCache == nil { - c.formCache = make(url.Values) - req := c.Request - if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { +func (ctx *Context) initFormCache() { + if ctx.formCache == nil { + ctx.formCache = make(url.Values) + req := ctx.Request + if err := req.ParseMultipartForm(ctx.engine.MaxMultipartMemory); err != nil { if !errors.Is(err, http.ErrNotMultipart) { debugPrint("error on parse multipart form array: %v", err) } } - c.formCache = req.PostForm + ctx.formCache = req.PostForm } } // GetPostFormArray returns a slice of strings for a given form key, plus // a boolean value whether at least one value exists for the given key. -func (c *Context) GetPostFormArray(key string) (values []string, ok bool) { - c.initFormCache() - values, ok = c.formCache[key] +func (ctx *Context) GetPostFormArray(key string) (values []string, ok bool) { + ctx.initFormCache() + values, ok = ctx.formCache[key] return } // PostFormMap returns a map for a given form key. -func (c *Context) PostFormMap(key string) (dicts map[string]string) { - dicts, _ = c.GetPostFormMap(key) +func (ctx *Context) PostFormMap(key string) (dicts map[string]string) { + dicts, _ = ctx.GetPostFormMap(key) return } // GetPostFormMap returns a map for a given form key, plus a boolean value // whether at least one value exists for the given key. -func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { - c.initFormCache() - return c.get(c.formCache, key) +func (ctx *Context) GetPostFormMap(key string) (map[string]string, bool) { + ctx.initFormCache() + return ctx.get(ctx.formCache, key) } // get is an internal method and returns a map which satisfies conditions. -func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) { +func (ctx *Context) get(m map[string][]string, key string) (map[string]string, bool) { dicts := make(map[string]string) exist := false for k, v := range m { @@ -586,13 +590,13 @@ func (c *Context) get(m map[string][]string, key string) (map[string]string, boo } // FormFile returns the first file for the provided form key. -func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { - if c.Request.MultipartForm == nil { - if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { +func (ctx *Context) FormFile(name string) (*multipart.FileHeader, error) { + if ctx.Request.MultipartForm == nil { + if err := ctx.Request.ParseMultipartForm(ctx.engine.MaxMultipartMemory); err != nil { return nil, err } } - f, fh, err := c.Request.FormFile(name) + f, fh, err := ctx.Request.FormFile(name) if err != nil { return nil, err } @@ -601,13 +605,13 @@ func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { } // MultipartForm is the parsed multipart form, including file uploads. -func (c *Context) MultipartForm() (*multipart.Form, error) { - err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory) - return c.Request.MultipartForm, err +func (ctx *Context) MultipartForm() (*multipart.Form, error) { + err := ctx.Request.ParseMultipartForm(ctx.engine.MaxMultipartMemory) + return ctx.Request.MultipartForm, err } // SaveUploadedFile uploads the form file to specific dst. -func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { +func (ctx *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { src, err := file.Open() if err != nil { return err @@ -637,46 +641,46 @@ func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. -func (c *Context) Bind(obj any) error { - b := binding.Default(c.Request.Method, c.ContentType()) - return c.MustBindWith(obj, b) +func (ctx *Context) Bind(obj any) error { + b := binding.Default(ctx.Request.Method, ctx.ContentType()) + return ctx.MustBindWith(obj, b) } // BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON). -func (c *Context) BindJSON(obj any) error { - return c.MustBindWith(obj, binding.JSON) +func (ctx *Context) BindJSON(obj any) error { + return ctx.MustBindWith(obj, binding.JSON) } // BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML). -func (c *Context) BindXML(obj any) error { - return c.MustBindWith(obj, binding.XML) +func (ctx *Context) BindXML(obj any) error { + return ctx.MustBindWith(obj, binding.XML) } // BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). -func (c *Context) BindQuery(obj any) error { - return c.MustBindWith(obj, binding.Query) +func (ctx *Context) BindQuery(obj any) error { + return ctx.MustBindWith(obj, binding.Query) } // BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML). -func (c *Context) BindYAML(obj any) error { - return c.MustBindWith(obj, binding.YAML) +func (ctx *Context) BindYAML(obj any) error { + return ctx.MustBindWith(obj, binding.YAML) } // BindTOML is a shortcut for c.MustBindWith(obj, binding.TOML). -func (c *Context) BindTOML(obj any) error { - return c.MustBindWith(obj, binding.TOML) +func (ctx *Context) BindTOML(obj any) error { + return ctx.MustBindWith(obj, binding.TOML) } // BindHeader is a shortcut for c.MustBindWith(obj, binding.Header). -func (c *Context) BindHeader(obj any) error { - return c.MustBindWith(obj, binding.Header) +func (ctx *Context) BindHeader(obj any) error { + return ctx.MustBindWith(obj, binding.Header) } // BindUri binds the passed struct pointer using binding.Uri. // It will abort the request with HTTP 400 if any error occurs. -func (c *Context) BindUri(obj any) error { - if err := c.ShouldBindUri(obj); err != nil { - c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck +func (ctx *Context) BindUri(obj any) error { + if err := ctx.ShouldBindUri(obj); err != nil { + ctx.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck return err } return nil @@ -685,9 +689,9 @@ func (c *Context) BindUri(obj any) error { // MustBindWith binds the passed struct pointer using the specified binding engine. // It will abort the request with HTTP 400 if any error occurs. // See the binding package. -func (c *Context) MustBindWith(obj any, b binding.Binding) error { - if err := c.ShouldBindWith(obj, b); err != nil { - c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck +func (ctx *Context) MustBindWith(obj any, b binding.Binding) error { + if err := ctx.ShouldBindWith(obj, b); err != nil { + ctx.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck return err } return nil @@ -702,45 +706,45 @@ func (c *Context) MustBindWith(obj any, b binding.Binding) error { // It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. // It decodes the json payload into the struct specified as a pointer. // Like c.Bind() but this method does not set the response status code to 400 or abort if input is not valid. -func (c *Context) ShouldBind(obj any) error { - b := binding.Default(c.Request.Method, c.ContentType()) - return c.ShouldBindWith(obj, b) +func (ctx *Context) ShouldBind(obj any) error { + b := binding.Default(ctx.Request.Method, ctx.ContentType()) + return ctx.ShouldBindWith(obj, b) } // ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON). -func (c *Context) ShouldBindJSON(obj any) error { - return c.ShouldBindWith(obj, binding.JSON) +func (ctx *Context) ShouldBindJSON(obj any) error { + return ctx.ShouldBindWith(obj, binding.JSON) } // ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML). -func (c *Context) ShouldBindXML(obj any) error { - return c.ShouldBindWith(obj, binding.XML) +func (ctx *Context) ShouldBindXML(obj any) error { + return ctx.ShouldBindWith(obj, binding.XML) } // ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). -func (c *Context) ShouldBindQuery(obj any) error { - return c.ShouldBindWith(obj, binding.Query) +func (ctx *Context) ShouldBindQuery(obj any) error { + return ctx.ShouldBindWith(obj, binding.Query) } // ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML). -func (c *Context) ShouldBindYAML(obj any) error { - return c.ShouldBindWith(obj, binding.YAML) +func (ctx *Context) ShouldBindYAML(obj any) error { + return ctx.ShouldBindWith(obj, binding.YAML) } // ShouldBindTOML is a shortcut for c.ShouldBindWith(obj, binding.TOML). -func (c *Context) ShouldBindTOML(obj any) error { - return c.ShouldBindWith(obj, binding.TOML) +func (ctx *Context) ShouldBindTOML(obj any) error { + return ctx.ShouldBindWith(obj, binding.TOML) } // ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header). -func (c *Context) ShouldBindHeader(obj any) error { - return c.ShouldBindWith(obj, binding.Header) +func (ctx *Context) ShouldBindHeader(obj any) error { + return ctx.ShouldBindWith(obj, binding.Header) } // ShouldBindUri binds the passed struct pointer using the specified binding engine. -func (c *Context) ShouldBindUri(obj any) error { - m := make(map[string][]string, len(c.Params)) - for _, v := range c.Params { +func (ctx *Context) ShouldBindUri(obj any) error { + m := make(map[string][]string, len(ctx.Params)) + for _, v := range ctx.Params { m[v.Key] = []string{v.Value} } return binding.Uri.BindUri(m, obj) @@ -748,8 +752,8 @@ func (c *Context) ShouldBindUri(obj any) error { // ShouldBindWith binds the passed struct pointer using the specified binding engine. // See the binding package. -func (c *Context) ShouldBindWith(obj any, b binding.Binding) error { - return b.Bind(c.Request, obj) +func (ctx *Context) ShouldBindWith(obj any, b binding.Binding) error { + return b.Bind(ctx.Request, obj) } // ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request @@ -757,41 +761,41 @@ func (c *Context) ShouldBindWith(obj any, b binding.Binding) error { // // NOTE: This method reads the body before binding. So you should use // ShouldBindWith for better performance if you need to call only once. -func (c *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) { +func (ctx *Context) ShouldBindBodyWith(obj any, bb binding.BindingBody) (err error) { var body []byte - if cb, ok := c.Get(BodyBytesKey); ok { + if cb, ok := ctx.Get(BodyBytesKey); ok { if cbb, ok := cb.([]byte); ok { body = cbb } } if body == nil { - body, err = io.ReadAll(c.Request.Body) + body, err = io.ReadAll(ctx.Request.Body) if err != nil { return err } - c.Set(BodyBytesKey, body) + ctx.Set(BodyBytesKey, body) } return bb.BindBody(body, obj) } // ShouldBindBodyWithJSON is a shortcut for c.ShouldBindBodyWith(obj, binding.JSON). -func (c *Context) ShouldBindBodyWithJSON(obj any) error { - return c.ShouldBindBodyWith(obj, binding.JSON) +func (ctx *Context) ShouldBindBodyWithJSON(obj any) error { + return ctx.ShouldBindBodyWith(obj, binding.JSON) } // ShouldBindBodyWithXML is a shortcut for c.ShouldBindBodyWith(obj, binding.XML). -func (c *Context) ShouldBindBodyWithXML(obj any) error { - return c.ShouldBindBodyWith(obj, binding.XML) +func (ctx *Context) ShouldBindBodyWithXML(obj any) error { + return ctx.ShouldBindBodyWith(obj, binding.XML) } // ShouldBindBodyWithYAML is a shortcut for c.ShouldBindBodyWith(obj, binding.YAML). -func (c *Context) ShouldBindBodyWithYAML(obj any) error { - return c.ShouldBindBodyWith(obj, binding.YAML) +func (ctx *Context) ShouldBindBodyWithYAML(obj any) error { + return ctx.ShouldBindBodyWith(obj, binding.YAML) } // ShouldBindBodyWithTOML is a shortcut for c.ShouldBindBodyWith(obj, binding.TOML). -func (c *Context) ShouldBindBodyWithTOML(obj any) error { - return c.ShouldBindBodyWith(obj, binding.TOML) +func (ctx *Context) ShouldBindBodyWithTOML(obj any) error { + return ctx.ShouldBindBodyWith(obj, binding.TOML) } // ClientIP implements one best effort algorithm to return the real client IP. @@ -799,19 +803,19 @@ func (c *Context) ShouldBindBodyWithTOML(obj any) error { // If it is it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]). // If the headers are not syntactically valid OR the remote IP does not correspond to a trusted proxy, // the remote IP (coming from Request.RemoteAddr) is returned. -func (c *Context) ClientIP() string { +func (ctx *Context) ClientIP() string { // Check if we're running on a trusted platform, continue running backwards if error - if c.engine.TrustedPlatform != "" { + if ctx.engine.TrustedPlatform != "" { // Developers can define their own header of Trusted Platform or use predefined constants - if addr := c.requestHeader(c.engine.TrustedPlatform); addr != "" { + if addr := ctx.requestHeader(ctx.engine.TrustedPlatform); addr != "" { return addr } } // Legacy "AppEngine" flag - if c.engine.AppEngine { + if ctx.engine.AppEngine { log.Println(`The AppEngine flag is going to be deprecated. Please check issues #2723 and #2739 and use 'TrustedPlatform: gin.PlatformGoogleAppEngine' instead.`) - if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { + if addr := ctx.requestHeader("X-Appengine-Remote-Addr"); addr != "" { return addr } } @@ -819,15 +823,15 @@ func (c *Context) ClientIP() string { // It also checks if the remoteIP is a trusted proxy or not. // In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks // defined by Engine.SetTrustedProxies() - remoteIP := net.ParseIP(c.RemoteIP()) + remoteIP := net.ParseIP(ctx.RemoteIP()) if remoteIP == nil { return "" } - trusted := c.engine.isTrustedProxy(remoteIP) + trusted := ctx.engine.isTrustedProxy(remoteIP) - if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { - for _, headerName := range c.engine.RemoteIPHeaders { - ip, valid := c.engine.validateHeader(c.requestHeader(headerName)) + if trusted && ctx.engine.ForwardedByClientIP && ctx.engine.RemoteIPHeaders != nil { + for _, headerName := range ctx.engine.RemoteIPHeaders { + ip, valid := ctx.engine.validateHeader(ctx.requestHeader(headerName)) if valid { return ip } @@ -837,8 +841,8 @@ func (c *Context) ClientIP() string { } // RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port). -func (c *Context) RemoteIP() string { - ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) +func (ctx *Context) RemoteIP() string { + ip, _, err := net.SplitHostPort(strings.TrimSpace(ctx.Request.RemoteAddr)) if err != nil { return "" } @@ -846,22 +850,22 @@ func (c *Context) RemoteIP() string { } // ContentType returns the Content-Type header of the request. -func (c *Context) ContentType() string { - return filterFlags(c.requestHeader("Content-Type")) +func (ctx *Context) ContentType() string { + return filterFlags(ctx.requestHeader("Content-Type")) } // IsWebsocket returns true if the request headers indicate that a websocket // handshake is being initiated by the client. -func (c *Context) IsWebsocket() bool { - if strings.Contains(strings.ToLower(c.requestHeader("Connection")), "upgrade") && - strings.EqualFold(c.requestHeader("Upgrade"), "websocket") { +func (ctx *Context) IsWebsocket() bool { + if strings.Contains(strings.ToLower(ctx.requestHeader("Connection")), "upgrade") && + strings.EqualFold(ctx.requestHeader("Upgrade"), "websocket") { return true } return false } -func (c *Context) requestHeader(key string) string { - return c.Request.Header.Get(key) +func (ctx *Context) requestHeader(key string) string { + return ctx.Request.Header.Get(key) } /************************************/ @@ -882,53 +886,53 @@ func bodyAllowedForStatus(status int) bool { } // Status sets the HTTP response code. -func (c *Context) Status(code int) { - c.Writer.WriteHeader(code) +func (ctx *Context) Status(code int) { + ctx.Writer.WriteHeader(code) } // Header is an intelligent shortcut for c.Writer.Header().Set(key, value). // It writes a header in the response. // If value == "", this method removes the header `c.Writer.Header().Del(key)` -func (c *Context) Header(key, value string) { +func (ctx *Context) Header(key, value string) { if value == "" { - c.Writer.Header().Del(key) + ctx.Writer.Header().Del(key) return } - c.Writer.Header().Set(key, value) + ctx.Writer.Header().Set(key, value) } // GetHeader returns value from request headers. -func (c *Context) GetHeader(key string) string { - return c.requestHeader(key) +func (ctx *Context) GetHeader(key string) string { + return ctx.requestHeader(key) } // GetRawData returns stream data. -func (c *Context) GetRawData() ([]byte, error) { - if c.Request.Body == nil { +func (ctx *Context) GetRawData() ([]byte, error) { + if ctx.Request.Body == nil { return nil, errors.New("cannot read nil body") } - return io.ReadAll(c.Request.Body) + return io.ReadAll(ctx.Request.Body) } // SetSameSite with cookie -func (c *Context) SetSameSite(samesite http.SameSite) { - c.sameSite = samesite +func (ctx *Context) SetSameSite(samesite http.SameSite) { + ctx.sameSite = samesite } // SetCookie adds a Set-Cookie header to the ResponseWriter's headers. // The provided cookie must have a valid Name. Invalid cookies may be // silently dropped. -func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { +func (ctx *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { if path == "" { path = "/" } - http.SetCookie(c.Writer, &http.Cookie{ + http.SetCookie(ctx.Writer, &http.Cookie{ Name: name, Value: url.QueryEscape(value), MaxAge: maxAge, Path: path, Domain: domain, - SameSite: c.sameSite, + SameSite: ctx.sameSite, Secure: secure, HttpOnly: httpOnly, }) @@ -938,8 +942,8 @@ func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, // ErrNoCookie if not found. And return the named cookie is unescaped. // If multiple cookies match the given name, only one cookie will // be returned. -func (c *Context) Cookie(name string) (string, error) { - cookie, err := c.Request.Cookie(name) +func (ctx *Context) Cookie(name string) (string, error) { + cookie, err := ctx.Request.Cookie(name) if err != nil { return "", err } @@ -948,121 +952,121 @@ func (c *Context) Cookie(name string) (string, error) { } // Render writes the response headers and calls render.Render to render data. -func (c *Context) Render(code int, r render.Render) { - c.Status(code) +func (ctx *Context) Render(code int, r render.Render) { + ctx.Status(code) if !bodyAllowedForStatus(code) { - r.WriteContentType(c.Writer) - c.Writer.WriteHeaderNow() + r.WriteContentType(ctx.Writer) + ctx.Writer.WriteHeaderNow() return } - if err := r.Render(c.Writer); err != nil { + if err := r.Render(ctx.Writer); err != nil { // Pushing error to c.Errors - _ = c.Error(err) - c.Abort() + _ = ctx.Error(err) + ctx.Abort() } } // HTML renders the HTTP template specified by its file name. // It also updates the HTTP code and sets the Content-Type as "text/html". // See http://golang.org/doc/articles/wiki/ -func (c *Context) HTML(code int, name string, obj any) { - instance := c.engine.HTMLRender.Instance(name, obj) - c.Render(code, instance) +func (ctx *Context) HTML(code int, name string, obj any) { + instance := ctx.engine.HTMLRender.Instance(name, obj) + ctx.Render(code, instance) } // IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. // It also sets the Content-Type as "application/json". // WARNING: we recommend using this only for development purposes since printing pretty JSON is // more CPU and bandwidth consuming. Use Context.JSON() instead. -func (c *Context) IndentedJSON(code int, obj any) { - c.Render(code, render.IndentedJSON{Data: obj}) +func (ctx *Context) IndentedJSON(code int, obj any) { + ctx.Render(code, render.IndentedJSON{Data: obj}) } // SecureJSON serializes the given struct as Secure JSON into the response body. // Default prepends "while(1)," to response body if the given struct is array values. // It also sets the Content-Type as "application/json". -func (c *Context) SecureJSON(code int, obj any) { - c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj}) +func (ctx *Context) SecureJSON(code int, obj any) { + ctx.Render(code, render.SecureJSON{Prefix: ctx.engine.secureJSONPrefix, Data: obj}) } // JSONP serializes the given struct as JSON into the response body. // It adds padding to response body to request data from a server residing in a different domain than the client. // It also sets the Content-Type as "application/javascript". -func (c *Context) JSONP(code int, obj any) { - callback := c.DefaultQuery("callback", "") +func (ctx *Context) JSONP(code int, obj any) { + callback := ctx.DefaultQuery("callback", "") if callback == "" { - c.Render(code, render.JSON{Data: obj}) + ctx.Render(code, render.JSON{Data: obj}) return } - c.Render(code, render.JsonpJSON{Callback: callback, Data: obj}) + ctx.Render(code, render.JsonpJSON{Callback: callback, Data: obj}) } // JSON serializes the given struct as JSON into the response body. // It also sets the Content-Type as "application/json". -func (c *Context) JSON(code int, obj any) { - c.Render(code, render.JSON{Data: obj}) +func (ctx *Context) JSON(code int, obj any) { + ctx.Render(code, render.JSON{Data: obj}) } // AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. // It also sets the Content-Type as "application/json". -func (c *Context) AsciiJSON(code int, obj any) { - c.Render(code, render.AsciiJSON{Data: obj}) +func (ctx *Context) AsciiJSON(code int, obj any) { + ctx.Render(code, render.AsciiJSON{Data: obj}) } // PureJSON serializes the given struct as JSON into the response body. // PureJSON, unlike JSON, does not replace special html characters with their unicode entities. -func (c *Context) PureJSON(code int, obj any) { - c.Render(code, render.PureJSON{Data: obj}) +func (ctx *Context) PureJSON(code int, obj any) { + ctx.Render(code, render.PureJSON{Data: obj}) } // XML serializes the given struct as XML into the response body. // It also sets the Content-Type as "application/xml". -func (c *Context) XML(code int, obj any) { - c.Render(code, render.XML{Data: obj}) +func (ctx *Context) XML(code int, obj any) { + ctx.Render(code, render.XML{Data: obj}) } // YAML serializes the given struct as YAML into the response body. -func (c *Context) YAML(code int, obj any) { - c.Render(code, render.YAML{Data: obj}) +func (ctx *Context) YAML(code int, obj any) { + ctx.Render(code, render.YAML{Data: obj}) } // TOML serializes the given struct as TOML into the response body. -func (c *Context) TOML(code int, obj any) { - c.Render(code, render.TOML{Data: obj}) +func (ctx *Context) TOML(code int, obj any) { + ctx.Render(code, render.TOML{Data: obj}) } // ProtoBuf serializes the given struct as ProtoBuf into the response body. -func (c *Context) ProtoBuf(code int, obj any) { - c.Render(code, render.ProtoBuf{Data: obj}) +func (ctx *Context) ProtoBuf(code int, obj any) { + ctx.Render(code, render.ProtoBuf{Data: obj}) } // String writes the given string into the response body. -func (c *Context) String(code int, format string, values ...any) { - c.Render(code, render.String{Format: format, Data: values}) +func (ctx *Context) String(code int, format string, values ...any) { + ctx.Render(code, render.String{Format: format, Data: values}) } // Redirect returns an HTTP redirect to the specific location. -func (c *Context) Redirect(code int, location string) { - c.Render(-1, render.Redirect{ +func (ctx *Context) Redirect(code int, location string) { + ctx.Render(-1, render.Redirect{ Code: code, Location: location, - Request: c.Request, + Request: ctx.Request, }) } // Data writes some data into the body stream and updates the HTTP code. -func (c *Context) Data(code int, contentType string, data []byte) { - c.Render(code, render.Data{ +func (ctx *Context) Data(code int, contentType string, data []byte) { + ctx.Render(code, render.Data{ ContentType: contentType, Data: data, }) } // DataFromReader writes the specified reader into the body stream and updates the HTTP code. -func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) { - c.Render(code, render.Reader{ +func (ctx *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) { + ctx.Render(code, render.Reader{ Headers: extraHeaders, ContentType: contentType, ContentLength: contentLength, @@ -1071,19 +1075,19 @@ func (c *Context) DataFromReader(code int, contentLength int64, contentType stri } // File writes the specified file into the body stream in an efficient way. -func (c *Context) File(filepath string) { - http.ServeFile(c.Writer, c.Request, filepath) +func (ctx *Context) File(filepath string) { + http.ServeFile(ctx.Writer, ctx.Request, filepath) } // FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way. -func (c *Context) FileFromFS(filepath string, fs http.FileSystem) { +func (ctx *Context) FileFromFS(filepath string, fs http.FileSystem) { defer func(old string) { - c.Request.URL.Path = old - }(c.Request.URL.Path) + ctx.Request.URL.Path = old + }(ctx.Request.URL.Path) - c.Request.URL.Path = filepath + ctx.Request.URL.Path = filepath - http.FileServer(fs).ServeHTTP(c.Writer, c.Request) + http.FileServer(fs).ServeHTTP(ctx.Writer, ctx.Request) } var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") @@ -1094,18 +1098,18 @@ func escapeQuotes(s string) string { // FileAttachment writes the specified file into the body stream in an efficient way // On the client side, the file will typically be downloaded with the given filename -func (c *Context) FileAttachment(filepath, filename string) { +func (ctx *Context) FileAttachment(filepath, filename string) { if isASCII(filename) { - c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+escapeQuotes(filename)+`"`) + ctx.Writer.Header().Set("Content-Disposition", `attachment; filename="`+escapeQuotes(filename)+`"`) } else { - c.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename)) + ctx.Writer.Header().Set("Content-Disposition", `attachment; filename*=UTF-8''`+url.QueryEscape(filename)) } - http.ServeFile(c.Writer, c.Request, filepath) + http.ServeFile(ctx.Writer, ctx.Request, filepath) } // SSEvent writes a Server-Sent Event into the body stream. -func (c *Context) SSEvent(name string, message any) { - c.Render(-1, sse.Event{ +func (ctx *Context) SSEvent(name string, message any) { + ctx.Render(-1, sse.Event{ Event: name, Data: message, }) @@ -1113,8 +1117,8 @@ func (c *Context) SSEvent(name string, message any) { // Stream sends a streaming response and returns a boolean // indicates "Is client disconnected in middle of stream" -func (c *Context) Stream(step func(w io.Writer) bool) bool { - w := c.Writer +func (ctx *Context) Stream(step func(w io.Writer) bool) bool { + w := ctx.Writer clientGone := w.CloseNotify() for { select { @@ -1147,44 +1151,44 @@ type Negotiate struct { } // Negotiate calls different Render according to acceptable Accept format. -func (c *Context) Negotiate(code int, config Negotiate) { - switch c.NegotiateFormat(config.Offered...) { +func (ctx *Context) Negotiate(code int, config Negotiate) { + switch ctx.NegotiateFormat(config.Offered...) { case binding.MIMEJSON: data := chooseData(config.JSONData, config.Data) - c.JSON(code, data) + ctx.JSON(code, data) case binding.MIMEHTML: data := chooseData(config.HTMLData, config.Data) - c.HTML(code, config.HTMLName, data) + ctx.HTML(code, config.HTMLName, data) case binding.MIMEXML: data := chooseData(config.XMLData, config.Data) - c.XML(code, data) + ctx.XML(code, data) case binding.MIMEYAML: data := chooseData(config.YAMLData, config.Data) - c.YAML(code, data) + ctx.YAML(code, data) case binding.MIMETOML: data := chooseData(config.TOMLData, config.Data) - c.TOML(code, data) + ctx.TOML(code, data) default: - c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) //nolint: errcheck + ctx.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server")) //nolint: errcheck } } // NegotiateFormat returns an acceptable Accept format. -func (c *Context) NegotiateFormat(offered ...string) string { +func (ctx *Context) NegotiateFormat(offered ...string) string { assert1(len(offered) > 0, "you must provide at least one offer") - if c.Accepted == nil { - c.Accepted = parseAccept(c.requestHeader("Accept")) + if ctx.Accepted == nil { + ctx.Accepted = parseAccept(ctx.requestHeader("Accept")) } - if len(c.Accepted) == 0 { + if len(ctx.Accepted) == 0 { return offered[0] } - for _, accepted := range c.Accepted { + for _, accepted := range ctx.Accepted { for _, offer := range offered { // According to RFC 2616 and RFC 2396, non-ASCII characters are not allowed in headers, // therefore we can just iterate over the string without casting it into []rune @@ -1206,8 +1210,8 @@ func (c *Context) NegotiateFormat(offered ...string) string { } // SetAccepted sets Accept header data. -func (c *Context) SetAccepted(formats ...string) { - c.Accepted = formats +func (ctx *Context) SetAccepted(formats ...string) { + ctx.Accepted = formats } /************************************/ @@ -1215,53 +1219,53 @@ func (c *Context) SetAccepted(formats ...string) { /************************************/ // hasRequestContext returns whether c.Request has Context and fallback. -func (c *Context) hasRequestContext() bool { - hasFallback := c.engine != nil && c.engine.ContextWithFallback - hasRequestContext := c.Request != nil && c.Request.Context() != nil +func (ctx *Context) hasRequestContext() bool { + hasFallback := ctx.engine != nil && ctx.engine.ContextWithFallback + hasRequestContext := ctx.Request != nil && ctx.Request.Context() != nil return hasFallback && hasRequestContext } // Deadline returns that there is no deadline (ok==false) when c.Request has no Context. -func (c *Context) Deadline() (deadline time.Time, ok bool) { - if !c.hasRequestContext() { +func (ctx *Context) Deadline() (deadline time.Time, ok bool) { + if !ctx.hasRequestContext() { return } - return c.Request.Context().Deadline() + return ctx.Request.Context().Deadline() } // Done returns nil (chan which will wait forever) when c.Request has no Context. -func (c *Context) Done() <-chan struct{} { - if !c.hasRequestContext() { +func (ctx *Context) Done() <-chan struct{} { + if !ctx.hasRequestContext() { return nil } - return c.Request.Context().Done() + return ctx.Request.Context().Done() } // Err returns nil when c.Request has no Context. -func (c *Context) Err() error { - if !c.hasRequestContext() { +func (ctx *Context) Err() error { + if !ctx.hasRequestContext() { return nil } - return c.Request.Context().Err() + return ctx.Request.Context().Err() } // Value returns the value associated with this context for key, or nil // if no value is associated with key. Successive calls to Value with // the same key returns the same result. -func (c *Context) Value(key any) any { +func (ctx *Context) Value(key any) any { if key == ContextRequestKey { - return c.Request + return ctx.Request } if key == ContextKey { - return c + return ctx } if keyAsString, ok := key.(string); ok { - if val, exists := c.Get(keyAsString); exists { + if val, exists := ctx.Get(keyAsString); exists { return val } } - if !c.hasRequestContext() { + if !ctx.hasRequestContext() { return nil } - return c.Request.Context().Value(key) + return ctx.Request.Context().Value(key) } diff --git a/framework/gin/deprecated.go b/framework/gin/deprecated.go index ca1ec6b..4ed6bd4 100644 --- a/framework/gin/deprecated.go +++ b/framework/gin/deprecated.go @@ -14,10 +14,10 @@ import ( // See the binding package. // // Deprecated: Use MustBindWith or ShouldBindWith. -func (c *Context) BindWith(obj any, b binding.Binding) error { +func (ctx *Context) BindWith(obj any, b binding.Binding) error { log.Println(`BindWith(\"any, binding.Binding\") error is going to be deprecated, please check issue #662 and either use MustBindWith() if you want HTTP 400 to be automatically returned if any error occur, or use ShouldBindWith() if you need to manage the error.`) - return c.MustBindWith(obj, b) + return ctx.MustBindWith(obj, b) } diff --git a/framework/gin/gin.go b/framework/gin/gin.go index 360b817..7ac904a 100644 --- a/framework/gin/gin.go +++ b/framework/gin/gin.go @@ -6,6 +6,7 @@ package gin import ( "fmt" + "github.com/Superdanda/hade/framework" "html/template" "net" "net/http" @@ -89,6 +90,9 @@ const ( type Engine struct { RouterGroup + //新增Hade框架容器 + container framework.Container + // RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a // handler for the path with (without) the trailing slash exists. // For example if /foo/ is requested but a route only exists for /foo, the @@ -209,6 +213,7 @@ func New(opts ...OptionFunc) *Engine { secureJSONPrefix: "while(1);", trustedProxies: []string{"0.0.0.0/0", "::/0"}, trustedCIDRs: defaultTrustedCIDRs, + container: framework.NewHadeContainer(), } engine.RouterGroup.engine = engine engine.pool.New = func() any { @@ -237,7 +242,7 @@ func (engine *Engine) Handler() http.Handler { func (engine *Engine) allocateContext(maxParams uint16) *Context { v := make(Params, 0, maxParams) skippedNodes := make([]skippedNode, 0, engine.maxSections) - return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes} + return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes, container: engine.container} } // Delims sets template left and right delims and returns an Engine instance. diff --git a/framework/gin/hade_context.go b/framework/gin/hade_context.go index 55837d4..e3b7835 100644 --- a/framework/gin/hade_context.go +++ b/framework/gin/hade_context.go @@ -2,8 +2,35 @@ package gin import ( "context" + "github.com/Superdanda/hade/framework" ) func (ctx *Context) BaseContext() context.Context { return ctx.Request.Context() } + +// Bind engine 实现 container 的绑定封装 +func (engine *Engine) Bind(provider framework.ServiceProvider) error { + return engine.container.Bind(provider) +} + +// IsBind 关键字凭证是否已经绑定服务提供者 +func (engine *Engine) IsBind(key string) bool { + return engine.container.IsBind(key) +} + +// Make context 实现 container 的几个封装 +// 实现 make 的封装 +func (ctx *Context) Make(key string) (interface{}, error) { + return ctx.container.Make(key) +} + +// MustMake 实现 mustMake 的封装 +func (ctx *Context) MustMake(key string) interface{} { + return ctx.container.MustMake(key) +} + +// MakeNew 实现 makenew 的封装 +func (ctx *Context) MakeNew(key string, params []interface{}) (interface{}, error) { + return ctx.container.MakeNew(key, params) +} diff --git a/framework/gin/hade_request.go b/framework/gin/hade_request.go index 515253b..3b98cfd 100644 --- a/framework/gin/hade_request.go +++ b/framework/gin/hade_request.go @@ -5,13 +5,6 @@ import ( "mime/multipart" ) -package gin - -import ( -"mime/multipart" -"github.com/spf13/cast" -) - // const defaultMultipartMemory = 32 << 20 // 32 MB // 代表请求包含的方法 diff --git a/framework/middleware/cost.go b/framework/middleware/cost.go index d507c0a..1ce452f 100644 --- a/framework/middleware/cost.go +++ b/framework/middleware/cost.go @@ -1,27 +1,25 @@ package middleware import ( - "github.com/echo/hade/framework" + "github.com/Superdanda/hade/framework/gin" "log" "time" ) // recovery机制,将协程中的函数异常进行捕获 -func Cost() framework.ControllerHandler { +func Cost() gin.HandlerFunc { // 使用函数回调 - return func(c *framework.Context) error { + return func(c *gin.Context) { // 记录开始时间 start := time.Now() - log.Printf("api uri start: %v", c.GetRequest().RequestURI) + log.Printf("api uri start: %v", c.Request.RequestURI) // 使用next执行具体的业务逻辑 c.Next() // 记录结束时间 end := time.Now() cost := end.Sub(start) - log.Printf("api uri end: %v, cost: %v", c.GetRequest().RequestURI, cost.Seconds()) - - return nil + log.Printf("api uri end: %v, cost: %v", c.Request.RequestURI, cost.Seconds()) } } diff --git a/framework/middleware/recovery.go b/framework/middleware/recovery.go index a44615d..2160a21 100644 --- a/framework/middleware/recovery.go +++ b/framework/middleware/recovery.go @@ -1,18 +1,17 @@ package middleware import ( - "github.com/echo/hade/framework" + "github.com/Superdanda/hade/framework/gin" "net/http" ) -func Recovery() framework.ControllerHandler { - return func(c *framework.Context) error { +func Recovery() gin.HandlerFunc { + return func(c *gin.Context) { defer func() { if err := recover(); err != nil { - c.SetStatus(http.StatusInternalServerError).Json(err) + c.ISetStatus(http.StatusInternalServerError).IJson(err) } }() c.Next() - return nil } } diff --git a/framework/middleware/timeout.go b/framework/middleware/timeout.go index 0841615..96f21e7 100644 --- a/framework/middleware/timeout.go +++ b/framework/middleware/timeout.go @@ -3,15 +3,15 @@ package middleware import ( "context" "fmt" - "github.com/echo/hade/framework" + "github.com/Superdanda/hade/framework/gin" "log" "net/http" "time" ) -func Timeout(d time.Duration) framework.ControllerHandler { +func Timeout(d time.Duration) gin.HandlerFunc { // 使用函数回调 - return func(c *framework.Context) error { + return func(c *gin.Context) { finish := make(chan struct{}, 1) panicChan := make(chan interface{}, 1) // 执行业务逻辑前预操作:初始化超时context @@ -32,14 +32,12 @@ func Timeout(d time.Duration) framework.ControllerHandler { // 执行业务逻辑后操作 select { case p := <-panicChan: - c.SetStatus(http.StatusInternalServerError).Json("time out") + c.ISetStatus(http.StatusInternalServerError).IJson("time out") log.Println(p) case <-finish: fmt.Println("finish") case <-durationCtx.Done(): - c.SetHasTimeout() - c.SetStatus(http.StatusInternalServerError).Json("time out") + c.ISetStatus(http.StatusInternalServerError).IJson("time out") } - return nil } } diff --git a/framework/provider.go b/framework/provider.go new file mode 100644 index 0000000..0e00f1b --- /dev/null +++ b/framework/provider.go @@ -0,0 +1,23 @@ +package framework + +// NewInstance 定义了如何创建一个新实例,所有服务容器的创建服务 +type NewInstance func(...interface{}) (interface{}, error) + +type ServiceProvider interface { + // Register 在服务容器中注册了一个实例化服务的方法,是否在注册的时候就实例化这个服务,需要参考 IsDefer 接口。 + Register(Container) NewInstance + + // Boot 在调用实例化服务的时候会调用,可以把一些准备工作:基础配置,初始化参数的操作放在这个里面。 + //如果 Boot 返回 error,整个服务实例化就会实例化失败,返回错误 + Boot(Container) error + + // IsDefer 决定是否在注册的时候实例化这个服务,如果不是注册的时候实例化,那就是在第一次 make 的时候进行实例化操作 + //false 表示不需要延迟实例化,在注册的时候就实例化。true 表示延迟实例化 + IsDefer() bool + + // Params params 定义传递给 NewInstance 的参数,可以自定义多个,建议将 container 作为第一个参数 + Params(Container) []interface{} + + // Name 代表了这个服务提供者的凭证 + Name() string +} diff --git a/go.mod b/go.mod index 9870627..f7c939f 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/Superdanda/hade go 1.23.2 -require github.com/spf13/cast v1.7.0 // indirect +require github.com/spf13/cast v1.7.0 //gin require ( diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5e3efc3 --- /dev/null +++ b/go.sum @@ -0,0 +1,95 @@ +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go index d1f9fd3..83bf824 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,9 @@ package main import ( "context" - "github.com/echo/hade/framework/gin" - middleware2 "github.com/echo/hade/framework/middleware" + "github.com/Superdanda/hade/framework/gin" + middleware2 "github.com/Superdanda/hade/framework/middleware" + "github.com/Superdanda/hade/provider/demo" "log" "net/http" "os" @@ -18,6 +19,8 @@ func main() { core.Use(middleware2.Recovery()) core.Use(middleware2.Cost()) + core.Bind(&demo.DemoServiceProvider{}) + registerRouter(core) server := &http.Server{ Handler: core, diff --git a/middleware.go b/middleware.go index faaac89..010ee4f 100644 --- a/middleware.go +++ b/middleware.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/echo/hade/framework/gin" + "github.com/Superdanda/hade/framework/gin" ) func Test1() gin.HandlerFunc { diff --git a/provider/demo/contract.go b/provider/demo/contract.go new file mode 100644 index 0000000..bed5a16 --- /dev/null +++ b/provider/demo/contract.go @@ -0,0 +1 @@ +package demo diff --git a/provider/demo/provider.go b/provider/demo/provider.go new file mode 100644 index 0000000..9542aef --- /dev/null +++ b/provider/demo/provider.go @@ -0,0 +1,30 @@ +package demo + +import ( + "fmt" + "github.com/Superdanda/hade/framework" +) + +type DemoServiceProvider struct { +} + +func (sp *DemoServiceProvider) Name() string { + return Key +} + +func (sp *DemoServiceProvider) Register(c framework.Container) framework.NewInstance { + return NewDemoService +} + +func (sp *DemoServiceProvider) IsDefer() bool { + return true +} + +func (sp *DemoServiceProvider) Params(c framework.Container) []interface{} { + return []interface{}{c} +} + +func (sp *DemoServiceProvider) Boot(c framework.Container) error { + fmt.Println("demo service boot") + return nil +} diff --git a/provider/demo/service.go b/provider/demo/service.go new file mode 100644 index 0000000..8d15e43 --- /dev/null +++ b/provider/demo/service.go @@ -0,0 +1,36 @@ +package demo + +import ( + "fmt" + "github.com/Superdanda/hade/framework" +) + +// Key Demo 服务的 key +const Key = "hade:demo" + +// Service Demo 服务的接口 +type Service interface { + GetFoo() Foo +} + +// Foo Demo 服务接口定义的一个数据结构 +type Foo struct { + Name string +} + +// DemoService serviceProvider 实现 +type DemoService struct { + Service + c framework.Container +} + +// GetFoo 实现接口 +func (s *DemoService) GetFoo() Foo { + return Foo{Name: "i am foo"} +} + +func NewDemoService(params ...interface{}) (interface{}, error) { + c := params[0].(framework.Container) + fmt.Println("new demo service") + return &DemoService{c: c}, nil +} diff --git a/route.go b/route.go index 38e34e7..46d89bf 100644 --- a/route.go +++ b/route.go @@ -1,7 +1,7 @@ package main import ( - "github.com/echo/hade/framework/gin" + "github.com/Superdanda/hade/framework/gin" ) func registerRouter(core *gin.Engine) { diff --git a/subject_controller.go b/subject_controller.go index d439db0..3628c7a 100644 --- a/subject_controller.go +++ b/subject_controller.go @@ -1,6 +1,9 @@ package main -import "github.com/echo/hade/framework/gin" +import ( + "github.com/Superdanda/hade/framework/gin" + "github.com/Superdanda/hade/provider/demo" +) func SubjectDelController(c *gin.Context) { c.ISetOkStatus().IJson("ok SubjectDelController") @@ -15,5 +18,9 @@ func SubjectGetController(c *gin.Context) { } func SubjectListController(c *gin.Context) { - c.ISetOkStatus().IJson("ok SubjectListController") + demoService := c.MustMake(demo.Key).(demo.Service) + + foo := demoService.GetFoo() + + c.ISetOkStatus().IJson(foo) } diff --git a/user_controller.go b/user_controller.go index 94433cf..58e0ea4 100644 --- a/user_controller.go +++ b/user_controller.go @@ -3,8 +3,7 @@ package main import ( "context" "fmt" - - "github.com/echo/hade/framework/gin" + "github.com/Superdanda/hade/framework/gin" "net/http" "time" )