Merge branch 'develop'

This commit is contained in:
astaxie 2020-02-07 16:25:41 +08:00
commit 0cd80525e7
18 changed files with 99 additions and 33 deletions

View File

@ -1,7 +1,7 @@
language: go language: go
go: go:
- "1.11.x" - "1.13.x"
services: services:
- redis-server - redis-server
- mysql - mysql

View File

@ -4,8 +4,6 @@
beego is used for rapid development of RESTful APIs, web apps and backend services in Go. beego is used for rapid development of RESTful APIs, web apps and backend services in Go.
It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding.
Response time ranking: [web-frameworks](https://github.com/the-benchmarker/web-frameworks).
###### More info at [beego.me](http://beego.me). ###### More info at [beego.me](http://beego.me).
## Quick Start ## Quick Start
@ -56,6 +54,7 @@ Congratulations! You've just built your first **beego** app.
* [http://beego.me/community](http://beego.me/community) * [http://beego.me/community](http://beego.me/community)
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232) * Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
* QQ Group Group ID:523992905
## License ## License

View File

@ -23,7 +23,7 @@ import (
const ( const (
// VERSION represent beego web framework version. // VERSION represent beego web framework version.
VERSION = "1.12.0" VERSION = "1.12.1"
// DEV is for develop // DEV is for develop
DEV = "dev" DEV = "dev"

View File

@ -25,7 +25,7 @@ package context
import ( import (
"bufio" "bufio"
"crypto/hmac" "crypto/hmac"
"crypto/sha1" "crypto/sha256"
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
@ -123,7 +123,7 @@ func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
timestamp := parts[1] timestamp := parts[1]
sig := parts[2] sig := parts[2]
h := hmac.New(sha1.New, []byte(Secret)) h := hmac.New(sha256.New, []byte(Secret))
fmt.Fprintf(h, "%s%s", vs, timestamp) fmt.Fprintf(h, "%s%s", vs, timestamp)
if fmt.Sprintf("%02x", h.Sum(nil)) != sig { if fmt.Sprintf("%02x", h.Sum(nil)) != sig {
@ -137,7 +137,7 @@ func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) {
func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) {
vs := base64.URLEncoding.EncodeToString([]byte(value)) vs := base64.URLEncoding.EncodeToString([]byte(value))
timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
h := hmac.New(sha1.New, []byte(Secret)) h := hmac.New(sha256.New, []byte(Secret))
fmt.Fprintf(h, "%s%s", vs, timestamp) fmt.Fprintf(h, "%s%s", vs, timestamp)
sig := fmt.Sprintf("%02x", h.Sum(nil)) sig := fmt.Sprintf("%02x", h.Sum(nil))
cookie := strings.Join([]string{vs, timestamp, sig}, "|") cookie := strings.Join([]string{vs, timestamp, sig}, "|")
@ -169,11 +169,11 @@ func (ctx *Context) CheckXSRFCookie() bool {
token = ctx.Request.Header.Get("X-Csrftoken") token = ctx.Request.Header.Get("X-Csrftoken")
} }
if token == "" { if token == "" {
ctx.Abort(403, "'_xsrf' argument missing from POST") ctx.Abort(422, "422")
return false return false
} }
if ctx._xsrfToken != token { if ctx._xsrfToken != token {
ctx.Abort(403, "XSRF cookie does not match POST argument") ctx.Abort(417, "417")
return false return false
} }
return true return true

6
go.mod
View File

@ -29,11 +29,13 @@ require (
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c // indirect github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c // indirect
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b // indirect
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect golang.org/x/tools v0.0.0-20200117065230-39095c1d176c
gopkg.in/yaml.v2 v2.2.1 gopkg.in/yaml.v2 v2.2.1
) )
replace golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85 replace golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 => github.com/golang/crypto v0.0.0-20181127143415-eb0de9b17e85
replace gopkg.in/yaml.v2 v2.2.1 => github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d replace gopkg.in/yaml.v2 v2.2.1 => github.com/go-yaml/yaml v0.0.0-20180328195020-5420a8b6744d
go 1.13

12
go.sum
View File

@ -61,8 +61,20 @@ github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b h1:0Ve0/CCjiAiyKddUM
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc= github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI= golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI=
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c h1:FodBYPZKH5tAN2O60HlglMwXGAeV/4k+NKbli79M/2c=
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -47,9 +47,11 @@ import (
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"os" "os"
"path"
"strings" "strings"
"sync" "sync"
"time" "time"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -558,12 +560,6 @@ func (b *BeegoHTTPRequest) Bytes() ([]byte, error) {
// ToFile saves the body data in response to one file. // ToFile saves the body data in response to one file.
// it calls Response inner. // it calls Response inner.
func (b *BeegoHTTPRequest) ToFile(filename string) error { func (b *BeegoHTTPRequest) ToFile(filename string) error {
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
resp, err := b.getResponse() resp, err := b.getResponse()
if err != nil { if err != nil {
return err return err
@ -572,10 +568,35 @@ func (b *BeegoHTTPRequest) ToFile(filename string) error {
return nil return nil
} }
defer resp.Body.Close() defer resp.Body.Close()
err = pathExistAndMkdir(filename)
if err != nil {
return err
}
f, err := os.Create(filename)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(f, resp.Body) _, err = io.Copy(f, resp.Body)
return err return err
} }
//Check that the file directory exists, there is no automatically created
func pathExistAndMkdir(filename string) (err error) {
filename = path.Dir(filename)
_, err = os.Stat(filename)
if err == nil {
return nil
}
if os.IsNotExist(err) {
err = os.MkdirAll(filename, os.ModePerm)
if err == nil {
return nil
}
}
return err
}
// ToJSON returns the map that marshals from the body bytes as json in response . // ToJSON returns the map that marshals from the body bytes as json in response .
// it calls Response inner. // it calls Response inner.
func (b *BeegoHTTPRequest) ToJSON(v interface{}) error { func (b *BeegoHTTPRequest) ToJSON(v interface{}) error {

View File

@ -232,6 +232,20 @@ func TestToFile(t *testing.T) {
} }
} }
func TestToFileDir(t *testing.T) {
f := "./files/beego_testfile"
req := Get("http://httpbin.org/ip")
err := req.ToFile(f)
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll("./files")
b, err := ioutil.ReadFile(f)
if n := strings.Index(string(b), "origin"); n == -1 {
t.Fatal(err)
}
}
func TestHeader(t *testing.T) { func TestHeader(t *testing.T) {
req := Get("http://httpbin.org/headers") req := Get("http://httpbin.org/headers")
req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36") req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36")

View File

@ -198,7 +198,7 @@ func getDbCreateSQL(al *alias) (sqls []string, tableIndexes map[string][]dbIndex
column = strings.Replace(column, "%COL%", fi.column, -1) column = strings.Replace(column, "%COL%", fi.column, -1)
} }
if fi.description != "" { if fi.description != "" && al.Driver!=DRSqlite {
column += " " + fmt.Sprintf("COMMENT '%s'",fi.description) column += " " + fmt.Sprintf("COMMENT '%s'",fi.description)
} }

View File

@ -129,7 +129,7 @@ func (f StrTo) Uint16() (uint16, error) {
return uint16(v), err return uint16(v), err
} }
// Uint32 string to uint31 // Uint32 string to uint32
func (f StrTo) Uint32() (uint32, error) { func (f StrTo) Uint32() (uint32, error) {
v, err := strconv.ParseUint(f.String(), 10, 32) v, err := strconv.ParseUint(f.String(), 10, 32)
return uint32(v), err return uint32(v), err

View File

@ -773,7 +773,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
} else if routerInfo.routerType == routerTypeHandler { } else if routerInfo.routerType == routerTypeHandler {
isRunnable = true isRunnable = true
routerInfo.handler.ServeHTTP(rw, r) routerInfo.handler.ServeHTTP(context.ResponseWriter, context.Request)
} else { } else {
runRouter = routerInfo.controllerType runRouter = routerInfo.controllerType
methodParams = routerInfo.methodParams methodParams = routerInfo.methodParams

View File

@ -19,7 +19,7 @@ import (
"crypto/cipher" "crypto/cipher"
"crypto/hmac" "crypto/hmac"
"crypto/rand" "crypto/rand"
"crypto/sha1" "crypto/sha256"
"crypto/subtle" "crypto/subtle"
"encoding/base64" "encoding/base64"
"encoding/gob" "encoding/gob"
@ -129,7 +129,7 @@ func encodeCookie(block cipher.Block, hashKey, name string, value map[interface{
b = encode(b) b = encode(b)
// 3. Create MAC for "name|date|value". Extra pipe to be used later. // 3. Create MAC for "name|date|value". Extra pipe to be used later.
b = []byte(fmt.Sprintf("%s|%d|%s|", name, time.Now().UTC().Unix(), b)) b = []byte(fmt.Sprintf("%s|%d|%s|", name, time.Now().UTC().Unix(), b))
h := hmac.New(sha1.New, []byte(hashKey)) h := hmac.New(sha256.New, []byte(hashKey))
h.Write(b) h.Write(b)
sig := h.Sum(nil) sig := h.Sum(nil)
// Append mac, remove name. // Append mac, remove name.
@ -153,7 +153,7 @@ func decodeCookie(block cipher.Block, hashKey, name, value string, gcmaxlifetime
} }
b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...) b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...)
h := hmac.New(sha1.New, []byte(hashKey)) h := hmac.New(sha256.New, []byte(hashKey))
h.Write(b) h.Write(b)
sig := h.Sum(nil) sig := h.Sum(nil)
if len(sig) != len(parts[2]) || subtle.ConstantTimeCompare(sig, parts[2]) != 1 { if len(sig) != len(parts[2]) || subtle.ConstantTimeCompare(sig, parts[2]) != 1 {

View File

@ -270,7 +270,8 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
Path: "/", Path: "/",
HttpOnly: !manager.config.DisableHTTPOnly, HttpOnly: !manager.config.DisableHTTPOnly,
Expires: expiration, Expires: expiration,
MaxAge: -1} MaxAge: -1,
Domain: manager.config.Domain}
http.SetCookie(w, cookie) http.SetCookie(w, cookie)
} }

View File

@ -175,6 +175,7 @@ func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
if err != nil { if err != nil {
return return
} }
defer f.Close()
ct := mime.TypeByExtension(filepath.Ext(filename)) ct := mime.TypeByExtension(filepath.Ext(filename))
basename := path.Base(filename) basename := path.Base(filename)
return e.Attach(f, basename, ct, id) return e.Attach(f, basename, ct, id)

View File

@ -26,6 +26,8 @@ const (
// ValidTag struct tag // ValidTag struct tag
ValidTag = "valid" ValidTag = "valid"
LabelTag = "label"
wordsize = 32 << (^uint(0) >> 32 & 1) wordsize = 32 << (^uint(0) >> 32 & 1)
) )
@ -124,6 +126,7 @@ func isStructPtr(t reflect.Type) bool {
func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) { func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) {
tag := f.Tag.Get(ValidTag) tag := f.Tag.Get(ValidTag)
label := f.Tag.Get(LabelTag)
if len(tag) == 0 { if len(tag) == 0 {
return return
} }
@ -136,7 +139,7 @@ func getValidFuncs(f reflect.StructField) (vfs []ValidFunc, err error) {
if len(vfunc) == 0 { if len(vfunc) == 0 {
continue continue
} }
vf, err = parseFunc(vfunc, f.Name) vf, err = parseFunc(vfunc, f.Name, label)
if err != nil { if err != nil {
return return
} }
@ -168,7 +171,7 @@ func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) {
return return
} }
func parseFunc(vfunc, key string) (v ValidFunc, err error) { func parseFunc(vfunc, key string, label string) (v ValidFunc, err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
err = fmt.Errorf("%v", r) err = fmt.Errorf("%v", r)
@ -188,7 +191,7 @@ func parseFunc(vfunc, key string) (v ValidFunc, err error) {
err = fmt.Errorf("%s require %d parameters", vfunc, num) err = fmt.Errorf("%s require %d parameters", vfunc, num)
return return
} }
v = ValidFunc{vfunc, []interface{}{key + "." + vfunc}} v = ValidFunc{vfunc, []interface{}{key + "." + vfunc + "." + label}}
return return
} }
@ -210,7 +213,7 @@ func parseFunc(vfunc, key string) (v ValidFunc, err error) {
return return
} }
tParams, err := trim(name, key+"."+name, params) tParams, err := trim(name, key+"."+ name + "." + label, params)
if err != nil { if err != nil {
return return
} }

View File

@ -267,15 +267,16 @@ func (v *Validation) apply(chk Validator, obj interface{}) *Result {
key := chk.GetKey() key := chk.GetKey()
Name := key Name := key
Field := "" Field := ""
Label := ""
parts := strings.Split(key, ".") parts := strings.Split(key, ".")
if len(parts) == 2 { if len(parts) == 3 {
Field = parts[0] Field = parts[0]
Name = parts[1] Name = parts[1]
Label = parts[2]
} }
err := &Error{ err := &Error{
Message: chk.DefaultMessage(), Message: Label + chk.DefaultMessage(),
Key: key, Key: key,
Name: Name, Name: Name,
Field: Field, Field: Field,
@ -298,7 +299,7 @@ func (v *Validation) AddError(key, message string) {
Field := "" Field := ""
parts := strings.Split(key, ".") parts := strings.Split(key, ".")
if len(parts) == 2 { if len(parts) == 3 {
Field = parts[0] Field = parts[0]
Name = parts[1] Name = parts[1]
} }

View File

@ -280,6 +280,18 @@ func TestMobile(t *testing.T) {
if valid.Mobile("8617400008888", "mobile").Ok { if valid.Mobile("8617400008888", "mobile").Ok {
t.Error("\"8617400008888\" is a valid mobile phone number should be false") t.Error("\"8617400008888\" is a valid mobile phone number should be false")
} }
if !valid.Mobile("16200008888", "mobile").Ok {
t.Error("\"16200008888\" is a valid mobile phone number should be true")
}
if !valid.Mobile("16500008888", "mobile").Ok {
t.Error("\"16500008888\" is a valid mobile phone number should be true")
}
if !valid.Mobile("16600008888", "mobile").Ok {
t.Error("\"16600008888\" is a valid mobile phone number should be true")
}
if !valid.Mobile("16700008888", "mobile").Ok {
t.Error("\"16700008888\" is a valid mobile phone number should be true")
}
} }
func TestTel(t *testing.T) { func TestTel(t *testing.T) {

View File

@ -632,7 +632,7 @@ func (b Base64) GetLimitValue() interface{} {
} }
// just for chinese mobile phone number // just for chinese mobile phone number
var mobilePattern = regexp.MustCompile(`^((\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][01356789]|[4][579]))\d{8}$`) var mobilePattern = regexp.MustCompile(`^((\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][01356789]|[4][579]|[6][2567]))\d{8}$`)
// Mobile check struct // Mobile check struct
type Mobile struct { type Mobile struct {