From 722e8191a931e2e4ea196fffa7a8129356942b90 Mon Sep 17 00:00:00 2001 From: Muyao CHEN Date: Sat, 28 Sep 2024 11:13:18 +0200 Subject: [PATCH] GoWeb: Remove everything to use Gin --- framework/context.go | 124 -------- framework/controller.go | 3 - framework/core.go | 115 -------- framework/group.go | 54 ---- framework/middleware/recovery.go | 9 +- framework/middleware/test.go | 17 +- framework/middleware/timeout.go | 15 +- framework/middleware/timeout_test.go | 33 +-- framework/request.go | 411 --------------------------- framework/response.go | 123 -------- framework/trie.go | 180 ------------ go.mod | 34 ++- go.sum | 96 ++++++- handlers.go | 78 +---- main.go | 4 +- routes.go | 16 +- 16 files changed, 175 insertions(+), 1137 deletions(-) delete mode 100644 framework/context.go delete mode 100644 framework/controller.go delete mode 100644 framework/core.go delete mode 100644 framework/group.go delete mode 100644 framework/request.go delete mode 100644 framework/response.go delete mode 100644 framework/trie.go diff --git a/framework/context.go b/framework/context.go deleted file mode 100644 index 81a96df..0000000 --- a/framework/context.go +++ /dev/null @@ -1,124 +0,0 @@ -package framework - -import ( - "context" - "net/http" - "sync" - "time" -) - -// Context type is the customized context of Araneae framework -// -// It packages the internal context.Context with basic "wr" couple. -type Context struct { - ctx context.Context - request *http.Request - responseWriter http.ResponseWriter - - handlers []ControllerHandler - // current handler index - index int - - params map[string]string - - hasTimeout bool - writerMux *sync.Mutex -} - -// NewContext create a new context -func NewContext(w http.ResponseWriter, r *http.Request) *Context { - return &Context{ - ctx: r.Context(), - request: r, - responseWriter: w, - writerMux: &sync.Mutex{}, - index: -1, // will be set to 0 when at the beginning - } -} - -// {{{ Basic functions - -// WriterMux returns the writer mutex. -// -// This is useful when goroutines concurrently write into responseWriter, -// while at the same time we are writing into the responseWriter for a -// panic or timeout. -// We can protect it at the context level. -func (ctx *Context) WriterMux() *sync.Mutex { - return ctx.writerMux -} - -// GetRequest returns the original request -func (ctx *Context) GetRequest() *http.Request { - return ctx.request -} - -// GetResponseWriter returns the original response writer -func (ctx *Context) GetResponseWriter() http.ResponseWriter { - return ctx.responseWriter -} - -// SetHasTimeout indicates that the context has timeout. -// -// So that other goroutines won't write into the responseWriter anymore -func (ctx *Context) SetHasTimeout() { - ctx.hasTimeout = true -} - -// HasTimeout returns whether the context has timeout. -func (ctx *Context) HasTimeout() bool { - return ctx.hasTimeout -} - -// }}} -// {{{ Implements context interface - -// BaseContext return a request default Context -func (ctx *Context) BaseContext() context.Context { - return ctx.request.Context() -} - -// Done calls the base function -func (ctx *Context) Done() <-chan struct{} { - return ctx.BaseContext().Done() -} - -// Deadline calls the base function -func (ctx *Context) Deadline() (deadline time.Time, ok bool) { - return ctx.BaseContext().Deadline() -} - -// Err calls the base function -func (ctx *Context) Err() error { - return ctx.BaseContext().Err() -} - -// Value calls the base function -func (ctx *Context) Value(key any) any { - return ctx.BaseContext().Value(key) -} - -// Next runs the next function in the function chain -func (ctx *Context) Next() error { - ctx.index++ - if ctx.index >= len(ctx.handlers) { - // This is the end of the chain - return nil - } - // Run this handler - if err := ctx.handlers[ctx.index](ctx); err != nil { - return err - } - return nil -} - -// SetHandlers sets handlers for context -func (ctx *Context) SetHandlers(handlers []ControllerHandler) { - ctx.handlers = handlers -} - -func (ctx *Context) SetParams(params map[string]string) { - ctx.params = params -} - -// }}} diff --git a/framework/controller.go b/framework/controller.go deleted file mode 100644 index 6ed9890..0000000 --- a/framework/controller.go +++ /dev/null @@ -1,3 +0,0 @@ -package framework - -type ControllerHandler func(c *Context) error diff --git a/framework/core.go b/framework/core.go deleted file mode 100644 index a657f1d..0000000 --- a/framework/core.go +++ /dev/null @@ -1,115 +0,0 @@ -package framework - -import ( - "log" - "net/http" - "strings" -) - -// Core is the core struct of the framework -type Core struct { - router map[string]*Trie - middlewares []ControllerHandler -} - -// NewCore initialize the Core. -func NewCore() *Core { - getRouter := NewTrie() - postRouter := NewTrie() - putRouter := NewTrie() - deleteRouter := NewTrie() - - router := map[string]*Trie{} - router["GET"] = getRouter - router["POST"] = postRouter - router["PUT"] = putRouter - router["DELETE"] = deleteRouter - - return &Core{router: router} -} - -// Get is a simple get router -func (c *Core) Get(url string, handlers ...ControllerHandler) { - allHandlers := append(c.middlewares, handlers...) - if err := c.router["GET"].AddRouter(url, allHandlers); err != nil { - log.Println(err) - } -} - -// Post is a simple post router -func (c *Core) Post(url string, handlers ...ControllerHandler) { - allHandlers := append(c.middlewares, handlers...) - if err := c.router["POST"].AddRouter(url, allHandlers); err != nil { - log.Println(err) - } -} - -// Put is a simple put router -func (c *Core) Put(url string, handlers ...ControllerHandler) { - allHandlers := append(c.middlewares, handlers...) - if err := c.router["PUT"].AddRouter(url, allHandlers); err != nil { - log.Println(err) - } -} - -// Delete is a simple delete router -func (c *Core) Delete(url string, handlers ...ControllerHandler) { - allHandlers := append(c.middlewares, handlers...) - if err := c.router["DELETE"].AddRouter(url, allHandlers); err != nil { - log.Println(err) - } -} - -// Use registers middlewares -func (c *Core) Use(middlewares ...ControllerHandler) { - c.middlewares = append(c.middlewares, middlewares...) -} - -// FindRouteByRequest finds route using the request -func (c *Core) FindRouteByRequest(r *http.Request) *node { - upperMethod := strings.ToUpper(r.Method) - - mapper, ok := c.router[upperMethod] - if !ok { - log.Printf("Method %q is not recognized\n", upperMethod) - return nil - } - - node := mapper.FindRoute(r.URL.Path) - if node == nil { - log.Printf("URI %q is not recognized\n", r.URL.Path) - return nil - } - - return node -} - -func (c *Core) Group(prefix string) IGroup { - return &Group{ - core: c, - prefix: prefix, - } -} - -// ServeHTTP implements the Handler interface -func (c *Core) ServeHTTP(w http.ResponseWriter, r *http.Request) { - log.Println("Welcome to the Araneae framework") - - ctx := NewContext(w, r) - - node := c.FindRouteByRequest(r) - if node == nil { - ctx.WriteJSON(http.StatusNotFound, "Request not found") - return - } - - params := node.ParseParamsFromEndNode(r.URL.Path) - - ctx.SetParams(params) - ctx.SetHandlers(node.handlers) - - if err := ctx.Next(); err != nil { - ctx.WriteJSON(http.StatusInternalServerError, "Internal error") - return - } -} diff --git a/framework/group.go b/framework/group.go deleted file mode 100644 index 9354450..0000000 --- a/framework/group.go +++ /dev/null @@ -1,54 +0,0 @@ -package framework - -// IGroup prefix routes -type IGroup interface { - Get(string, ...ControllerHandler) - Post(string, ...ControllerHandler) - Put(string, ...ControllerHandler) - Delete(string, ...ControllerHandler) - Use(...ControllerHandler) -} - -// Group is the implementation of IGroup interface -type Group struct { - core *Core - prefix string - middlewares []ControllerHandler -} - -// NewGroup create a new prefix group -func NewGroup(core *Core, prefix string) *Group { - return &Group{ - core: core, - prefix: prefix, - } -} - -// Get is a simple get router of the group -func (g *Group) Get(url string, handlers ...ControllerHandler) { - allHandlers := append(g.middlewares, handlers...) - g.core.Get(g.prefix+url, allHandlers...) -} - -// Post is a simple post router of the group -func (g *Group) Post(url string, handlers ...ControllerHandler) { - allHandlers := append(g.middlewares, handlers...) - g.core.Post(g.prefix+url, allHandlers...) -} - -// Put is a simple put router of the group -func (g *Group) Put(url string, handlers ...ControllerHandler) { - allHandlers := append(g.middlewares, handlers...) - g.core.Put(g.prefix+url, allHandlers...) -} - -// Delete is a simple delete router of the group -func (g *Group) Delete(url string, handlers ...ControllerHandler) { - allHandlers := append(g.middlewares, handlers...) - g.core.Delete(g.prefix+url, allHandlers...) -} - -// Use registers middlewares -func (g *Group) Use(middlewares ...ControllerHandler) { - g.middlewares = append(g.middlewares, middlewares...) -} diff --git a/framework/middleware/recovery.go b/framework/middleware/recovery.go index 16568a9..d248b5f 100644 --- a/framework/middleware/recovery.go +++ b/framework/middleware/recovery.go @@ -3,19 +3,18 @@ package middleware import ( "net/http" - "git.vinchent.xyz/vinchent/go-web/framework" + "github.com/gin-gonic/gin" ) // Recovery is a middleware that recovers from the panic -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.WriteJSON(http.StatusInternalServerError, err) + c.JSON(http.StatusInternalServerError, err) } }() c.Next() - return nil } } diff --git a/framework/middleware/test.go b/framework/middleware/test.go index 8a068ee..9ff2696 100644 --- a/framework/middleware/test.go +++ b/framework/middleware/test.go @@ -3,32 +3,29 @@ package middleware import ( "log" - "git.vinchent.xyz/vinchent/go-web/framework" + "github.com/gin-gonic/gin" ) -func Test1() framework.ControllerHandler { - return func(c *framework.Context) error { +func Test1() gin.HandlerFunc { + return func(c *gin.Context) { log.Println("middleware test1 pre") c.Next() log.Println("middleware test1 post") - return nil } } -func Test2() framework.ControllerHandler { - return func(c *framework.Context) error { +func Test2() gin.HandlerFunc { + return func(c *gin.Context) { log.Println("middleware test2 pre") c.Next() log.Println("middleware test2 post") - return nil } } -func Test3() framework.ControllerHandler { - return func(c *framework.Context) error { +func Test3() gin.HandlerFunc { + return func(c *gin.Context) { log.Println("middleware test3 pre") c.Next() log.Println("middleware test3 post") - return nil } } diff --git a/framework/middleware/timeout.go b/framework/middleware/timeout.go index 47183e9..6e7fa62 100644 --- a/framework/middleware/timeout.go +++ b/framework/middleware/timeout.go @@ -6,15 +6,15 @@ import ( "net/http" "time" - "git.vinchent.xyz/vinchent/go-web/framework" + "github.com/gin-gonic/gin" ) -func Timeout(d time.Duration) framework.ControllerHandler { - return func(c *framework.Context) error { +func Timeout(d time.Duration) gin.HandlerFunc { + return func(c *gin.Context) { finish := make(chan struct{}, 1) panicChan := make(chan interface{}, 1) - durationCtx, cancel := context.WithTimeout(c.BaseContext(), d) + durationCtx, cancel := context.WithTimeout(c.Request.Context(), d) defer cancel() go func() { @@ -35,15 +35,12 @@ func Timeout(d time.Duration) framework.ControllerHandler { case p := <-panicChan: // panic log.Println(p) - c.GetResponseWriter().WriteHeader(http.StatusInternalServerError) + c.Status(http.StatusInternalServerError) case <-finish: // finish normally log.Println("finish") case <-durationCtx.Done(): - c.SetHasTimeout() - c.GetResponseWriter().WriteHeader(http.StatusRequestTimeout) - c.GetResponseWriter().Write([]byte("time out")) + c.JSON(http.StatusRequestTimeout, "time out") } - return nil } } diff --git a/framework/middleware/timeout_test.go b/framework/middleware/timeout_test.go index f8a1a61..80043bb 100644 --- a/framework/middleware/timeout_test.go +++ b/framework/middleware/timeout_test.go @@ -8,30 +8,28 @@ import ( "testing" "time" - "git.vinchent.xyz/vinchent/go-web/framework" + "github.com/gin-gonic/gin" ) func TestTimeout(t *testing.T) { t.Run("Test timeout handler", func(t *testing.T) { timeoutHandler := Timeout(1 * time.Millisecond) - longHandler := func(c *framework.Context) error { + longHandler := func(c *gin.Context) { time.Sleep(2 * time.Millisecond) - return nil } res := prepareMiddlewareTest(t, timeoutHandler, longHandler) assertCode(t, res.StatusCode, http.StatusRequestTimeout) - assertBody(t, res.Body, "time out") + assertBody(t, res.Body, "\"time out\"") }) t.Run("Test no timeout", func(t *testing.T) { timeoutHandler := Timeout(2 * time.Millisecond) - quickHandler := func(c *framework.Context) error { + quickHandler := func(c *gin.Context) { // time.Sleep(1 * time.Millisecond) - c.WriteJSON(http.StatusOK, "ok") - return nil + c.JSON(http.StatusOK, "ok") } res := prepareMiddlewareTest(t, timeoutHandler, quickHandler) @@ -44,7 +42,7 @@ func TestRecover(t *testing.T) { t.Run("Test panic", func(t *testing.T) { recoverer := Recovery() - panicHandler := func(c *framework.Context) error { + panicHandler := func(c *gin.Context) { panic("panic") } @@ -55,9 +53,8 @@ func TestRecover(t *testing.T) { t.Run("Test no panic", func(t *testing.T) { recoverer := Recovery() - normalHandler := func(c *framework.Context) error { - c.WriteJSON(http.StatusOK, "ok") - return nil + normalHandler := func(c *gin.Context) { + c.JSON(http.StatusOK, "ok") } res := prepareMiddlewareTest(t, recoverer, normalHandler) @@ -68,20 +65,18 @@ func TestRecover(t *testing.T) { func prepareMiddlewareTest( t testing.TB, - mid framework.ControllerHandler, - in framework.ControllerHandler, + mid gin.HandlerFunc, + in gin.HandlerFunc, ) *http.Response { t.Helper() request := httptest.NewRequest(http.MethodGet, "/", nil) response := httptest.NewRecorder() - c := framework.NewContext(response, request) + _, r := gin.CreateTestContext(response) - c.SetHandlers([]framework.ControllerHandler{in}) + r.Use(mid) + r.GET("/", in) - err := mid(c) - if err != nil { - t.Fatal(err) - } + r.ServeHTTP(response, request) res := response.Result() return res diff --git a/framework/request.go b/framework/request.go deleted file mode 100644 index e38bb7d..0000000 --- a/framework/request.go +++ /dev/null @@ -1,411 +0,0 @@ -package framework - -import ( - "bytes" - "encoding/json" - "encoding/xml" - "errors" - "io" - "mime/multipart" - "net/url" - - "github.com/spf13/cast" -) - -type IRequest interface { - // url query - // e.g. foo.com?a=1&b=bar&c[]=bar - QueryAll(key string) url.Values - QueryInt(key string, defval int) (int, bool) - QueryInt64(key string, defval int64) (int64, bool) - QueryFloat32(key string, defval float32) (float32, bool) - QueryFloat64(key string, defval float64) (float64, bool) - QueryBool(key string, defval bool) (bool, bool) - QueryString(key string, defval string) (string, bool) - QueryStringSlice(key string, defval []string) ([]string, bool) - - // url params - // e.g. /book/:id - Param(key string) any - ParamInt(key string, defval int) (int, bool) - ParamInt64(key string, defval int64) (int64, bool) - ParamFloat32(key string, defval float32) (float32, bool) - ParamFloat64(key string, defval float64) (float64, bool) - ParamBool(key string, defval bool) (bool, bool) - ParamString(key string, defval string) (string, bool) - - // form - FormAll(key string) url.Values - FormInt(key string, defval int) (int, bool) - FormInt64(key string, defval int64) (int64, bool) - FormFloat32(key string, defval float32) (float32, bool) - FormFloat64(key string, defval float64) (float64, bool) - FormBool(key string, defval bool) (bool, bool) - FormString(key string, defval string) (string, bool) - FormStringSlice(key string, defval []string) ([]string, bool) - FormFile(key string) (*multipart.FileHeader, error) - - // JSON body - BindJSON(obj any) error - - // XML body - BindXML(obj any) error - - // RAW body - GetRawData() ([]byte, error) - - // Basic informations - Uri() string - Method() string - Host() string - ClientIP() string - - // Header - Headers() map[string][]string - Header(key string) (string, bool) - - // Cookie - Cookies() map[string]string - Cookie(key string) (string, bool) -} - -// {{{ url query - -// QueryAll returns all queries in a request URL -func (ctx *Context) QueryAll() url.Values { - if ctx.request != nil { - return map[string][]string(ctx.request.URL.Query()) - } - return url.Values{} -} - -// QueryInt gets an int value from the query request -func (ctx *Context) QueryInt(key string, defval int) (int, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToInt(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) QueryInt64(key string, defval int64) (int64, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToInt64(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) QueryBool(key string, defval bool) (bool, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToBool(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) QueryFloat32(key string, defval float32) (float32, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToFloat32(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) QueryFloat64(key string, defval float64) (float64, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToFloat64(vals[0]), true - } - } - return defval, false -} - -// QueryString gets a string value from the query request -func (ctx *Context) QueryString(key string, defval string) (string, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToString(vals[0]), true - } - } - return defval, false -} - -// QueryArray gets an array of string values from the query request -func (ctx *Context) QueryStringSlice(key string, defval []string) ([]string, bool) { - params := ctx.QueryAll() - if vals, ok := params[key]; ok { - return cast.ToStringSlice(vals[0]), true - } - return defval, false -} - -// }}} -// {{{ url params - -func (ctx *Context) Param(key string) any { - if ctx.params != nil { - if val, ok := ctx.params[key]; ok { - return val - } - } - return nil -} - -func (ctx *Context) ParamInt(key string, def int) (int, bool) { - if val := ctx.Param(key); val != nil { - return cast.ToInt(val), true - } - return def, false -} - -func (ctx *Context) ParamInt64(key string, def int64) (int64, bool) { - if val := ctx.Param(key); val != nil { - return cast.ToInt64(val), true - } - return def, false -} - -func (ctx *Context) ParamFloat64(key string, def float64) (float64, bool) { - if val := ctx.Param(key); val != nil { - return cast.ToFloat64(val), true - } - return def, false -} - -func (ctx *Context) ParamFloat32(key string, def float32) (float32, bool) { - if val := ctx.Param(key); val != nil { - return cast.ToFloat32(val), true - } - return def, false -} - -func (ctx *Context) ParamBool(key string, def bool) (bool, bool) { - if val := ctx.Param(key); val != nil { - return cast.ToBool(val), true - } - return def, false -} - -func (ctx *Context) ParamString(key string, def string) (string, bool) { - if val := ctx.Param(key); val != nil { - return cast.ToString(val), true - } - return def, false -} - -// }}} -// {{{ Post form - -// FormAll gets everything from the submitted form -func (ctx *Context) FormAll() url.Values { - if ctx.request != nil { - _ = ctx.request.ParseForm() - return ctx.request.PostForm - } - return url.Values{} -} - -// FormInt gets an int value from the submitted form -func (ctx *Context) FormInt(key string, defval int) (int, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToInt(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) FormInt64(key string, defval int64) (int64, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToInt64(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) FormBool(key string, defval bool) (bool, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToBool(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) FormFloat32(key string, defval float32) (float32, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToFloat32(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) FormFloat64(key string, defval float64) (float64, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToFloat64(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) FormString(key string, defval string) (string, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - if len(vals) > 0 { - return cast.ToString(vals[0]), true - } - } - return defval, false -} - -func (ctx *Context) FormStringSlice(key string, defval []string) ([]string, bool) { - params := ctx.FormAll() - if vals, ok := params[key]; ok { - return cast.ToStringSlice(vals[0]), true - } - return defval, false -} - -// }}} -// {{{ type binder - -var ( - ErrNoRequest = errors.New("missing request in the context") - ErrNotSingleObj = errors.New("body must have only a single value") -) - -// JSON body -func (ctx *Context) BindJSON(obj any) error { - if ctx.request == nil { - return ErrNoRequest - } - dec := json.NewDecoder(ctx.request.Body) - err := dec.Decode(obj) - if err != nil { - return err - } - err = dec.Decode(&struct{}{}) - if err != io.EOF { - return ErrNotSingleObj - } - - return nil -} - -// XML body -func (ctx *Context) BindXML(obj any) error { - if ctx.request == nil { - return ErrNoRequest - } - dec := xml.NewDecoder(ctx.request.Body) - err := dec.Decode(obj) - if err != nil { - return err - } - err = dec.Decode(&struct{}{}) - if err != io.EOF { - return ErrNotSingleObj - } - - return nil -} - -// RAW body -func (ctx *Context) GetRawData() ([]byte, error) { - if ctx.request == nil { - return []byte{}, ErrNoRequest - } - body, err := io.ReadAll(ctx.request.Body) - if err != nil { - return []byte{}, err - } - /* Restore the body (io.ReadCloser) to it's original state */ - ctx.request.Body = io.NopCloser(bytes.NewBuffer(body)) - - return body, nil -} - -// }}} -// {{{ Basic informations - -func (ctx *Context) Uri() string { - return ctx.request.RequestURI -} - -func (ctx *Context) Method() string { - return ctx.request.Method -} - -func (ctx *Context) Host() string { - return ctx.request.Host -} - -func (ctx *Context) ClientIP() string { - r := ctx.request - ipAddress := r.Header.Get("X-Real-Ip") - if ipAddress == "" { - ipAddress = r.Header.Get("X-Forwarded-For") - } - if ipAddress == "" { - ipAddress = r.RemoteAddr - } - return ipAddress -} - -// }}} -// {{{ Headers - -// Header -func (ctx *Context) Headers() map[string][]string { - return ctx.request.Header -} - -func (ctx *Context) Header(key string) (string, bool) { - vals := ctx.request.Header.Values(key) - if vals == nil || len(vals) <= 0 { - return "", false - } - return vals[0], true -} - -// }}} -// {{{ Cookies - -// Cookies gets cookie key-value pairs -func (ctx *Context) Cookies() map[string]string { - cookies := ctx.request.Cookies() - ret := map[string]string{} - for _, c := range cookies { - ret[c.Name] = c.Value - } - return ret -} - -func (ctx *Context) Cookie(key string) (string, bool) { - cookies := ctx.Cookies() - if val, ok := cookies[key]; ok { - return val, true - } - return "", false -} - -// }}} diff --git a/framework/response.go b/framework/response.go deleted file mode 100644 index 8dd00ce..0000000 --- a/framework/response.go +++ /dev/null @@ -1,123 +0,0 @@ -package framework - -import ( - "encoding/json" - "encoding/xml" - "fmt" - "html/template" - "net/http" - "net/url" -) - -type IResponse interface { - WriteJSON(status int, obj any) IResponse - WriteXML(status int, obj any) IResponse - WriteHTML(status int, filepath string, obj any) IResponse - WriteText(format string, values ...any) IResponse - - Redirect(path string) IResponse - SetHeader(key string, val string) IResponse - SetCookie( - key string, - val string, - maxAge int, - path, domain string, - secure, httpOnly bool, - ) IResponse - SetStatus(code int) IResponse - - // set 200 - SetOkStatus() IResponse -} - -func (ctx *Context) WriteJSON(status int, obj any) IResponse { - // There is a timeout, some error message data must have already been - // written to the output. Stop writing anything into the responseWriter. - if ctx.HasTimeout() { - return nil - } - data, err := json.Marshal(obj) - if err != nil { - return ctx.SetStatus(http.StatusInternalServerError) - } - ctx.responseWriter.Header().Set("Content-type", "application/json") - ctx.responseWriter.WriteHeader(status) - _, _ = ctx.responseWriter.Write(data) - return ctx -} - -func (ctx *Context) WriteXML(status int, obj any) IResponse { - // There is a timeout, some error message data must have already been - // written to the output. Stop writing anything into the responseWriter. - if ctx.HasTimeout() { - return nil - } - data, err := xml.Marshal(obj) - if err != nil { - return ctx.SetStatus(http.StatusInternalServerError) - } - ctx.responseWriter.Header().Set("Content-type", "application/json") - ctx.responseWriter.WriteHeader(status) - _, _ = ctx.responseWriter.Write(data) - return ctx -} - -func (ctx *Context) WriteHTML(status int, filepath string, obj any) IResponse { - ctx.SetHeader("Content-Type", "application/html") - t, _ := template.New("output").ParseFiles(filepath) - t.Execute(ctx.responseWriter, obj) - - return ctx -} - -func (ctx *Context) WriteText(format string, values ...any) IResponse { - out := fmt.Sprintf(format, values...) - ctx.SetHeader("Content-Type", "application/text") - ctx.responseWriter.Write([]byte(out)) - return ctx -} - -func (ctx *Context) Redirect(path string) IResponse { - http.Redirect(ctx.responseWriter, ctx.request, path, http.StatusTemporaryRedirect) - return ctx -} - -func (ctx *Context) SetHeader(key string, val string) IResponse { - ctx.responseWriter.Header().Add(key, val) - return ctx -} - -func (ctx *Context) SetCookie( - key string, - val string, - maxAge int, - path, domain string, - secure, httpOnly bool, -) IResponse { - if path == "" { - path = "/" - } - - http.SetCookie(ctx.responseWriter, &http.Cookie{ - Name: key, - Value: url.QueryEscape(val), - MaxAge: maxAge, - Path: path, - Domain: domain, - SameSite: http.SameSiteDefaultMode, - Secure: secure, - HttpOnly: httpOnly, - }) - return ctx -} - -func (ctx *Context) SetStatus(code int) IResponse { - ctx.responseWriter.WriteHeader(code) - return ctx -} - -// set 200 -func (ctx *Context) SetOkStatus() IResponse { - ctx.responseWriter.WriteHeader(http.StatusOK) - return ctx -} diff --git a/framework/trie.go b/framework/trie.go deleted file mode 100644 index 83e1673..0000000 --- a/framework/trie.go +++ /dev/null @@ -1,180 +0,0 @@ -package framework - -import ( - "errors" - "fmt" - "strings" -) - -type Trie struct { - root *node -} - -func NewTrie() *Trie { - return &Trie{root: newNode("")} -} - -func (t *Trie) FindRoute(uri string) *node { - uri = strings.ToUpper(uri) - uri = strings.TrimPrefix(uri, "/") - if uri == "" { - return t.root - } - - found := t.root.findRoute(uri) - if found == nil { - return nil - } - - return found -} - -func (t *Trie) AddRouter(uri string, handlers []ControllerHandler) error { - uri = strings.ToUpper(uri) - uri = strings.TrimPrefix(uri, "/") - if uri == "" { - t.root.isLast = true - t.root.handlers = append(t.root.handlers, handlers...) - return nil - } - - upperUri := strings.ToUpper(uri) - match := t.FindRoute(upperUri) - if match != nil { - // existing route - return fmt.Errorf("existing route for %q", uri) - } - - // The route does not exist, add it to the tree - err := t.root.addRoute(upperUri, handlers) - if err != nil { - return err - } - return nil -} - -type node struct { - isLast bool - segment string - handlers []ControllerHandler - children []*node - parent *node -} - -func (n *node) ParseParamsFromEndNode(uri string) map[string]string { - ret := map[string]string{} - uri = strings.ToUpper(uri) - uri = strings.TrimPrefix(uri, "/") - if uri == "" { - // root - return ret - } - segments := strings.Split(uri, "/") - cnt := len(segments) - cur := n - - for i := cnt - 1; i >= 0; i-- { - if isWildcard(cur.segment) { - // set params - ret[cur.segment[1:]] = segments[i] - } - cur = cur.parent - } - return ret -} - -func newNode(segment string) *node { - return &node{segment: segment} -} - -func isWildcard(s string) bool { - return strings.HasPrefix(s, ":") -} - -// /user/name -// /user/:id/name -// /user/3/name - -// findRoute finds the handler for the uri if exists. -// -// we suppose that uri passed here doesn't begin with "/" -func (n *node) findRoute(uri string) *node { - splitted := strings.SplitN(uri, "/", 2) - splittedLen := len(splitted) - - if isWildcard(splitted[0]) { - // input is a wildcard, check if this endpoint has already children - // if so, return the first one which is not nil. - nbChildren := len(n.children) - if nbChildren > 0 && n.children[0].segment != splitted[0] { - // several nodes exist, return the first one that is not nil - return n.children[0] - } - } - - // try to find the value in childre - for _, child := range n.children { - if isWildcard(child.segment) || child.segment == splitted[0] { - if splittedLen == 1 { - // This is the last value, do the check and return - if child.isLast { - // if isLast, that means we have already registered the endpoint - return child - } else { - // otherwise, take it as not registered - return nil - } - } - // More segments to check - return child.findRoute(splitted[1]) - } - } - - // nothing found in the children - return nil -} - -func (n *node) addRoute(uri string, handlers []ControllerHandler) error { - splitted := strings.SplitN(uri, "/", 2) - splittedLen := len(splitted) - isLast := splittedLen == 1 - - // try to find the value in childre - for _, child := range n.children { - if isWildcard(child.segment) || child.segment == splitted[0] { - if isLast { - // This is the last value, do the check and return - if child.isLast { - // if isLast, that means we have already registered the endpoint - return errors.New("node exists") - } else { - // otherwise, set the child - child.isLast = true - child.handlers = append(child.handlers, handlers...) - return nil - } - } - // More segments to check - return child.addRoute(splitted[1], handlers) - } - } - - // create a new node - new := newNode(splitted[0]) - new.parent = n - if isLast { - // this is the end - new.handlers = append(new.handlers, handlers...) - new.isLast = true - n.children = append(n.children, new) - return nil - } - // continue - new.isLast = false - err := new.addRoute(splitted[1], handlers) - if err != nil { - return err - } - n.children = append(n.children, new) - return nil -} diff --git a/go.mod b/go.mod index 972a4de..b2e1ffb 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,36 @@ module git.vinchent.xyz/vinchent/go-web go 1.22.5 -require github.com/spf13/cast v1.7.0 +require github.com/gin-gonic/gin v1.10.0 + +require ( + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 9103036..8a7b933 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,96 @@ -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/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +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/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/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +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.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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/handlers.go b/handlers.go index 6dd3244..f066d83 100644 --- a/handlers.go +++ b/handlers.go @@ -1,85 +1,29 @@ package main import ( - "context" - "log" "net/http" "time" - "git.vinchent.xyz/vinchent/go-web/framework" + "github.com/gin-gonic/gin" ) -// func FooControllerHandler(ctx *framework.Context) error { -// return ctx.WriteJSON(http.StatusOK, map[string]any{ -// "code": 0, -// }) -// } - -func FooControllerHandler(ctx *framework.Context) error { - durationCtx, cancel := context.WithTimeout(ctx.BaseContext(), time.Duration(1*time.Second)) - defer cancel() - - finish := make(chan struct{}, 1) - panicChan := make(chan interface{}, 1) - - // some long task - go func() { - // Deal with the panic during the work - defer func() { - if p := recover(); p != nil { - panicChan <- p - } - }() - // do the business - time.Sleep(10 * time.Second) - ctx.WriteJSON(http.StatusOK, "ok") - finish <- struct{}{} - }() - - select { - case <-panicChan: - // Protect ResponseWriter for concurrently writing from different - // goroutines if there are any - ctx.WriterMux().Lock() - defer ctx.WriterMux().Unlock() - log.Println("panicked") - ctx.WriteJSON(http.StatusInternalServerError, "panicked") - case <-finish: - log.Println("finished") - case <-durationCtx.Done(): - ctx.WriterMux().Lock() - defer ctx.WriterMux().Unlock() - log.Println("Timeout") - ctx.WriteJSON(http.StatusInternalServerError, "time out") - ctx.SetHasTimeout() - } - - return nil -} - -func UserLoginController(ctx *framework.Context) error { +func UserLoginController(ctx *gin.Context) { time.Sleep(10 * time.Second) - ctx.WriteJSON(http.StatusOK, "ok") - return nil + ctx.JSON(http.StatusOK, "ok") } -func SubjectDelController(ctx *framework.Context) error { - ctx.WriteJSON(http.StatusAccepted, "deleted") - return nil +func SubjectDelController(ctx *gin.Context) { + ctx.JSON(http.StatusAccepted, "deleted") } -func SubjectUpdateController(ctx *framework.Context) error { - ctx.WriteJSON(http.StatusAccepted, "updated") - return nil +func SubjectUpdateController(ctx *gin.Context) { + ctx.JSON(http.StatusAccepted, "updated") } -func SubjectGetController(ctx *framework.Context) error { - ctx.WriteJSON(http.StatusAccepted, "got") - log.Println(ctx.ParamInt("ID", 0)) - return nil +func SubjectGetController(ctx *gin.Context) { + ctx.JSON(http.StatusAccepted, "got") } -func SubjectListController(ctx *framework.Context) error { - ctx.WriteJSON(http.StatusAccepted, "list") - return nil +func SubjectListController(ctx *gin.Context) { + ctx.JSON(http.StatusAccepted, "list") } diff --git a/main.go b/main.go index 66a7371..0aedda4 100644 --- a/main.go +++ b/main.go @@ -10,11 +10,11 @@ import ( "syscall" "time" - "git.vinchent.xyz/vinchent/go-web/framework" + "github.com/gin-gonic/gin" ) func main() { - core := framework.NewCore() + core := gin.New() registerRouter(core) server := &http.Server{ Addr: ":8080", diff --git a/routes.go b/routes.go index aab7cb7..a51a0b7 100644 --- a/routes.go +++ b/routes.go @@ -1,21 +1,21 @@ package main import ( - "git.vinchent.xyz/vinchent/go-web/framework" "git.vinchent.xyz/vinchent/go-web/framework/middleware" + "github.com/gin-gonic/gin" ) -func registerRouter(core *framework.Core) { +func registerRouter(core *gin.Engine) { core.Use(middleware.Test1(), middleware.Test2()) - core.Get("/user/login", UserLoginController) + core.GET("/user/login", UserLoginController) subjectApi := core.Group("/subject") { subjectApi.Use(middleware.Test3()) - subjectApi.Delete("/:id", SubjectDelController) - subjectApi.Put("/:id", SubjectUpdateController) - subjectApi.Get("/:id", SubjectGetController) - subjectApi.Get("/:id/test", SubjectGetController) - subjectApi.Get("/list/all", SubjectListController) + subjectApi.DELETE("/:id", SubjectDelController) + subjectApi.PUT("/:id", SubjectUpdateController) + subjectApi.GET("/:id", SubjectGetController) + subjectApi.GET("/:id/test", SubjectGetController) + subjectApi.GET("/list/all", SubjectListController) } }