Context: create a customized Context type.
To package the internal context and other common structs, especially http.Request and http.ResponseWriter. Provides helper functìons.
This commit is contained in:
parent
63d5d0dc59
commit
f804f175e0
254
framework/context.go
Normal file
254
framework/context.go
Normal file
@ -0,0 +1,254 @@
|
||||
package framework
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"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
|
||||
handler ControllerHandler
|
||||
|
||||
hasTimeout bool
|
||||
writerMux *sync.Mutex
|
||||
}
|
||||
|
||||
// NewContext create a new context
|
||||
func NewContext(w http.ResponseWriter, r *http.Request) *Context {
|
||||
return &Context{
|
||||
request: r,
|
||||
responseWriter: w,
|
||||
}
|
||||
}
|
||||
|
||||
// {{{ Basic functions
|
||||
|
||||
// WriterMux returns the writer mutex
|
||||
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.
|
||||
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)
|
||||
}
|
||||
|
||||
// }}}
|
||||
// {{{ 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.
|
||||
//
|
||||
// A pointer of obj should be passed.
|
||||
func (ctx *Context) ReadJSON(obj any) error {
|
||||
if ctx.request == nil {
|
||||
return errors.New("missing request in the context")
|
||||
}
|
||||
dec := json.NewDecoder(ctx.request.Body)
|
||||
err := dec.Decode(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = dec.Decode(&struct{}{})
|
||||
if err != io.EOF {
|
||||
return errors.New("body must have only a single JSON value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteJSON send back an object in JSON format with the status code
|
||||
func (ctx *Context) WriteJSON(status int, obj any) error {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctx.responseWriter.Header().Set("Content-type", "application/json")
|
||||
ctx.responseWriter.WriteHeader(status)
|
||||
_, err = ctx.responseWriter.Write(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// }}}
|
||||
// }}}
|
3
framework/controller.go
Normal file
3
framework/controller.go
Normal file
@ -0,0 +1,3 @@
|
||||
package framework
|
||||
|
||||
type ControllerHandler func(c *Context) error
|
Loading…
Reference in New Issue
Block a user