1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/go-baa-baa

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
context.go 17 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
yanghengfei Отправлено 27.02.2020 11:17 3b7010f
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
package baa
import (
"bufio"
"bytes"
"encoding/xml"
"errors"
"fmt"
"html/template"
"io"
"mime/multipart"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
)
var (
// ErrJSONPayloadEmpty is returned when the JSON payload is empty.
ErrJSONPayloadEmpty = errors.New("JSON payload is empty")
// ErrXMLPayloadEmpty is returned when the XML payload is empty.
ErrXMLPayloadEmpty = errors.New("XML payload is empty")
)
const (
// defaultMaxMemory Maximum amount of memory to use when parsing a multipart form.
// Set this to whatever value you prefer; default is 32 MB.
defaultMaxMemory = 32 << 20 // 32 MB
// CharsetUTF8 ...
CharsetUTF8 = "charset=utf-8"
// MediaTypes
ApplicationJSON = "application/json"
ApplicationJSONCharsetUTF8 = ApplicationJSON + "; " + CharsetUTF8
ApplicationJavaScript = "application/javascript"
ApplicationJavaScriptCharsetUTF8 = ApplicationJavaScript + "; " + CharsetUTF8
ApplicationXML = "application/xml"
ApplicationXMLCharsetUTF8 = ApplicationXML + "; " + CharsetUTF8
ApplicationForm = "application/x-www-form-urlencoded"
ApplicationProtobuf = "application/protobuf"
TextHTML = "text/html"
TextHTMLCharsetUTF8 = TextHTML + "; " + CharsetUTF8
TextPlain = "text/plain"
TextPlainCharsetUTF8 = TextPlain + "; " + CharsetUTF8
MultipartForm = "multipart/form-data"
)
// Context provlider a HTTP context for baa
// context contains reqest, response, header, cookie and some content type.
type Context struct {
Req *http.Request
Resp *Response
baa *Baa
store map[string]interface{}
storeMutex sync.RWMutex // store rw lock
routeName string // route name
pNames []string // route params names
pValues []string // route params values
handlers []HandlerFunc // middleware handler and route match handler
hi int // handlers execute position
}
// NewContext create a http context
func NewContext(w http.ResponseWriter, r *http.Request, b *Baa) *Context {
c := new(Context)
c.Resp = NewResponse(w, b)
c.baa = b
c.pNames = make([]string, 0, 16)
c.pValues = make([]string, 0, 16)
c.handlers = make([]HandlerFunc, len(b.middleware), len(b.middleware)+3)
copy(c.handlers, b.middleware)
c.Reset(w, r)
return c
}
// RouteName return context matched route name
func (c *Context) RouteName() string {
return c.routeName
}
// Reset ...
func (c *Context) Reset(w http.ResponseWriter, r *http.Request) {
c.Resp.reset(w)
c.Req = r
c.hi = 0
c.handlers = c.handlers[:len(c.baa.middleware)]
c.routeName = ""
c.pNames = c.pNames[:0]
c.pValues = c.pValues[:0]
c.storeMutex.Lock()
c.store = nil
c.storeMutex.Unlock()
}
// Set store data in context
func (c *Context) Set(key string, v interface{}) {
c.storeMutex.Lock()
if c.store == nil {
c.store = make(map[string]interface{})
}
c.store[key] = v
c.storeMutex.Unlock()
}
// Get returns data from context
func (c *Context) Get(key string) interface{} {
c.storeMutex.RLock()
defer c.storeMutex.RUnlock()
if c.store == nil {
return nil
}
return c.store[key]
}
// Gets returns data map from content store
func (c *Context) Gets() map[string]interface{} {
c.storeMutex.RLock()
vals := make(map[string]interface{})
for k, v := range c.store {
vals[k] = v
}
c.storeMutex.RUnlock()
return vals
}
// SetParam read route param value from uri
func (c *Context) SetParam(name, value string) {
c.pNames = append(c.pNames, name)
c.pValues = append(c.pValues, value)
}
// Param get route param from context
func (c *Context) Param(name string) string {
for i := len(c.pNames) - 1; i >= 0; i-- {
if c.pNames[i] == name {
return c.pValues[i]
}
}
return ""
}
// Params returns route params from context
func (c *Context) Params() map[string]string {
m := make(map[string]string)
for i := 0; i < len(c.pNames); i++ {
m[c.pNames[i]] = c.pValues[i]
}
return m
}
// ParamInt get route param from context and format to int
func (c *Context) ParamInt(name string) int {
v, _ := strconv.Atoi(c.Param(name))
return v
}
// ParamInt32 get route param from context and format to int32
func (c *Context) ParamInt32(name string) int32 {
return int32(c.ParamInt64(name))
}
// ParamInt64 get route param from context and format to int64
func (c *Context) ParamInt64(name string) int64 {
v, _ := strconv.ParseInt(c.Param(name), 10, 64)
return v
}
// ParamFloat get route param from context and format to float64
func (c *Context) ParamFloat(name string) float64 {
v, _ := strconv.ParseFloat(c.Param(name), 64)
return v
}
// ParamBool get route param from context and format to bool
func (c *Context) ParamBool(name string) bool {
v, _ := strconv.ParseBool(c.Param(name))
return v
}
// Query get a param from http.Request.Form
func (c *Context) Query(name string) string {
c.ParseForm(0)
return c.Req.Form.Get(name)
}
// QueryTrim querys and trims spaces form parameter.
func (c *Context) QueryTrim(name string) string {
c.ParseForm(0)
return strings.TrimSpace(c.Req.Form.Get(name))
}
// QueryStrings get a group param from http.Request.Form and format to string slice
func (c *Context) QueryStrings(name string) []string {
c.ParseForm(0)
if v, ok := c.Req.Form[name]; ok {
return v
}
return []string{}
}
// QueryEscape returns escapred query result.
func (c *Context) QueryEscape(name string) string {
c.ParseForm(0)
return template.HTMLEscapeString(c.Req.Form.Get(name))
}
// QueryInt get a param from http.Request.Form and format to int
func (c *Context) QueryInt(name string) int {
c.ParseForm(0)
v, _ := strconv.Atoi(c.Req.Form.Get(name))
return v
}
// QueryInt32 get a param from http.Request.Form and format to int32
func (c *Context) QueryInt32(name string) int32 {
return int32(c.QueryInt64(name))
}
// QueryInt64 get a param from http.Request.Form and format to int64
func (c *Context) QueryInt64(name string) int64 {
c.ParseForm(0)
v, _ := strconv.ParseInt(c.Req.Form.Get(name), 10, 64)
return v
}
// QueryFloat get a param from http.Request.Form and format to float64
func (c *Context) QueryFloat(name string) float64 {
c.ParseForm(0)
v, _ := strconv.ParseFloat(c.Req.Form.Get(name), 64)
return v
}
// QueryBool get a param from http.Request.Form and format to bool
func (c *Context) QueryBool(name string) bool {
c.ParseForm(0)
v, _ := strconv.ParseBool(c.Req.Form.Get(name))
return v
}
// Querys return http.Request.URL queryString data
func (c *Context) Querys() map[string]interface{} {
params := make(map[string]interface{})
var newValues url.Values
if c.Req.URL != nil {
newValues, _ = url.ParseQuery(c.Req.URL.RawQuery)
}
for k, v := range newValues {
if len(v) > 1 {
params[k] = v
} else {
params[k] = v[0]
}
}
return params
}
// Posts return http.Request form data
func (c *Context) Posts() map[string]interface{} {
if err := c.ParseForm(0); err != nil {
return nil
}
params := make(map[string]interface{})
data := c.Req.PostForm
if len(data) == 0 && len(c.Req.Form) > 0 {
data = c.Req.Form
}
for k, v := range data {
if len(v) > 1 {
params[k] = v
} else {
params[k] = v[0]
}
}
return params
}
// QueryJSON decode json from http.Request.Body
func (c *Context) QueryJSON(v interface{}) error {
content, err := c.Body().Bytes()
if err != nil {
return err
}
if len(content) == 0 {
return ErrJSONPayloadEmpty
}
return Unmarshal(content, v)
}
// QueryXML decode xml from http.Request.Body
func (c *Context) QueryXML(v interface{}) error {
content, err := c.Body().Bytes()
if err != nil {
return err
}
if len(content) == 0 {
return ErrXMLPayloadEmpty
}
return xml.Unmarshal(content, v)
}
// GetFile returns information about user upload file by given form field name.
func (c *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, error) {
if err := c.ParseForm(0); err != nil {
return nil, nil, err
}
return c.Req.FormFile(name)
}
// SaveToFile reads a file from request by field name and saves to given path.
func (c *Context) SaveToFile(name, savePath string) error {
fr, _, err := c.GetFile(name)
if err != nil {
return err
}
defer fr.Close()
fw, err := os.OpenFile(savePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer fw.Close()
_, err = io.Copy(fw, fr)
return err
}
// Body get raw request body and return RequestBody
func (c *Context) Body() *RequestBody {
return NewRequestBody(c.Req.Body)
}
// SetCookie sets given cookie value to response header.
// full params example:
// SetCookie(<name>, <value>, <max age>, <path>, <domain>, <secure>, <http only>)
func (c *Context) SetCookie(name string, value string, others ...interface{}) {
cookie := http.Cookie{}
cookie.Name = name
cookie.Value = url.QueryEscape(value)
if len(others) > 0 {
switch v := others[0].(type) {
case int:
cookie.MaxAge = v
case int64:
cookie.MaxAge = int(v)
case int32:
cookie.MaxAge = int(v)
}
}
cookie.Path = "/"
if len(others) > 1 {
if v, ok := others[1].(string); ok && len(v) > 0 {
cookie.Path = v
}
}
if len(others) > 2 {
if v, ok := others[2].(string); ok && len(v) > 0 {
cookie.Domain = v
}
}
if len(others) > 3 {
switch v := others[3].(type) {
case bool:
cookie.Secure = v
default:
if others[3] != nil {
cookie.Secure = true
}
}
}
if len(others) > 4 {
if v, ok := others[4].(bool); ok && v {
cookie.HttpOnly = true
}
}
c.Resp.Header().Add("Set-Cookie", cookie.String())
}
// GetCookie returns given cookie value from request header.
func (c *Context) GetCookie(name string) string {
cookie, err := c.Req.Cookie(name)
if err != nil {
return ""
}
v, _ := url.QueryUnescape(cookie.Value)
return v
}
// GetCookieInt returns cookie result in int type.
func (c *Context) GetCookieInt(name string) int {
v, _ := strconv.Atoi(c.GetCookie(name))
return v
}
// GetCookieInt32 returns cookie result in int32 type.
func (c *Context) GetCookieInt32(name string) int32 {
return int32(c.GetCookieInt64(name))
}
// GetCookieInt64 returns cookie result in int64 type.
func (c *Context) GetCookieInt64(name string) int64 {
v, _ := strconv.ParseInt(c.GetCookie(name), 10, 64)
return v
}
// GetCookieFloat64 returns cookie result in float64 type.
func (c *Context) GetCookieFloat64(name string) float64 {
v, _ := strconv.ParseFloat(c.GetCookie(name), 64)
return v
}
// GetCookieBool returns cookie result in float64 type.
func (c *Context) GetCookieBool(name string) bool {
v, _ := strconv.ParseBool(c.GetCookie(name))
return v
}
// String write text by string
func (c *Context) String(code int, s string) {
c.Resp.Header().Set("Content-Type", TextPlainCharsetUTF8)
c.Resp.WriteHeader(code)
c.Resp.Write([]byte(s))
}
// Text write text by []byte
func (c *Context) Text(code int, s []byte) {
c.Resp.Header().Set("Content-Type", TextHTMLCharsetUTF8)
c.Resp.WriteHeader(code)
c.Resp.Write(s)
}
// JSON write data by json format
func (c *Context) JSON(code int, v interface{}) {
var re []byte
var err error
if c.baa.debug {
re, err = MarshalIndent(v, "", " ")
} else {
re, err = Marshal(v)
}
if err != nil {
c.Error(err)
return
}
c.Resp.Header().Set("Content-Type", ApplicationJSONCharsetUTF8)
c.Resp.WriteHeader(code)
c.Resp.Write(re)
}
// JSONString return string by Marshal interface
func (c *Context) JSONString(v interface{}) (string, error) {
var re []byte
var err error
if c.baa.debug {
re, err = MarshalIndent(v, "", " ")
} else {
re, err = Marshal(v)
}
if err != nil {
return "", err
}
return string(re), nil
}
// JSONP write data by jsonp format
func (c *Context) JSONP(code int, callback string, v interface{}) {
re, err := Marshal(v)
if err != nil {
c.Error(err)
return
}
c.Resp.Header().Set("Content-Type", ApplicationJavaScriptCharsetUTF8)
c.Resp.WriteHeader(code)
c.Resp.Write([]byte(callback + "("))
c.Resp.Write(re)
c.Resp.Write([]byte(");"))
}
// XML sends an XML response with status code.
func (c *Context) XML(code int, v interface{}) {
var re []byte
var err error
if c.baa.debug {
re, err = xml.MarshalIndent(v, "", " ")
} else {
re, err = xml.Marshal(v)
}
if err != nil {
c.Error(err)
return
}
c.Resp.Header().Set("Content-Type", ApplicationXMLCharsetUTF8)
c.Resp.WriteHeader(code)
c.Resp.Write([]byte(xml.Header))
c.Resp.Write(re)
}
// HTML write render data by html template engine use context.store
// it is a alias of c.Render
func (c *Context) HTML(code int, tpl string) {
c.Render(code, tpl)
}
// Render write render data by html template engine use context.store
func (c *Context) Render(code int, tpl string) {
re, err := c.Fetch(tpl)
if err != nil {
c.Error(err)
return
}
c.Resp.Header().Set("Content-Type", TextHTMLCharsetUTF8)
c.Resp.WriteHeader(code)
c.Resp.Write(re)
}
// Fetch render data by html template engine use context.store and returns data
func (c *Context) Fetch(tpl string) ([]byte, error) {
buf := new(bytes.Buffer)
if err := c.baa.Render().Render(buf, tpl, c.Gets()); err != nil {
return nil, err
}
// clear go template generated black lines
nbuf := new(bytes.Buffer)
r := bufio.NewReader(buf)
for {
line, _, err := r.ReadLine()
if err != nil {
break
}
clearLine := strings.TrimSpace(string(line))
if len(clearLine) == 0 {
continue
}
nbuf.Write(line)
nbuf.WriteRune('\n')
}
return nbuf.Bytes(), nil
}
// Redirect redirects the request using http.Redirect with status code.
func (c *Context) Redirect(code int, url string) error {
if code < http.StatusMultipleChoices || code > http.StatusTemporaryRedirect {
return fmt.Errorf("invalid redirect status code")
}
http.Redirect(c.Resp, c.Req, url, code)
return nil
}
// RemoteAddr returns more real IP address.
func (c *Context) RemoteAddr() string {
var addr string
var key string
key = "__ctx_remoteAddr"
if addr, ok := c.Get(key).(string); ok {
return addr
}
for _, k := range []string{"X-Forwarded-For", "Ali-Cdn-Real-Ip", "X-Real-IP"} {
if addr = c.Req.Header.Get(k); addr != "" {
if strings.Contains(addr, ",") {
addrs := strings.Split(addr, ",")
addr = addrs[0]
}
break
}
}
if addr == "" {
addr = c.Req.RemoteAddr
addr, _, _ = net.SplitHostPort(addr)
}
c.Set(key, addr)
return addr
}
// Referer returns http request Referer
func (c *Context) Referer() string {
return c.Req.Header.Get("Referer")
}
// UserAgent returns http request UserAgent
func (c *Context) UserAgent() string {
return c.Req.Header.Get("User-Agent")
}
// URL returns http request full url
func (c *Context) URL(hasQuery bool) string {
scheme := c.Req.URL.Scheme
host := c.Req.URL.Host
if scheme == "" {
if c.Req.TLS != nil {
scheme = "https"
} else {
scheme = "http"
}
}
if host == "" {
host = c.Req.Host
}
if len(host) > 0 {
if host[0] == ':' {
//
} else if host[0] == '/' {
scheme += ":"
} else {
scheme += "://"
}
} else {
scheme = ""
}
if hasQuery {
url := c.Req.RequestURI
if url == "" {
url = c.Req.URL.Path
if len(c.Req.URL.RawQuery) > 0 {
url += "?" + c.Req.URL.RawQuery
}
}
return scheme + host + url
}
return scheme + host + c.Req.URL.Path
}
// IsMobile returns if it is a mobile phone device request
func (c *Context) IsMobile() bool {
userAgent := c.UserAgent()
for _, v := range []string{"iPhone", "iPod", "Android"} {
if strings.Contains(userAgent, v) {
return true
}
}
return false
}
// IsAJAX returns if it is a ajax request
func (c *Context) IsAJAX() bool {
return c.Req.Header.Get("X-Requested-With") == "XMLHttpRequest"
}
// ParseForm parses a request body as multipart/form-data or
// parses the raw query from the URL and updates r.Form.
func (c *Context) ParseForm(maxSize int64) error {
if c.Req.Form != nil {
return nil
}
contentType := c.Req.Header.Get("Content-Type")
if (c.Req.Method == "POST" || c.Req.Method == "PUT") &&
len(contentType) > 0 && strings.Contains(contentType, MultipartForm) {
if maxSize == 0 {
maxSize = defaultMaxMemory
}
return c.Req.ParseMultipartForm(maxSize)
}
return c.Req.ParseForm()
}
// Next execute next handler
// handle middleware first, last execute route handler
// if something wrote to http, break chain and return
func (c *Context) Next() {
if c.hi >= len(c.handlers) {
return
}
if c.Resp.Wrote() {
if c.baa.Debug() {
c.baa.Logger().Println("Warning: content has been written, handle chain break.")
}
return
}
i := c.hi
c.hi++
if c.handlers[i] != nil {
c.handlers[i](c)
} else {
c.Next()
}
}
// Break break the handles chain and Immediate return
func (c *Context) Break() {
c.hi = len(c.handlers)
}
// Error invokes the registered HTTP error handler.
func (c *Context) Error(err error) {
c.baa.Error(err, c)
}
// NotFound invokes the registered HTTP NotFound handler.
func (c *Context) NotFound() {
c.baa.NotFound(c)
}
// Baa get app instance
func (c *Context) Baa() *Baa {
return c.baa
}
// DI get registered dependency injection service
func (c *Context) DI(name string) interface{} {
return c.baa.GetDI(name)
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/go-baa-baa.git
git@api.gitlife.ru:oschina-mirror/go-baa-baa.git
oschina-mirror
go-baa-baa
go-baa-baa
master