ss/context.go

154 lines
2.8 KiB
Go

package bond
import (
"io"
"strings"
"encoding/json"
"net/http"
"net/url"
"fmt"
"vultras.su/core/bond/contents"
"vultras.su/core/bond/urlenc"
)
type Context struct {
R *Request
RelUrl *url.URL
W ResponseWriter
// Custom data to store stuff.
Data any
scanErr error
dec Decoder
}
func (c *Context) Method() ReqMethod {
return ReqMethod(c.R.Method)
}
// Set the reply status code.
func (c *Context) SetStatus(status Status) {
c.W.WriteHeader(int(status))
}
// Set the reply content type.
func (c *Context) SetContentType(typ contents.Type, opts ...contents.Option) {
ret := string(typ)
for _, opt := range opts {
kv := opt.KeyValue()
ret += ";"+kv[0]+"="+kv[1]
}
c.SetHeader("Content-Type", ret)
}
// Get the request content type.
func (c *Context) ContentType() contents.Type {
ret, ok := c.Header("Content-Type")
if !ok {
return ""
}
if len(ret) < 1 {
return ""
}
splits := strings.SplitN(ret[0], ";", 2)
if len(splits) < 0 {
return ""
}
return contents.Type(splits[0])
}
func (c *Context) SetHeader(k, v string) {
c.W.Header().Set(k, v)
}
func (c *Context) Header(name string) ([]string, bool) {
ret, ok := c.R.Header[name]
return ret, ok
}
// Closes the requests body after finishes scaning.
func (c *Context) Close() {
c.R.Body.Close()
}
// Scan the incoming value from body depending
// on the content type of the request into the
// structure or map[string] any.
func (c *Context) Scan(v any) bool {
if c.dec == nil {
typ := c.ContentType()
switch typ {
case contents.Json:
c.dec = json.NewDecoder(c.R.Body)
case contents.UrlEncoded:
body, _ := io.ReadAll(c.R.Body)
err := urlenc.Unmarshal(body, v)
if err != nil {
c.scanErr = err
}
return false
default:
c.scanErr = UnknownContentTypeErr
return false
}
}
err := c.dec.Decode(v)
if err != nil {
if err != io.EOF {
c.scanErr = err
}
return false
}
return true
}
func (c *Context) ScanErr() error {
return c.scanErr
}
func (c *Context) AbsPath() string {
return c.R.URL.Path
}
func (c *Context) PathPrefix() string {
pth := c.AbsPath()
rpth := c.Path()
return pth[:len(pth)-len(rpth)]
}
func (c *Context) Path() string {
return c.RelUrl.Path
}
func (c *Context) NotFound() {
http.NotFound(c.W, c.R)
}
func (c *Context) InternalServerError(err error) {
c.W.WriteHeader(http.StatusInternalServerError)
if err != nil {
c.Printf("500 Internal Server Error: %q", err)
} else {
}
}
func (c *Context) Print(v ...any) (int, error) {
return fmt.Print(v...)
}
func (c *Context) Printf(format string, v ...any) (int, error) {
return fmt.Fprintf(c.W, format, v...)
}
func (c *Context) Query() url.Values {
return c.R.URL.Query()
}
func (c *Context) Redirect(u string, status Status) {
pth := c.AbsPath()
if len(pth) > 0 && pth[len(pth)-1] != '/' {
pth += "/"
}
c.R.URL.Path = pth
http.Redirect(c.W, c.R, u, int(status))
}