1
0
mirror of https://github.com/astaxie/beego.git synced 2025-07-11 20:01:03 +00:00

47 Commits

Author SHA1 Message Date
90999717dd Merge branch 'develop' 2016-12-05 23:14:26 +08:00
a20ccde90d beego 1.7.2 2016-12-05 22:58:29 +08:00
d548ddeb5b Merge pull request #2267 from centos-ren/develop
Fix :supervisor work dir
2016-12-05 22:57:38 +08:00
de99ac5da5 Merge pull request #2197 from OlegFX/master
policies implementation
2016-12-05 22:45:07 +08:00
e2d9d34c75 Merge pull request #2272 from szyhf/Fix#2263
Another Fix to #2263
2016-12-05 22:40:54 +08:00
bdb525831d Merge pull request #2293 from DusanKasan/develop
resolves #2291, introduces AndNotCond/OrNotCond to orm.Condition
2016-12-05 22:40:35 +08:00
22dba90ec4 Merge pull request #2305 from songtianyi/fixtypo-Getfiles
typo fix in comments, Getfiles-->GetFiles
2016-12-05 15:09:57 +08:00
7af6dad58e typo fix in comments, Getfiles-->GetFiles 2016-12-05 12:49:21 +08:00
5af26446ec Merge pull request #2299 from amrfaissal/fix-2294
Fix xml.GetSection() function which causes a panic
2016-11-30 14:25:14 +08:00
39d40ba8fa This fixes #2294 2016-11-29 14:55:57 +01:00
5bc3e30653 Added ToString method which converts values of any type to string 2016-11-29 14:55:56 +01:00
34e2b26b99 resolves #2291, introduces AndNotCond/OrNotCond to orm.Condition 2016-11-28 09:49:06 +01:00
24015e9ace Merge pull request #2285 from cat2neat/fix-bodyclose
Fix http body may not be closed
2016-11-20 19:48:09 +08:00
9ff88f0b35 Fix http body may not be closed 2016-11-20 05:16:52 +00:00
27c59a8017 Merge pull request #2278 from mgenov/err_fix
beego/session: return proper error when session is not found
2016-11-13 16:03:55 +08:00
a74ebaa1eb beego/session: return proper error when session is not found
Parent errs was returned instead of err which is returned from the last
statement.
2016-11-13 10:01:29 +02:00
b5c29d6143 Fix #2263
Update db_mysql.go instead of db.go, in order to avoid affect other database.
2016-11-08 13:39:31 +08:00
0a822209c8 Fix :supervisor work dir 2016-11-08 11:14:48 +08:00
3baac14095 Merge pull request #2257 from xiaoqiang0/develop
swagger: add 'Default' for Parameter
2016-11-07 22:48:06 +08:00
ef3655877a swagger: add 'Default' for Parameter 2016-11-03 22:59:23 +08:00
5a8c40710c Merge pull request #2228 from Hepri/develop
Rework getFlatParams for time.Time
2016-10-30 10:48:49 +08:00
b635af5a8c Merge pull request #2248 from smacker/RouterPattern-in-ctx
Add RouterPattern to context.Input
2016-10-30 10:48:15 +08:00
683e6856ef Add RouterPattern to context.Input
Right now beego adds this param only in dev mode, but I noticed that it's very useful to have in prod environment to.
My current use case - filter that sends logs in newrelic. Pattern there will help a lot to generate correct transaction name.
2016-10-28 10:44:16 +07:00
8beefc8bfd Fixed bug when all "time.Time" params in raw sql queries formatted as time 2016-10-17 21:51:31 +05:00
e430de3307 Merge pull request #2223 from tailnode/develop
修复windows上app.conf中include其他配置文件时找不到文件的BUG
2016-10-14 21:11:25 +08:00
2b442e842e fix path issue in windows 2016-10-14 16:52:03 +08:00
41633900da Merge pull request #2218 from WatchtowerSecurity/NameFix
Name fix
2016-10-13 21:24:11 +08:00
aaf6e775d6 Merge pull request #2216 from WatchtowerSecurity/httponlyfix
HTTPOnly Configurable
2016-10-13 21:19:28 +08:00
2f6fc3f62b Merge pull request #2213 from axyu/develop
fix log func call depth bug
2016-10-13 21:18:54 +08:00
84310b1652 Merge pull request #2205 from jirfag/master
add GetUint(8|16|32|64) to controller
2016-10-13 21:17:54 +08:00
5ceac1dd04 string convert int fail use math/big fix #756 2016-10-12 15:04:31 +08:00
51b31c6cb0 Changed the name to match. 2016-10-11 11:06:52 -05:00
5488a5bbd7 Forgot to fix it here 2016-10-11 11:06:22 -05:00
33f7f46670 Updated the name 2016-10-11 10:49:19 -05:00
3c05eafbc4 HTTP Only Configurable 2016-10-10 09:50:34 -05:00
dfa9e03980 fix log func call depth bug 2016-10-09 15:19:21 +08:00
53e996d4c3 Merge pull request #2211 from ihippik/patch-2
Default values
2016-10-08 17:52:07 +08:00
b151a9616e Default values 2016-10-08 11:28:47 +03:00
1effb6ce30 add GetUint(8|16|32|64) to controller 2016-10-02 07:42:06 +00:00
0be05eb47c policies implementation 2016-09-28 21:21:07 +03:00
1090ca0154 Merge pull request #2190 from szyhf/develop
fix to advice in #2187
2016-09-28 20:17:41 +08:00
a328584238 Merge pull request #2193 from LyricTian/develop
httplib add CheckRedirect
2016-09-28 20:16:48 +08:00
f19ad3fdd3 httplib add CheckRedirect 2016-09-28 18:04:51 +08:00
836be7ab9a fix to advice in #2187
Clear BConfig.Log.Outputs's default "console" while user has set "LogOutputs" in config file.
2016-09-28 16:20:41 +08:00
bba04dd864 Merge pull request #2184 from rahal/master
Emailer : Should use config Username only if no From is provided.
2016-09-28 07:56:12 +08:00
9499b3eb90 Emailer : Should use config Username only if no From is provided.
Should fix the case where Username is not an email.
2016-09-27 16:21:41 +01:00
9f6bbe3c51 add foundation link 2016-09-23 14:16:16 +08:00
25 changed files with 469 additions and 52 deletions

View File

@ -2,6 +2,7 @@
[![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego) [![Build Status](https://travis-ci.org/astaxie/beego.svg?branch=master)](https://travis-ci.org/astaxie/beego)
[![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego)
[![Foundation](https://img.shields.io/badge/Golang-Foundation-green.svg)](http://golangfoundation.org)
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.

View File

@ -65,6 +65,7 @@ func oldMap() map[string]interface{} {
m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime m["BConfig.WebConfig.Session.SessionCookieLifeTime"] = BConfig.WebConfig.Session.SessionCookieLifeTime
m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie m["BConfig.WebConfig.Session.SessionAutoSetCookie"] = BConfig.WebConfig.Session.SessionAutoSetCookie
m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain
m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly
m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs
m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum
m["BConfig.Log.Outputs"] = BConfig.Log.Outputs m["BConfig.Log.Outputs"] = BConfig.Log.Outputs

View File

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

View File

@ -94,9 +94,10 @@ type SessionConfig struct {
SessionCookieLifeTime int SessionCookieLifeTime int
SessionAutoSetCookie bool SessionAutoSetCookie bool
SessionDomain string SessionDomain string
EnableSidInHttpHeader bool // enable store/get the sessionId into/from http headers SessionDisableHTTPOnly bool // used to allow for cross domain cookies/javascript cookies.
SessionNameInHttpHeader string SessionEnableSidInHTTPHeader bool // enable store/get the sessionId into/from http headers
EnableSidInUrlQuery bool // enable get the sessionId from Url Query params SessionNameInHTTPHeader string
SessionEnableSidInURLQuery bool // enable get the sessionId from Url Query params
} }
// LogConfig holds Log related config // LogConfig holds Log related config
@ -143,6 +144,9 @@ func init() {
if err = parseConfig(appConfigPath); err != nil { if err = parseConfig(appConfigPath); err != nil {
panic(err) panic(err)
} }
if err = os.Chdir(AppPath); err != nil {
panic(err)
}
} }
func recoverPanic(ctx *context.Context) { func recoverPanic(ctx *context.Context) {
@ -226,12 +230,13 @@ func newBConfig() *Config {
SessionName: "beegosessionID", SessionName: "beegosessionID",
SessionGCMaxLifetime: 3600, SessionGCMaxLifetime: 3600,
SessionProviderConfig: "", SessionProviderConfig: "",
SessionDisableHTTPOnly: false,
SessionCookieLifeTime: 0, //set cookie default is the browser life SessionCookieLifeTime: 0, //set cookie default is the browser life
SessionAutoSetCookie: true, SessionAutoSetCookie: true,
SessionDomain: "", SessionDomain: "",
EnableSidInHttpHeader: false, // enable store/get the sessionId into/from http headers SessionEnableSidInHTTPHeader: false, // enable store/get the sessionId into/from http headers
SessionNameInHttpHeader: "Beegosessionid", SessionNameInHTTPHeader: "Beegosessionid",
EnableSidInUrlQuery: false, // enable get the sessionId from Url Query params SessionEnableSidInURLQuery: false, // enable get the sessionId from Url Query params
}, },
}, },
Log: LogConfig{ Log: LogConfig{
@ -294,6 +299,10 @@ func assignConfig(ac config.Configer) error {
} }
if lo := ac.String("LogOutputs"); lo != "" { if lo := ac.String("LogOutputs"); lo != "" {
// if lo is not nil or empty
// means user has set his own LogOutputs
// clear the default setting to BConfig.Log.Outputs
BConfig.Log.Outputs = make(map[string]string)
los := strings.Split(lo, ";") los := strings.Split(lo, ";")
for _, v := range los { for _, v := range los {
if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 { if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 {

View File

@ -43,6 +43,8 @@ package config
import ( import (
"fmt" "fmt"
"os" "os"
"reflect"
"time"
) )
// Configer defines how to get and set value from configuration raw data. // Configer defines how to get and set value from configuration raw data.
@ -204,3 +206,37 @@ func ParseBool(val interface{}) (value bool, err error) {
} }
return false, fmt.Errorf("parsing <nil>: invalid syntax") return false, fmt.Errorf("parsing <nil>: invalid syntax")
} }
// ToString converts values of any type to string.
func ToString(x interface{}) string {
switch y := x.(type) {
// Handle dates with special logic
// This needs to come above the fmt.Stringer
// test since time.Time's have a .String()
// method
case time.Time:
return y.Format("A Monday")
// Handle type string
case string:
return y
// Handle type with .String() method
case fmt.Stringer:
return y.String()
// Handle type with .Error() method
case error:
return y.Error()
}
// Handle named string type
if v := reflect.ValueOf(x); v.Kind() == reflect.String {
return v.String()
}
// Fallback to fmt package for anything else like numeric types
return fmt.Sprint(x)
}

View File

@ -23,6 +23,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path" "path"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -132,8 +133,8 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
includefiles := strings.Fields(key) includefiles := strings.Fields(key)
if includefiles[0] == "include" && len(includefiles) == 2 { if includefiles[0] == "include" && len(includefiles) == 2 {
otherfile := strings.Trim(includefiles[1], "\"") otherfile := strings.Trim(includefiles[1], "\"")
if !path.IsAbs(otherfile) { if !filepath.IsAbs(otherfile) {
otherfile = path.Join(path.Dir(name), otherfile) otherfile = filepath.Join(filepath.Dir(name), otherfile)
} }
i, err := ini.parseFile(otherfile) i, err := ini.parseFile(otherfile)
if err != nil { if err != nil {

View File

@ -193,10 +193,14 @@ func (c *ConfigContainer) DefaultStrings(key string, defaultval []string) []stri
// GetSection returns map for the given section // GetSection returns map for the given section
func (c *ConfigContainer) GetSection(section string) (map[string]string, error) { func (c *ConfigContainer) GetSection(section string) (map[string]string, error) {
if v, ok := c.data[section]; ok { if v, ok := c.data[section].(map[string]interface{}); ok {
return v.(map[string]string), nil mapstr := make(map[string]string)
for k, val := range v {
mapstr[k] = config.ToString(val)
} }
return nil, errors.New("not exist setction") return mapstr, nil
}
return nil, fmt.Errorf("section '%s' not found", section)
} }
// SaveConfigFile save the config into file // SaveConfigFile save the config into file

View File

@ -37,6 +37,10 @@ func TestXML(t *testing.T) {
<copyrequestbody>true</copyrequestbody> <copyrequestbody>true</copyrequestbody>
<path1>${GOPATH}</path1> <path1>${GOPATH}</path1>
<path2>${GOPATH||/home/go}</path2> <path2>${GOPATH||/home/go}</path2>
<mysection>
<id>1</id>
<name>MySection</name>
</mysection>
</config> </config>
` `
keyValue = map[string]interface{}{ keyValue = map[string]interface{}{
@ -65,11 +69,22 @@ func TestXML(t *testing.T) {
} }
f.Close() f.Close()
defer os.Remove("testxml.conf") defer os.Remove("testxml.conf")
xmlconf, err := config.NewConfig("xml", "testxml.conf") xmlconf, err := config.NewConfig("xml", "testxml.conf")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
var xmlsection map[string]string
xmlsection, err = xmlconf.GetSection("mysection")
if err != nil {
t.Fatal(err)
}
if len(xmlsection) == 0 {
t.Error("section should not be empty")
}
for k, v := range keyValue { for k, v := range keyValue {
var ( var (

View File

@ -399,6 +399,16 @@ func (c *Controller) GetInt8(key string, def ...int8) (int8, error) {
return int8(i64), err return int8(i64), err
} }
// GetUint8 return input as an uint8 or the default value while it's present and input is blank
func (c *Controller) GetUint8(key string, def ...uint8) (uint8, error) {
strv := c.Ctx.Input.Query(key)
if len(strv) == 0 && len(def) > 0 {
return def[0], nil
}
u64, err := strconv.ParseUint(strv, 10, 8)
return uint8(u64), err
}
// GetInt16 returns input as an int16 or the default value while it's present and input is blank // GetInt16 returns input as an int16 or the default value while it's present and input is blank
func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
strv := c.Ctx.Input.Query(key) strv := c.Ctx.Input.Query(key)
@ -409,6 +419,16 @@ func (c *Controller) GetInt16(key string, def ...int16) (int16, error) {
return int16(i64), err return int16(i64), err
} }
// GetUint16 returns input as an uint16 or the default value while it's present and input is blank
func (c *Controller) GetUint16(key string, def ...uint16) (uint16, error) {
strv := c.Ctx.Input.Query(key)
if len(strv) == 0 && len(def) > 0 {
return def[0], nil
}
u64, err := strconv.ParseUint(strv, 10, 16)
return uint16(u64), err
}
// GetInt32 returns input as an int32 or the default value while it's present and input is blank // GetInt32 returns input as an int32 or the default value while it's present and input is blank
func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
strv := c.Ctx.Input.Query(key) strv := c.Ctx.Input.Query(key)
@ -419,6 +439,16 @@ func (c *Controller) GetInt32(key string, def ...int32) (int32, error) {
return int32(i64), err return int32(i64), err
} }
// GetUint32 returns input as an uint32 or the default value while it's present and input is blank
func (c *Controller) GetUint32(key string, def ...uint32) (uint32, error) {
strv := c.Ctx.Input.Query(key)
if len(strv) == 0 && len(def) > 0 {
return def[0], nil
}
u64, err := strconv.ParseUint(strv, 10, 32)
return uint32(u64), err
}
// GetInt64 returns input value as int64 or the default value while it's present and input is blank. // GetInt64 returns input value as int64 or the default value while it's present and input is blank.
func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
strv := c.Ctx.Input.Query(key) strv := c.Ctx.Input.Query(key)
@ -428,6 +458,15 @@ func (c *Controller) GetInt64(key string, def ...int64) (int64, error) {
return strconv.ParseInt(strv, 10, 64) return strconv.ParseInt(strv, 10, 64)
} }
// GetUint64 returns input value as uint64 or the default value while it's present and input is blank.
func (c *Controller) GetUint64(key string, def ...uint64) (uint64, error) {
strv := c.Ctx.Input.Query(key)
if len(strv) == 0 && len(def) > 0 {
return def[0], nil
}
return strconv.ParseUint(strv, 10, 64)
}
// GetBool returns input value as bool or the default value while it's present and input is blank. // GetBool returns input value as bool or the default value while it's present and input is blank.
func (c *Controller) GetBool(key string, def ...bool) (bool, error) { func (c *Controller) GetBool(key string, def ...bool) (bool, error) {
strv := c.Ctx.Input.Query(key) strv := c.Ctx.Input.Query(key)
@ -453,7 +492,7 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader,
} }
// GetFiles return multi-upload files // GetFiles return multi-upload files
// files, err:=c.Getfiles("myfiles") // files, err:=c.GetFiles("myfiles")
// if err != nil { // if err != nil {
// http.Error(w, err.Error(), http.StatusNoContent) // http.Error(w, err.Error(), http.StatusNoContent)
// return // return

View File

@ -15,6 +15,8 @@
package beego package beego
import ( import (
"math"
"strconv"
"testing" "testing"
"github.com/astaxie/beego/context" "github.com/astaxie/beego/context"
@ -75,3 +77,47 @@ func TestGetInt64(t *testing.T) {
t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val) t.Errorf("TestGeetInt64 expect 40,get %T,%v", val, val)
} }
} }
func TestGetUint8(t *testing.T) {
i := context.NewInput()
i.SetParam("age", strconv.FormatUint(math.MaxUint8, 10))
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetUint8("age")
if val != math.MaxUint8 {
t.Errorf("TestGetUint8 expect %v,get %T,%v", math.MaxUint8, val, val)
}
}
func TestGetUint16(t *testing.T) {
i := context.NewInput()
i.SetParam("age", strconv.FormatUint(math.MaxUint16, 10))
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetUint16("age")
if val != math.MaxUint16 {
t.Errorf("TestGetUint16 expect %v,get %T,%v", math.MaxUint16, val, val)
}
}
func TestGetUint32(t *testing.T) {
i := context.NewInput()
i.SetParam("age", strconv.FormatUint(math.MaxUint32, 10))
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetUint32("age")
if val != math.MaxUint32 {
t.Errorf("TestGetUint32 expect %v,get %T,%v", math.MaxUint32, val, val)
}
}
func TestGetUint64(t *testing.T) {
i := context.NewInput()
i.SetParam("age", strconv.FormatUint(math.MaxUint64, 10))
ctx := &context.Context{Input: i}
ctrlr := Controller{Ctx: ctx}
val, _ := ctrlr.GetUint64("age")
if val != math.MaxUint64 {
t.Errorf("TestGetUint64 expect %v,get %T,%v", uint64(math.MaxUint64), val, val)
}
}

View File

@ -53,10 +53,11 @@ func registerSession() error {
conf.Secure = BConfig.Listen.EnableHTTPS conf.Secure = BConfig.Listen.EnableHTTPS
conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime
conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig) conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig)
conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly
conf.Domain = BConfig.WebConfig.Session.SessionDomain conf.Domain = BConfig.WebConfig.Session.SessionDomain
conf.EnableSidInHttpHeader = BConfig.WebConfig.Session.EnableSidInHttpHeader conf.EnableSidInHttpHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
conf.SessionNameInHttpHeader = BConfig.WebConfig.Session.SessionNameInHttpHeader conf.SessionNameInHttpHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
conf.EnableSidInUrlQuery = BConfig.WebConfig.Session.EnableSidInUrlQuery conf.EnableSidInUrlQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
} else { } else {
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil { if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
return err return err

View File

@ -136,6 +136,7 @@ type BeegoHTTPSettings struct {
TLSClientConfig *tls.Config TLSClientConfig *tls.Config
Proxy func(*http.Request) (*url.URL, error) Proxy func(*http.Request) (*url.URL, error)
Transport http.RoundTripper Transport http.RoundTripper
CheckRedirect func(req *http.Request, via []*http.Request) error
EnableCookie bool EnableCookie bool
Gzip bool Gzip bool
DumpBody bool DumpBody bool
@ -265,6 +266,15 @@ func (b *BeegoHTTPRequest) SetProxy(proxy func(*http.Request) (*url.URL, error))
return b return b
} }
// SetCheckRedirect specifies the policy for handling redirects.
//
// If CheckRedirect is nil, the Client uses its default policy,
// which is to stop after 10 consecutive requests.
func (b *BeegoHTTPRequest) SetCheckRedirect(redirect func(req *http.Request, via []*http.Request) error) *BeegoHTTPRequest {
b.setting.CheckRedirect = redirect
return b
}
// Param adds query param in to request. // Param adds query param in to request.
// params build query string as ?key1=value1&key2=value2... // params build query string as ?key1=value1&key2=value2...
func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest { func (b *BeegoHTTPRequest) Param(key, value string) *BeegoHTTPRequest {
@ -446,6 +456,10 @@ func (b *BeegoHTTPRequest) DoRequest() (*http.Response, error) {
b.req.Header.Set("User-Agent", b.setting.UserAgent) b.req.Header.Set("User-Agent", b.setting.UserAgent)
} }
if b.setting.CheckRedirect != nil {
client.CheckRedirect = b.setting.CheckRedirect
}
if b.setting.ShowDebug { if b.setting.ShowDebug {
dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody) dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody)
if err != nil { if err != nil {

View File

@ -56,10 +56,10 @@ func (s *JLWriter) WriteMsg(when time.Time, msg string, level int) error {
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode) return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode)
} }
resp.Body.Close()
return nil return nil
} }

View File

@ -380,7 +380,10 @@ func (bl *BeeLogger) Error(format string, v ...interface{}) {
// Warning Log WARNING level message. // Warning Log WARNING level message.
func (bl *BeeLogger) Warning(format string, v ...interface{}) { func (bl *BeeLogger) Warning(format string, v ...interface{}) {
bl.Warn(format, v...) if LevelWarn > bl.level {
return
}
bl.writeMsg(LevelWarn, format, v...)
} }
// Notice Log NOTICE level message. // Notice Log NOTICE level message.
@ -393,7 +396,10 @@ func (bl *BeeLogger) Notice(format string, v ...interface{}) {
// Informational Log INFORMATIONAL level message. // Informational Log INFORMATIONAL level message.
func (bl *BeeLogger) Informational(format string, v ...interface{}) { func (bl *BeeLogger) Informational(format string, v ...interface{}) {
bl.Info(format, v...) if LevelInfo > bl.level {
return
}
bl.writeMsg(LevelInfo, format, v...)
} }
// Debug Log DEBUG level message. // Debug Log DEBUG level message.
@ -425,7 +431,10 @@ func (bl *BeeLogger) Info(format string, v ...interface{}) {
// Trace Log TRACE level message. // Trace Log TRACE level message.
// compatibility alias for Debug() // compatibility alias for Debug()
func (bl *BeeLogger) Trace(format string, v ...interface{}) { func (bl *BeeLogger) Trace(format string, v ...interface{}) {
bl.Debug(format, v...) if LevelDebug > bl.level {
return
}
bl.writeMsg(LevelDebug, format, v...)
} }
// Flush flush all chan data. // Flush flush all chan data.

View File

@ -44,10 +44,10 @@ func (s *SLACKWriter) WriteMsg(when time.Time, msg string, level int) error {
if err != nil { if err != nil {
return err return err
} }
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode) return fmt.Errorf("Post webhook failed %s %d", resp.Status, resp.StatusCode)
} }
resp.Body.Close()
return nil return nil
} }

View File

@ -16,6 +16,8 @@ package orm
import ( import (
"fmt" "fmt"
"reflect"
"strings"
) )
// mysql operators. // mysql operators.
@ -96,6 +98,83 @@ func (d *dbBaseMysql) IndexExists(db dbQuerier, table string, name string) bool
return cnt > 0 return cnt > 0
} }
// InsertOrUpdate a row
// If your primary key or unique column conflict will update
// If no will insert
// Add "`" for mysql sql building
func (d *dbBaseMysql) InsertOrUpdate(q dbQuerier, mi *modelInfo, ind reflect.Value, a *alias, args ...string) (int64, error) {
iouStr := ""
argsMap := map[string]string{}
iouStr = "ON DUPLICATE KEY UPDATE"
//Get on the key-value pairs
for _, v := range args {
kv := strings.Split(v, "=")
if len(kv) == 2 {
argsMap[strings.ToLower(kv[0])] = kv[1]
}
}
isMulti := false
names := make([]string, 0, len(mi.fields.dbcols)-1)
Q := d.ins.TableQuote()
values, _, err := d.collectValues(mi, ind, mi.fields.dbcols, true, true, &names, a.TZ)
if err != nil {
return 0, err
}
marks := make([]string, len(names))
updateValues := make([]interface{}, 0)
updates := make([]string, len(names))
for i, v := range names {
marks[i] = "?"
valueStr := argsMap[strings.ToLower(v)]
if valueStr != "" {
updates[i] = "`" + v + "`" + "=" + valueStr
} else {
updates[i] = "`" + v + "`" + "=?"
updateValues = append(updateValues, values[i])
}
}
values = append(values, updateValues...)
sep := fmt.Sprintf("%s, %s", Q, Q)
qmarks := strings.Join(marks, ", ")
qupdates := strings.Join(updates, ", ")
columns := strings.Join(names, sep)
multi := len(values) / len(names)
if isMulti {
qmarks = strings.Repeat(qmarks+"), (", multi-1) + qmarks
}
//conflitValue maybe is a int,can`t use fmt.Sprintf
query := fmt.Sprintf("INSERT INTO %s%s%s (%s%s%s) VALUES (%s) %s "+qupdates, Q, mi.table, Q, Q, columns, Q, qmarks, iouStr)
d.ins.ReplaceMarks(&query)
if isMulti || !d.ins.HasReturningID(mi, &query) {
res, err := q.Exec(query, values...)
if err == nil {
if isMulti {
return res.RowsAffected()
}
return res.LastInsertId()
}
return 0, err
}
row := q.QueryRow(query, values...)
var id int64
err = row.Scan(&id)
return id, err
}
// create new mysql dbBaser. // create new mysql dbBaser.
func newdbBaseMysql() dbBaser { func newdbBaseMysql() dbBaser {
b := new(dbBaseMysql) b := new(dbBaseMysql)

View File

@ -147,8 +147,10 @@ outFor:
arg = v.In(tz).Format(formatDate) arg = v.In(tz).Format(formatDate)
} else if fi != nil && fi.fieldType == TypeDateTimeField { } else if fi != nil && fi.fieldType == TypeDateTimeField {
arg = v.In(tz).Format(formatDateTime) arg = v.In(tz).Format(formatDateTime)
} else { } else if fi != nil && fi.fieldType == TypeTimeField {
arg = v.In(tz).Format(formatTime) arg = v.In(tz).Format(formatTime)
} else {
arg = v.In(tz).Format(formatDateTime)
} }
} else { } else {
typ := val.Type() typ := val.Type()

View File

@ -75,6 +75,19 @@ func (c *Condition) AndCond(cond *Condition) *Condition {
return c return c
} }
// AndNotCond combine a AND NOT condition to current condition
func (c *Condition) AndNotCond(cond *Condition) *Condition {
c = c.clone()
if c == cond {
panic(fmt.Errorf("<Condition.AndNotCond> cannot use self as sub cond"))
}
if cond != nil {
c.params = append(c.params, condValue{cond: cond, isCond: true, isNot: true})
}
return c
}
// Or add OR expression to condition // Or add OR expression to condition
func (c Condition) Or(expr string, args ...interface{}) *Condition { func (c Condition) Or(expr string, args ...interface{}) *Condition {
if expr == "" || len(args) == 0 { if expr == "" || len(args) == 0 {
@ -105,6 +118,19 @@ func (c *Condition) OrCond(cond *Condition) *Condition {
return c return c
} }
// OrNotCond combine a OR NOT condition to current condition
func (c *Condition) OrNotCond(cond *Condition) *Condition {
c = c.clone()
if c == cond {
panic(fmt.Errorf("<Condition.OrNotCond> cannot use self as sub cond"))
}
if cond != nil {
c.params = append(c.params, condValue{cond: cond, isCond: true, isNot: true, isOr: true})
}
return c
}
// IsEmpty check the condition arguments are empty or not. // IsEmpty check the condition arguments are empty or not.
func (c *Condition) IsEmpty() bool { func (c *Condition) IsEmpty() bool {
return len(c.params) == 0 return len(c.params) == 0

View File

@ -909,6 +909,16 @@ func TestSetCond(t *testing.T) {
num, err = qs.SetCond(cond2).Count() num, err = qs.SetCond(cond2).Count()
throwFail(t, err) throwFail(t, err)
throwFail(t, AssertIs(num, 2)) throwFail(t, AssertIs(num, 2))
cond3 := cond.AndNotCond(cond.And("status__in", 1))
num, err = qs.SetCond(cond3).Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 2))
cond4 := cond.And("user_name", "slene").OrNotCond(cond.And("user_name", "slene"))
num, err = qs.SetCond(cond4).Count()
throwFail(t, err)
throwFail(t, AssertIs(num, 3))
} }
func TestLimit(t *testing.T) { func TestLimit(t *testing.T) {

View File

@ -16,6 +16,7 @@ package orm
import ( import (
"fmt" "fmt"
"math/big"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
@ -87,6 +88,14 @@ func (f StrTo) Int32() (int32, error) {
// Int64 string to int64 // Int64 string to int64
func (f StrTo) Int64() (int64, error) { func (f StrTo) Int64() (int64, error) {
v, err := strconv.ParseInt(f.String(), 10, 64) v, err := strconv.ParseInt(f.String(), 10, 64)
if err != nil {
i := new(big.Int)
ni, ok := i.SetString(f.String(), 10) // octal
if !ok {
return int64(v), err
}
return ni.Int64(), nil
}
return int64(v), err return int64(v), err
} }
@ -117,6 +126,14 @@ func (f StrTo) Uint32() (uint32, error) {
// Uint64 string to uint64 // Uint64 string to uint64
func (f StrTo) Uint64() (uint64, error) { func (f StrTo) Uint64() (uint64, error) {
v, err := strconv.ParseUint(f.String(), 10, 64) v, err := strconv.ParseUint(f.String(), 10, 64)
if err != nil {
i := new(big.Int)
ni, ok := i.SetString(f.String(), 10)
if !ok {
return uint64(v), err
}
return ni.Uint64(), nil
}
return uint64(v), err return uint64(v), err
} }

97
policy.go Normal file
View File

@ -0,0 +1,97 @@
// Copyright 2016 beego authors. 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 beego
import (
"strings"
"github.com/astaxie/beego/context"
)
// PolicyFunc defines a policy function which is invoked before the controller handler is executed.
type PolicyFunc func(*context.Context)
// FindRouter Find Router info for URL
func (p *ControllerRegister) FindPolicy(cont *context.Context) []PolicyFunc {
var urlPath = cont.Input.URL()
if !BConfig.RouterCaseSensitive {
urlPath = strings.ToLower(urlPath)
}
httpMethod := cont.Input.Method()
isWildcard := false
// Find policy for current method
t, ok := p.policies[httpMethod]
// If not found - find policy for whole controller
if !ok {
t, ok = p.policies["*"]
isWildcard = true
}
if ok {
runObjects := t.Match(urlPath, cont)
if r, ok := runObjects.([]PolicyFunc); ok {
return r
} else if !isWildcard {
// If no policies found and we checked not for "*" method - try to find it
t, ok = p.policies["*"]
if ok {
runObjects = t.Match(urlPath, cont)
if r, ok = runObjects.([]PolicyFunc); ok {
return r
}
}
}
}
return nil
}
func (p *ControllerRegister) addToPolicy(method, pattern string, r ...PolicyFunc) {
method = strings.ToUpper(method)
p.enablePolicy = true
if !BConfig.RouterCaseSensitive {
pattern = strings.ToLower(pattern)
}
if t, ok := p.policies[method]; ok {
t.AddRouter(pattern, r)
} else {
t := NewTree()
t.AddRouter(pattern, r)
p.policies[method] = t
}
}
// Register new policy in beego
func Policy(pattern, method string, policy ...PolicyFunc) {
BeeApp.Handlers.addToPolicy(method, pattern, policy...)
}
// Find policies and execute if were found
func (p *ControllerRegister) execPolicy(cont *context.Context, urlPath string) (started bool) {
if !p.enablePolicy {
return false
}
// Find Policy for method
policyList := p.FindPolicy(cont)
if len(policyList) > 0 {
// Run policies
for _, runPolicy := range policyList {
runPolicy(cont)
if cont.ResponseWriter.Started {
return true
}
}
return false
}
return false
}

View File

@ -114,6 +114,8 @@ type controllerInfo struct {
// ControllerRegister containers registered router rules, controller handlers and filters. // ControllerRegister containers registered router rules, controller handlers and filters.
type ControllerRegister struct { type ControllerRegister struct {
routers map[string]*Tree routers map[string]*Tree
enablePolicy bool
policies map[string]*Tree
enableFilter bool enableFilter bool
filters [FinishRouter + 1][]*FilterRouter filters [FinishRouter + 1][]*FilterRouter
pool sync.Pool pool sync.Pool
@ -123,6 +125,7 @@ type ControllerRegister struct {
func NewControllerRegister() *ControllerRegister { func NewControllerRegister() *ControllerRegister {
cr := &ControllerRegister{ cr := &ControllerRegister{
routers: make(map[string]*Tree), routers: make(map[string]*Tree),
policies: make(map[string]*Tree),
} }
cr.pool.New = func() interface{} { cr.pool.New = func() interface{} {
return beecontext.NewContext() return beecontext.NewContext()
@ -711,11 +714,14 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
goto Admin goto Admin
} }
//check policies
if p.execPolicy(context, urlPath) {
goto Admin
}
if routerInfo != nil { if routerInfo != nil {
if BConfig.RunMode == DEV {
//store router pattern into context //store router pattern into context
context.Input.SetData("RouterPattern", routerInfo.pattern) context.Input.SetData("RouterPattern", routerInfo.pattern)
}
if routerInfo.routerType == routerTypeRESTFul { if routerInfo.routerType == routerTypeRESTFul {
if _, ok := routerInfo.methods[r.Method]; ok { if _, ok := routerInfo.methods[r.Method]; ok {
isRunnable = true isRunnable = true

View File

@ -86,6 +86,7 @@ type ManagerConfig struct {
EnableSetCookie bool `json:"enableSetCookie,omitempty"` EnableSetCookie bool `json:"enableSetCookie,omitempty"`
Gclifetime int64 `json:"gclifetime"` Gclifetime int64 `json:"gclifetime"`
Maxlifetime int64 `json:"maxLifetime"` Maxlifetime int64 `json:"maxLifetime"`
DisableHTTPOnly bool `json:"disableHTTPOnly"`
Secure bool `json:"secure"` Secure bool `json:"secure"`
CookieLifeTime int `json:"cookieLifeTime"` CookieLifeTime int `json:"cookieLifeTime"`
ProviderConfig string `json:"providerConfig"` ProviderConfig string `json:"providerConfig"`
@ -206,13 +207,13 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
session, err = manager.provider.SessionRead(sid) session, err = manager.provider.SessionRead(sid)
if err != nil { if err != nil {
return nil, errs return nil, err
} }
cookie := &http.Cookie{ cookie := &http.Cookie{
Name: manager.config.CookieName, Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: !manager.config.DisableHTTPOnly,
Secure: manager.isSecure(r), Secure: manager.isSecure(r),
Domain: manager.config.Domain, Domain: manager.config.Domain,
} }
@ -251,7 +252,7 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
expiration := time.Now() expiration := time.Now()
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{Name: manager.config.CookieName,
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: !manager.config.DisableHTTPOnly,
Expires: expiration, Expires: expiration,
MaxAge: -1} MaxAge: -1}
@ -285,7 +286,7 @@ func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Reque
cookie = &http.Cookie{Name: manager.config.CookieName, cookie = &http.Cookie{Name: manager.config.CookieName,
Value: url.QueryEscape(sid), Value: url.QueryEscape(sid),
Path: "/", Path: "/",
HttpOnly: true, HttpOnly: !manager.config.DisableHTTPOnly,
Secure: manager.isSecure(r), Secure: manager.isSecure(r),
Domain: manager.config.Domain, Domain: manager.config.Domain,
} }

View File

@ -97,6 +97,7 @@ type Parameter struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"`
Items *ParameterItems `json:"items,omitempty" yaml:"items,omitempty"` Items *ParameterItems `json:"items,omitempty" yaml:"items,omitempty"`
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
} }
// A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body". // A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body".
@ -126,7 +127,7 @@ type Propertie struct {
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"` Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"` Title string `json:"title,omitempty" yaml:"title,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default string `json:"default,omitempty" yaml:"default,omitempty"` Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"` Type string `json:"type,omitempty" yaml:"type,omitempty"`
Example string `json:"example,omitempty" yaml:"example,omitempty"` Example string `json:"example,omitempty" yaml:"example,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"` Required []string `json:"required,omitempty" yaml:"required,omitempty"`

View File

@ -232,14 +232,16 @@ func (e *Email) Send() error {
return errors.New("Must specify at least one To address") return errors.New("Must specify at least one To address")
} }
from, err := mail.ParseAddress(e.Username) // Use the username if no From is provided
if len(e.From) == 0 {
e.From = e.Username
}
from, err := mail.ParseAddress(e.From)
if err != nil { if err != nil {
return err return err
} }
if len(e.From) == 0 {
e.From = e.Username
}
// use mail's RFC 2047 to encode any string // use mail's RFC 2047 to encode any string
e.Subject = qEncode("utf-8", e.Subject) e.Subject = qEncode("utf-8", e.Subject)