diff --git a/framework/context.go b/framework/context.go index 8991be3..c702404 100644 --- a/framework/context.go +++ b/framework/context.go @@ -6,8 +6,6 @@ import ( "errors" "io" "net/http" - "net/url" - "strconv" "sync" "time" ) @@ -24,6 +22,8 @@ type Context struct { // current handler index index int + params map[string]any + hasTimeout bool writerMux *sync.Mutex } @@ -123,128 +123,6 @@ func (ctx *Context) SetHandlers(handlers []ControllerHandler) { // }}} // {{{ Implements request functions -// {{{ Request URI - -// QueryInt gets an int value from the query request -func (ctx *Context) QueryInt(key string, defval int) (int, error) { - params, err := ctx.QueryAll() - if err != nil { - return defval, err - } - if vals, ok := params[key]; ok { - len := len(vals) - if len > 0 { - intval, err := strconv.Atoi(vals[len-1]) // return the last elem - if err != nil { - return defval, err - } - return intval, nil - } - } - return defval, errors.New("key not found") -} - -// QueryString gets a string value from the query request -func (ctx *Context) QueryString(key string, defval string) (string, error) { - params, err := ctx.QueryAll() - if err != nil { - return defval, err - } - if vals, ok := params[key]; ok { - len := len(vals) - if len > 0 { - return vals[len-1], nil // return the last elem - } - } - return defval, errors.New("key not found") -} - -// QueryArray gets an array of string values from the query request -func (ctx *Context) QueryArray(key string, defval []string) ([]string, error) { - params, err := ctx.QueryAll() - if err != nil { - return defval, err - } - if vals, ok := params[key]; ok { - return vals, nil // return the last elem - } - return defval, errors.New("key not found") -} - -// QueryAll returns all queries in a request URL -func (ctx *Context) QueryAll() (url.Values, error) { - if ctx.request != nil { - return map[string][]string(ctx.request.URL.Query()), nil - } - return url.Values{}, errors.New("missing request in the context") -} - -// }}} -// {{{ Post form - -// FormInt gets an int value from the submitted form -func (ctx *Context) FormInt(key string, defval int) (int, error) { - vals, err := ctx.FormAll() - if err != nil { - return defval, err - } - - valStrs, ok := vals[key] - if !ok { - return defval, errors.New("key not found") - } - - valInt, err := strconv.Atoi(valStrs[0]) // Get the first one as result - if err != nil { - return defval, err - } - - return valInt, nil -} - -// FormString gets a string value from the submitted form -func (ctx *Context) FormString(key string, defval string) (string, error) { - vals, err := ctx.FormAll() - if err != nil { - return defval, err - } - - valStrs, ok := vals[key] - if !ok { - return defval, errors.New("key not found") - } - - return valStrs[0], nil -} - -// FormArray gets an array of string values from the submitted form -func (ctx *Context) FormArray(key string, defval []string) ([]string, error) { - vals, err := ctx.FormAll() - if err != nil { - return defval, err - } - - valStrs, ok := vals[key] - if !ok { - return defval, errors.New("key not found") - } - - return valStrs, nil -} - -// FormAll gets everything from the submitted form -func (ctx *Context) FormAll() (url.Values, error) { - if ctx.request != nil { - err := ctx.request.ParseForm() - if err != nil { - return url.Values{}, err - } - return ctx.request.PostForm, err - } - return url.Values{}, errors.New("missing request in the context") -} - -// }}} // {{{ application/json // ReadJSON binds the request JSON body to an object. diff --git a/framework/request.go b/framework/request.go new file mode 100644 index 0000000..17054fb --- /dev/null +++ b/framework/request.go @@ -0,0 +1,283 @@ +package framework + +import ( + "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 +} + +// }}} diff --git a/framework/response.go b/framework/response.go new file mode 100644 index 0000000..31c5fac --- /dev/null +++ b/framework/response.go @@ -0,0 +1,23 @@ +package framework + +type IResponse interface { + WriteJSON(obj any) IResponse + WriteJSONP(obj any) IResponse + WriteXML(obj any) IResponse + WriteHTML(template 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 +} diff --git a/go.mod b/go.mod index c692470..972a4de 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module git.vinchent.xyz/vinchent/go-web go 1.22.5 + +require github.com/spf13/cast v1.7.0 diff --git a/go.sum b/go.sum index e69de29..9103036 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,12 @@ +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/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/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=