1 Star 0 Fork 80

sdguet / faygo

forked from andeyalee / faygo 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
context_input.go 19.93 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
// Copyright 2016 HenryLee. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package faygo
import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"path/filepath"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/henrylee2cn/faygo/apiware"
)
// Regexes for checking the accept headers
// TODO make sure these are correct
var (
acceptsHTMLRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`)
acceptsXMLRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`)
acceptsJSONRegex = regexp.MustCompile(`(application/json)(?:,|$)`)
)
// Protocol returns request protocol name, such as HTTP/1.1 .
func (ctx *Context) Protocol() string {
return ctx.R.Proto
}
// URI returns full request url with query string, fragment.
func (ctx *Context) URI() string {
return ctx.R.RequestURI
}
// URL returns full request url with query string, fragment.
func (ctx *Context) URL() *url.URL {
return ctx.R.URL
}
// Path returns request url path (without query string, fragment).
func (ctx *Context) Path() string {
return ctx.R.URL.Path
}
// ModifyPath modifies the access path for the request.
func (ctx *Context) ModifyPath(p string) {
ctx.R.URL.Path = p
}
// Scheme returns request scheme as "http" or "https".
func (ctx *Context) Scheme() string {
if scheme := ctx.HeaderParam(HeaderXForwardedProto); scheme != "" {
return scheme
}
if ctx.R.URL.Scheme != "" {
return ctx.R.URL.Scheme
}
if ctx.R.TLS == nil {
return "http"
}
return "https"
}
// Site returns base site url as `scheme://domain:port` type.
func (ctx *Context) Site() string {
return ctx.Scheme() + "://" + ctx.R.Host
}
// Host returns a host:port string for this request,
// such as "www.example.com" or "www.example.com:8080".
func (ctx *Context) Host() string {
return ctx.R.Host
}
// Domain returns domain as `www.example.com` style.
func (ctx *Context) Domain() string {
return strings.Split(ctx.R.Host, ":")[0]
}
// Port returns the port number of request.
func (ctx *Context) Port() int {
parts := strings.Split(ctx.R.Host, ":")
if len(parts) == 1 {
return 80
}
port, _ := strconv.Atoi(parts[1])
return port
}
// IP gets just the ip from the most direct one client.
func (ctx *Context) IP() string {
ip := strings.Split(ctx.R.RemoteAddr, ":")[0]
if len(ip) == 0 {
return ""
}
if ip[0] != '[' {
return ip
}
return "127.0.0.1"
}
// RealIP returns request client ip.
// if in proxy, return first proxy id.
// if error, return 127.0.0.1.
func (ctx *Context) RealIP() string {
var ip = ctx.R.Header.Get(HeaderXRealIP)
if len(ip) > 0 {
return ip
}
ips := ctx.Proxy()
if len(ips) > 0 && ips[0] != "" {
ip = strings.Split(ips[0], ":")[0]
if len(ip) == 0 {
return ""
}
if ip[0] != '[' {
return ip
}
return "127.0.0.1"
}
return ctx.IP()
}
// Proxy returns proxy client ips slice.
func (ctx *Context) Proxy() []string {
if ips := ctx.HeaderParam(HeaderXForwardedFor); ips != "" {
return strings.Split(ips, ",")
}
return []string{}
}
// Referer returns http referer header.
func (ctx *Context) Referer() string {
return ctx.HeaderParam(HeaderReferer)
}
// Method returns http request method.
func (ctx *Context) Method() string {
return ctx.R.Method
}
// Is returns boolean of this request is on given method, such as Is("POST").
func (ctx *Context) Is(method string) bool {
return ctx.Method() == method
}
// IsGet Is this a GET method request?
func (ctx *Context) IsGet() bool {
return ctx.Is("GET")
}
// IsPost Is this a POST method request?
func (ctx *Context) IsPost() bool {
return ctx.Is("POST")
}
// IsHead Is this a Head method request?
func (ctx *Context) IsHead() bool {
return ctx.Is("HEAD")
}
// IsOptions Is this a OPTIONS method request?
func (ctx *Context) IsOptions() bool {
return ctx.Is("OPTIONS")
}
// IsPut Is this a PUT method request?
func (ctx *Context) IsPut() bool {
return ctx.Is("PUT")
}
// IsDelete Is this a DELETE method request?
func (ctx *Context) IsDelete() bool {
return ctx.Is("DELETE")
}
// IsPatch Is this a PATCH method request?
func (ctx *Context) IsPatch() bool {
return ctx.Is("PATCH")
}
// IsAjax returns boolean of this request is generated by ajax.
func (ctx *Context) IsAjax() bool {
return ctx.HeaderParam(HeaderXRequestedWith) == "XMLHttpRequest"
}
// IsSecure returns boolean of this request is in https.
func (ctx *Context) IsSecure() bool {
return ctx.Scheme() == "https"
}
// IsWebsocket returns boolean of this request is in webSocket.
func (ctx *Context) IsWebsocket() bool {
return ctx.HeaderParam(HeaderUpgrade) == "websocket"
}
// IsUpload returns boolean of whether file uploads in this request or not..
func (ctx *Context) IsUpload() bool {
return strings.Contains(ctx.HeaderParam(HeaderContentType), MIMEMultipartForm)
}
// AcceptHTML Checks if request accepts html response
func (ctx *Context) AcceptHTML() bool {
return acceptsHTMLRegex.MatchString(ctx.HeaderParam(HeaderAccept))
}
// AcceptXML Checks if request accepts xml response
func (ctx *Context) AcceptXML() bool {
return acceptsXMLRegex.MatchString(ctx.HeaderParam(HeaderAccept))
}
// AcceptJSON Checks if request accepts json response
func (ctx *Context) AcceptJSON() bool {
return acceptsJSONRegex.MatchString(ctx.HeaderParam(HeaderAccept))
}
// UserAgent returns request client user agent string.
func (ctx *Context) UserAgent() string {
return ctx.HeaderParam(HeaderUserAgent)
}
// Data returns the stored data in this context.
func (ctx *Context) Data(key interface{}) interface{} {
if v, ok := ctx.data[key]; ok {
return v
}
return nil
}
// HasData checks if the key exists in the context.
func (ctx *Context) HasData(key interface{}) bool {
_, ok := ctx.data[key]
return ok
}
// DataAll return the implicit data in the context
func (ctx *Context) DataAll() map[interface{}]interface{} {
return ctx.data
}
// SetData stores data with given key in this context.
// This data are only available in this context.
func (ctx *Context) SetData(key, val interface{}) {
ctx.data[key] = val
}
// Del delete data by key.
func (ctx *Context) Del(key interface{}) {
delete(ctx.data, key)
}
// Param returns the first value for the kinds of parameters.
// priority:
// path parameters > POST and PUT body parameters > URL query string values > header > cookie.Value.
//
// Param calls ParseMultipartForm and ParseForm if necessary and ignores
// any errors returned by these functions.
// If key is not present, Param returns the empty string.
// To access multiple values of the same key, call ParseForm and
// then inspect Request.Form directly.
func (ctx *Context) Param(key string) string {
var value string
value = ctx.BizParam(key)
if len(value) > 0 {
return value
}
value = ctx.R.Header.Get(key)
if len(value) > 0 {
return value
}
if cookie, _ := ctx.R.Cookie(key); cookie != nil {
return cookie.Value
}
return value
}
// BizParam returns the first value for the kinds of business parameters.
// priority:
// path parameters > POST and PUT body parameters > URL query string values.
//
// BizParam calls ParseMultipartForm and ParseForm if necessary and ignores
// any errors returned by these functions.
// If key is not present, BizParam returns the empty string.
// To access multiple values of the same key, call ParseForm and
// then inspect Request.Form directly.
func (ctx *Context) BizParam(key string) string {
var value string
value = ctx.pathParams.ByName(key)
if len(value) > 0 {
return value
}
if ctx.R.Form == nil {
ctx.R.ParseMultipartForm(ctx.frame.config.multipartMaxMemory)
}
return ctx.R.FormValue(key)
}
// BindBizParam data from ctx.BizParam(key) to dest
// like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=abc
// var id int ctx.BindBizParam(&id, "id") id ==123
// var isok bool ctx.BindBizParam(&isok, "isok") isok ==true
// var ft float64 ctx.BindBizParam(&ft, "ft") ft ==1.2
// ol := make([]int, 0, 2) ctx.BindBizParam(&ol, "ol") ol ==[1 2]
// ul := make([]string, 0, 2) ctx.BindBizParam(&ul, "ul") ul ==[str array]
// user struct{Name} ctx.BindBizParam(&user, "user") user == {Name:"abc"}
func (ctx *Context) BindBizParam(dest interface{}, key string) error {
return apiware.ConvertAssign(reflect.ValueOf(dest), ctx.BizParam(key))
}
// PathParam returns path param by key.
func (ctx *Context) PathParam(key string) string {
return ctx.pathParams.ByName(key)
}
// PathParamAll returns whole path parameters.
func (ctx *Context) PathParamAll() PathParams {
return ctx.pathParams
}
// ParseFormOrMulitForm parseForm or parseMultiForm based on Content-type
func (ctx *Context) ParseFormOrMulitForm(maxMemory int64) error {
// Parse the body depending on the content type.
if strings.Contains(ctx.HeaderParam(HeaderContentType), MIMEMultipartForm) {
if err := ctx.R.ParseMultipartForm(maxMemory); err != nil {
return errors.New("Error parsing request body:" + err.Error())
}
} else if err := ctx.R.ParseForm(); err != nil {
return errors.New("Error parsing request body:" + err.Error())
}
return nil
}
// FormParam returns the first value for the named component of the POST or PUT ruest body.
// URL query parameters and path parameters are ignored.
// FormParam calls ParseMultipartForm and ParseForm if necessary and ignores
// any errors returned by these functions.
// If key is not present, FormParam returns the empty string.
func (ctx *Context) FormParam(key string) string {
if ctx.R.PostForm == nil {
ctx.R.ParseMultipartForm(ctx.frame.config.multipartMaxMemory)
}
return ctx.R.PostFormValue(key)
}
// FormParams returns the form field value with "[]string" for the provided key.
func (ctx *Context) FormParams(key string) []string {
if ctx.R.PostForm == nil {
ctx.R.ParseMultipartForm(ctx.frame.config.multipartMaxMemory)
}
return ctx.R.PostForm[key]
}
// FormParamAll returns the parsed form data from POST, PATCH,
// or PUT body parameters.
func (ctx *Context) FormParamAll() url.Values {
if ctx.R.PostForm == nil {
ctx.R.ParseMultipartForm(ctx.frame.config.multipartMaxMemory)
}
return ctx.R.PostForm
}
const (
// TAG_PARAM param tag
TAG_PARAM = apiware.TAG_PARAM
)
// BindForm reads form data from request's body
func (ctx *Context) BindForm(structObject interface{}) error {
value := reflect.ValueOf(structObject)
if value.Kind() != reflect.Ptr {
return errors.New("`*Context.BindForm` accepts only parameter of struct pointer type")
}
value = reflect.Indirect(value)
if value.Kind() != reflect.Struct {
return errors.New("`*Context.BindForm` accepts only parameter of struct pointer type")
}
t := value.Type()
for i, count := 0, t.NumField(); i < count; i++ {
fieldT := t.Field(i)
if fieldT.Anonymous {
continue
}
var key = fieldT.Tag.Get(TAG_PARAM)
if key == "" {
key = MapParamName(fieldT.Name)
}
err := apiware.ConvertAssign(value.Field(i), ctx.FormParams(key)...)
if err != nil {
return err
}
}
return nil
}
// QueryParam gets the first query value associated with the given key.
// If there are no values associated with the key, QueryParam returns
// the empty string.
func (ctx *Context) QueryParam(key string) string {
if ctx.queryParams == nil {
ctx.queryParams = ctx.R.URL.Query()
}
return ctx.queryParams.Get(key)
}
// QueryParams returns the query param with "[]string".
func (ctx *Context) QueryParams(key string) []string {
if ctx.queryParams == nil {
ctx.queryParams = ctx.R.URL.Query()
}
return ctx.queryParams[key]
}
// QueryParamAll returns all query params.
func (ctx *Context) QueryParamAll() url.Values {
if ctx.queryParams == nil {
ctx.queryParams = ctx.R.URL.Query()
}
return ctx.queryParams
}
// HeaderParam gets the first header value associated with the given key.
// If there are no values associated with the key, HeaderParam returns
// the empty string.
func (ctx *Context) HeaderParam(key string) string {
return ctx.R.Header.Get(key)
}
// HeaderParamAll returns the whole ruest header.
func (ctx *Context) HeaderParamAll() http.Header {
return ctx.R.Header
}
// CookieParam returns request cookie item string by a given key.
// if non-existed, return empty string.
func (ctx *Context) CookieParam(key string) string {
cookie, err := ctx.R.Cookie(key)
if err != nil {
return ""
}
return cookie.Value
}
// SecureCookieParam Get secure cookie from request by a given key.
func (ctx *Context) SecureCookieParam(secret, key string) (string, bool) {
val := ctx.CookieParam(key)
if val == "" {
return "", false
}
parts := strings.SplitN(val, "|", 3)
if len(parts) != 3 {
return "", false
}
vs := parts[0]
timestamp := parts[1]
sig := parts[2]
h := hmac.New(sha1.New, []byte(secret))
fmt.Fprintf(h, "%s%s", vs, timestamp)
if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
return "", false
}
res, _ := base64.URLEncoding.DecodeString(vs)
return BytesToString(res), true
}
// FormFile returns the first file for the provided form key.
// FormFile calls ParseMultipartForm and ParseForm if necessary.
func (ctx *Context) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
return ctx.R.FormFile(key)
}
// HasFormFile returns if the file header for the provided form key is exist.
func (ctx *Context) HasFormFile(key string) bool {
if ctx.R.MultipartForm == nil {
ctx.R.ParseMultipartForm(ctx.frame.config.multipartMaxMemory)
}
if ctx.R.MultipartForm != nil && ctx.R.MultipartForm.File != nil {
if fhs := ctx.R.MultipartForm.File[key]; len(fhs) > 0 {
return true
}
}
return false
}
// SavedFileInfo for SaveFiles()
type SavedFileInfo struct {
Url string
Size int64
}
// SaveFile saves the uploaded file to global.UploadDir(),
// character "?" indicates that the original file name.
// for example newfname="a/?" -> global.UploadDir()/a/fname.
func (ctx *Context) SaveFile(key string, cover bool, newfname ...string) (savedFileInfo SavedFileInfo, err error) {
f, fh, err := ctx.R.FormFile(key)
if err != nil {
return
}
defer func() {
err2 := f.Close()
if err2 != nil && err == nil {
err = err2
}
}()
var filename string
if os.PathSeparator == '/' {
filename = filepath.Base(strings.Replace(fh.Filename, "\\", "/", -1))
} else {
filename = filepath.Base(strings.Replace(fh.Filename, "/", "\\", -1))
}
// Sets the full file name
var fullname string
if len(newfname) == 0 {
fullname = filepath.Join(UploadDir(), filename)
} else {
if strings.Contains(newfname[0], "?") {
fullname = filepath.Join(UploadDir(), strings.Replace(newfname[0], "?", filename, -1))
} else {
fname := strings.TrimRight(newfname[0], ".")
if filepath.Ext(fname) == "" {
fullname = filepath.Join(UploadDir(), fname+filepath.Ext(filename))
} else {
fullname = filepath.Join(UploadDir(), fname)
}
}
}
// Create the completion file path
p, _ := filepath.Split(fullname)
err = os.MkdirAll(p, 0777)
if err != nil {
return
}
// If the file with the same name exists, add the suffix of the serial number
idx := strings.LastIndex(fullname, filepath.Ext(fullname))
_fullname := fullname
for i := 2; FileExists(_fullname) && !cover; i++ {
_fullname = fmt.Sprintf("%s(%d)%s", fullname[:idx], i, fullname[idx:])
}
fullname = _fullname
// Create the URL of the file
savedFileInfo.Url = "/" + strings.Replace(fullname, `\`, `/`, -1)
// Save the file to local
f2, err := os.OpenFile(fullname, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return
}
savedFileInfo.Size, err = io.Copy(f2, f)
err3 := f2.Close()
if err3 != nil && err == nil {
err = err3
}
return
}
// SaveFiles saves the uploaded files to global.UploadDir(),
// it's similar to SaveFile, but for saving multiple files.
func (ctx *Context) SaveFiles(key string, cover bool, newfname ...string) (savedFileInfos []SavedFileInfo, err error) {
if !ctx.HasFormFile(key) {
err = errors.New("there are no file param: " + key)
return
}
files := ctx.R.MultipartForm.File[key]
hasFilename := len(newfname) > 0
filemap := map[string]int{}
for _, fh := range files {
var f multipart.File
f, err = fh.Open()
if err != nil {
return
}
defer func() {
err2 := f.Close()
if err2 != nil && err == nil {
err = err2
}
}()
var filename string
if os.PathSeparator == '/' {
filename = filepath.Base(strings.Replace(fh.Filename, "\\", "/", -1))
} else {
filename = filepath.Base(strings.Replace(fh.Filename, "/", "\\", -1))
}
// Sets the full file name
var fullname string
if !hasFilename {
fullname = filepath.Join(UploadDir(), filename)
} else {
if strings.Contains(newfname[0], "?") {
fullname = filepath.Join(UploadDir(), strings.Replace(newfname[0], "?", filename, -1))
} else {
fname := strings.TrimRight(newfname[0], ".")
if filepath.Ext(fname) == "" {
fullname = filepath.Join(UploadDir(), fname+filepath.Ext(filename))
} else {
fullname = filepath.Join(UploadDir(), fname)
}
}
}
// If the file with the same name exists, add the suffix of the serial number
idx := strings.LastIndex(fullname, filepath.Ext(fullname))
num := filemap[fullname]
_fullname := fullname
num++
if num >= 2 {
_fullname = fmt.Sprintf("%s(%d)%s", fullname[:idx], num, fullname[idx:])
}
for FileExists(_fullname) && !cover {
num++
_fullname = fmt.Sprintf("%s(%d)%s", fullname[:idx], num, fullname[idx:])
}
filemap[fullname] = num
fullname = _fullname
var info SavedFileInfo
// Create the URL of the file
info.Url = "/" + strings.Replace(fullname, `\`, `/`, -1)
// Save the file to local
var f2 *os.File
f2, err = os.OpenFile(fullname, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
// Create the completion file path
p, _ := filepath.Split(fullname)
err = os.MkdirAll(p, 0777)
if err != nil {
return
}
f2, err = os.OpenFile(fullname, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return
}
}
info.Size, err = io.Copy(f2, f)
err3 := f2.Close()
if err3 != nil && err == nil {
err = err3
return
}
savedFileInfos = append(savedFileInfos, info)
}
return
}
// BindJSON reads JSON from request's body
func (ctx *Context) BindJSON(jsonObject interface{}) error {
rawData, _ := ioutil.ReadAll(ctx.R.Body)
// check if jsonObject is already a pointer, if yes then pass as it's
if reflect.TypeOf(jsonObject).Kind() == reflect.Ptr {
err := json.Unmarshal(rawData, jsonObject)
if err != nil {
return err
}
}
// finally, if the jsonObject is not a pointer
return json.Unmarshal(rawData, &jsonObject)
}
// BindXML reads XML from request's body
func (ctx *Context) BindXML(xmlObject interface{}) error {
rawData, _ := ioutil.ReadAll(ctx.R.Body)
// check if xmlObject is already a pointer, if yes then pass as it's
if reflect.TypeOf(xmlObject).Kind() == reflect.Ptr {
err := xml.Unmarshal(rawData, xmlObject)
if err != nil {
return err
}
}
// finally, if the xmlObject is not a pointer
return xml.Unmarshal(rawData, &xmlObject)
}
// LimitedBodyBytes returns the raw request body data as bytes.
// Note:
// 1.limited by maximum length;
// 2.if frame.config.PrintBody==false and ctx.R.Body is readed, returns nil;
// 3.if ctx.IsUpload()==true and ctx.R.Body is readed, returns nil.
func (ctx *Context) LimitedBodyBytes() []byte {
if ctx.limitedRequestBody != nil {
return ctx.limitedRequestBody
}
if ctx.R.Body == nil {
ctx.limitedRequestBody = []byte{}
return ctx.limitedRequestBody
}
safe := &io.LimitedReader{R: ctx.R.Body, N: ctx.frame.config.multipartMaxMemory}
buf := bytes.NewBuffer(make([]byte, 0, bytes.MinRead))
buf.ReadFrom(safe)
ctx.limitedRequestBody = buf.Bytes()
ctx.R.Body = ioutil.NopCloser(io.MultiReader(buf, ctx.R.Body))
return ctx.limitedRequestBody
}
Go
1
https://gitee.com/sdguet/faygo.git
git@gitee.com:sdguet/faygo.git
sdguet
faygo
faygo
master

搜索帮助