diff --git a/app.go b/app.go
new file mode 100644
index 00000000..67fea98c
--- /dev/null
+++ b/app.go
@@ -0,0 +1,108 @@
+package beego
+
+import (
+ "fmt"
+ "github.com/astaxie/beego/context"
+ "net"
+ "net/http"
+ "net/http/fcgi"
+ "time"
+)
+
+type FilterFunc func(*context.Context)
+
+type App struct {
+ Handlers *ControllerRegistor
+}
+
+// New returns a new PatternServeMux.
+func NewApp() *App {
+ cr := NewControllerRegistor()
+ app := &App{Handlers: cr}
+ return app
+}
+
+func (app *App) Run() {
+ addr := HttpAddr
+
+ if HttpPort != 0 {
+ addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
+ }
+ var (
+ err error
+ l net.Listener
+ )
+ if UseFcgi {
+ if HttpPort == 0 {
+ l, err = net.Listen("unix", addr)
+ } else {
+ l, err = net.Listen("tcp", addr)
+ }
+ if err != nil {
+ BeeLogger.Fatal("Listen: ", err)
+ }
+ err = fcgi.Serve(l, app.Handlers)
+ } else {
+ if EnableHotUpdate {
+ server := &http.Server{
+ Handler: app.Handlers,
+ ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
+ WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
+ }
+ laddr, err := net.ResolveTCPAddr("tcp", addr)
+ if nil != err {
+ BeeLogger.Fatal("ResolveTCPAddr:", err)
+ }
+ l, err = GetInitListner(laddr)
+ theStoppable = newStoppable(l)
+ err = server.Serve(theStoppable)
+ theStoppable.wg.Wait()
+ CloseSelf()
+ } else {
+ s := &http.Server{
+ Addr: addr,
+ Handler: app.Handlers,
+ ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
+ WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
+ }
+ if HttpTLS {
+ err = s.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
+ } else {
+ err = s.ListenAndServe()
+ }
+ }
+ }
+ if err != nil {
+ BeeLogger.Fatal("ListenAndServe: ", err)
+ }
+}
+
+func (app *App) Router(path string, c ControllerInterface, mappingMethods ...string) *App {
+ app.Handlers.Add(path, c, mappingMethods...)
+ return app
+}
+
+func (app *App) AutoRouter(c ControllerInterface) *App {
+ app.Handlers.AddAuto(c)
+ return app
+}
+
+func (app *App) Filter(pattern, action string, filter FilterFunc) *App {
+ app.Handlers.AddFilter(pattern, action, filter)
+ return app
+}
+
+func (app *App) SetViewsPath(path string) *App {
+ ViewsPath = path
+ return app
+}
+
+func (app *App) SetStaticPath(url string, path string) *App {
+ StaticDir[url] = path
+ return app
+}
+
+func (app *App) DelStaticPath(url string) *App {
+ delete(StaticDir, url)
+ return app
+}
diff --git a/beego.go b/beego.go
index df2c7d6d..6acf4ac1 100644
--- a/beego.go
+++ b/beego.go
@@ -1,221 +1,13 @@
package beego
import (
- "fmt"
"github.com/astaxie/beego/session"
- "html/template"
- "net"
"net/http"
- "net/http/fcgi"
- "os"
"path"
- "runtime"
- "time"
)
const VERSION = "0.9.0"
-var (
- BeeApp *App
- AppName string
- AppPath string
- AppConfigPath string
- StaticDir map[string]string
- TemplateCache map[string]*template.Template
- HttpAddr string
- HttpPort int
- RecoverPanic bool
- AutoRender bool
- PprofOn bool
- ViewsPath string
- RunMode string //"dev" or "prod"
- AppConfig *Config
- //related to session
- GlobalSessions *session.Manager //GlobalSessions
- SessionOn bool // whether auto start session,default is false
- SessionProvider string // default session provider memory mysql redis
- SessionName string // sessionName cookie's name
- SessionGCMaxLifetime int64 // session's gc maxlifetime
- SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo
- UseFcgi bool
- MaxMemory int64
- EnableGzip bool // enable gzip
- DirectoryIndex bool //enable DirectoryIndex default is false
- EnableHotUpdate bool //enable HotUpdate default is false
- HttpServerTimeOut int64 //set httpserver timeout
- ErrorsShow bool //set weather show errors
- XSRFKEY string //set XSRF
- EnableXSRF bool
- XSRFExpire int
- CopyRequestBody bool //When in raw application, You want to the reqeustbody
- TemplateLeft string
- TemplateRight string
-)
-
-func init() {
- os.Chdir(path.Dir(os.Args[0]))
- BeeApp = NewApp()
- AppPath, _ = os.Getwd()
- StaticDir = make(map[string]string)
- TemplateCache = make(map[string]*template.Template)
- HttpAddr = ""
- HttpPort = 8080
- AppName = "beego"
- RunMode = "dev" //default runmod
- AutoRender = true
- RecoverPanic = true
- PprofOn = false
- ViewsPath = "views"
- SessionOn = false
- SessionProvider = "memory"
- SessionName = "beegosessionID"
- SessionGCMaxLifetime = 3600
- SessionSavePath = ""
- UseFcgi = false
- MaxMemory = 1 << 26 //64MB
- EnableGzip = false
- StaticDir["/static"] = "static"
- AppConfigPath = path.Join(AppPath, "conf", "app.conf")
- HttpServerTimeOut = 0
- ErrorsShow = true
- XSRFKEY = "beegoxsrf"
- XSRFExpire = 60
- TemplateLeft = "{{"
- TemplateRight = "}}"
- ParseConfig()
- runtime.GOMAXPROCS(runtime.NumCPU())
-}
-
-type App struct {
- Handlers *ControllerRegistor
-}
-
-// New returns a new PatternServeMux.
-func NewApp() *App {
- cr := NewControllerRegistor()
- app := &App{Handlers: cr}
- return app
-}
-
-func (app *App) Run() {
- addr := HttpAddr
-
- if HttpPort != 0 {
- addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
- }
- var (
- err error
- l net.Listener
- )
- if UseFcgi {
- if HttpPort == 0 {
- l, err = net.Listen("unix", addr)
- } else {
- l, err = net.Listen("tcp", addr)
- }
- if err != nil {
- BeeLogger.Fatal("Listen: ", err)
- }
- err = fcgi.Serve(l, app.Handlers)
- } else {
- if EnableHotUpdate {
- server := &http.Server{
- Handler: app.Handlers,
- ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
- WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
- }
- laddr, err := net.ResolveTCPAddr("tcp", addr)
- if nil != err {
- BeeLogger.Fatal("ResolveTCPAddr:", err)
- }
- l, err = GetInitListner(laddr)
- theStoppable = newStoppable(l)
- err = server.Serve(theStoppable)
- theStoppable.wg.Wait()
- CloseSelf()
- } else {
- s := &http.Server{
- Addr: addr,
- Handler: app.Handlers,
- ReadTimeout: time.Duration(HttpServerTimeOut) * time.Second,
- WriteTimeout: time.Duration(HttpServerTimeOut) * time.Second,
- }
- err = s.ListenAndServe()
- }
- }
- if err != nil {
- BeeLogger.Fatal("ListenAndServe: ", err)
- }
-}
-
-func (app *App) Router(path string, c ControllerInterface, mappingMethods ...string) *App {
- app.Handlers.Add(path, c, mappingMethods...)
- return app
-}
-
-func (app *App) AutoRouter(c ControllerInterface) *App {
- app.Handlers.AddAuto(c)
- return app
-}
-
-func (app *App) Filter(filter http.HandlerFunc) *App {
- app.Handlers.Filter(filter)
- return app
-}
-
-func (app *App) FilterParam(param string, filter http.HandlerFunc) *App {
- app.Handlers.FilterParam(param, filter)
- return app
-}
-
-func (app *App) FilterPrefixPath(path string, filter http.HandlerFunc) *App {
- app.Handlers.FilterPrefixPath(path, filter)
- return app
-}
-
-func (app *App) FilterAfter(filter http.HandlerFunc) *App {
- app.Handlers.FilterAfter(filter)
- return app
-}
-
-func (app *App) FilterParamAfter(param string, filter http.HandlerFunc) *App {
- app.Handlers.FilterParamAfter(param, filter)
- return app
-}
-
-func (app *App) FilterPrefixPathAfter(path string, filter http.HandlerFunc) *App {
- app.Handlers.FilterPrefixPathAfter(path, filter)
- return app
-}
-
-func (app *App) SetViewsPath(path string) *App {
- ViewsPath = path
- return app
-}
-
-func (app *App) SetStaticPath(url string, path string) *App {
- StaticDir[url] = path
- return app
-}
-
-func (app *App) DelStaticPath(url string) *App {
- delete(StaticDir, url)
- return app
-}
-
-func (app *App) ErrorLog(ctx *Context) {
- BeeLogger.Printf("[ERR] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
-}
-
-func (app *App) AccessLog(ctx *Context) {
- BeeLogger.Printf("[ACC] host: '%s', request: '%s %s', proto: '%s', ua: '%s', remote: '%s'\n", ctx.Request.Host, ctx.Request.Method, ctx.Request.URL.Path, ctx.Request.Proto, ctx.Request.UserAgent(), ctx.Request.RemoteAddr)
-}
-
-func RegisterController(path string, c ControllerInterface) *App {
- BeeApp.Router(path, c)
- return BeeApp
-}
-
func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
BeeApp.Router(rootpath, c, mappingMethods...)
return BeeApp
@@ -232,11 +24,6 @@ func AutoRouter(c ControllerInterface) *App {
return BeeApp
}
-func RouterHandler(path string, c http.Handler) *App {
- BeeApp.Handlers.AddHandler(path, c)
- return BeeApp
-}
-
func Errorhandler(err string, h http.HandlerFunc) *App {
ErrorMaps[err] = h
return BeeApp
@@ -257,37 +44,18 @@ func DelStaticPath(url string) *App {
return BeeApp
}
-func Filter(filter http.HandlerFunc) *App {
- BeeApp.Filter(filter)
- return BeeApp
-}
-
-func FilterParam(param string, filter http.HandlerFunc) *App {
- BeeApp.FilterParam(param, filter)
- return BeeApp
-}
-
-func FilterPrefixPath(path string, filter http.HandlerFunc) *App {
- BeeApp.FilterPrefixPath(path, filter)
- return BeeApp
-}
-
-func FilterAfter(filter http.HandlerFunc) *App {
- BeeApp.FilterAfter(filter)
- return BeeApp
-}
-
-func FilterParamAfter(param string, filter http.HandlerFunc) *App {
- BeeApp.FilterParamAfter(param, filter)
- return BeeApp
-}
-
-func FilterPrefixPathAfter(path string, filter http.HandlerFunc) *App {
- BeeApp.FilterPrefixPathAfter(path, filter)
+//action has four values:
+//BeforRouter
+//AfterStatic
+//BeforExec
+//AfterExec
+func AddFilter(pattern, action string, filter FilterFunc) *App {
+ BeeApp.Filter(pattern, action, filter)
return BeeApp
}
func Run() {
+ //if AppConfigPath not In the conf/app.conf reParse config
if AppConfigPath != path.Join(AppPath, "conf", "app.conf") {
err := ParseConfig()
if err != nil {
@@ -296,18 +64,18 @@ func Run() {
}
}
}
- if PprofOn {
- BeeApp.Router(`/debug/pprof`, &ProfController{})
- BeeApp.Router(`/debug/pprof/:pp([\w]+)`, &ProfController{})
- }
+
if SessionOn {
GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime, SessionSavePath)
go GlobalSessions.GC()
}
- err := BuildTemplate(ViewsPath)
- if err != nil {
- if RunMode == "dev" {
- Warn(err)
+
+ if AutoRender {
+ err := BuildTemplate(ViewsPath)
+ if err != nil {
+ if RunMode == "dev" {
+ Warn(err)
+ }
}
}
registerErrorHander()
diff --git a/config.go b/config.go
index 07a2962e..a038eab1 100644
--- a/config.go
+++ b/config.go
@@ -1,131 +1,91 @@
package beego
import (
- "bufio"
- "bytes"
- "errors"
- "io"
+ "github.com/astaxie/beego/config"
+ "github.com/astaxie/beego/session"
+ "html/template"
"os"
+ "path"
+ "runtime"
"strconv"
- "strings"
- "sync"
- "unicode"
)
var (
- bComment = []byte{'#'}
- bEmpty = []byte{}
- bEqual = []byte{'='}
- bDQuote = []byte{'"'}
+ BeeApp *App
+ AppName string
+ AppPath string
+ AppConfigPath string
+ StaticDir map[string]string
+ TemplateCache map[string]*template.Template
+ HttpAddr string
+ HttpPort int
+ HttpTLS bool
+ HttpCertFile string
+ HttpKeyFile string
+ RecoverPanic bool
+ AutoRender bool
+ PprofOn bool
+ ViewsPath string
+ RunMode string //"dev" or "prod"
+ AppConfig config.ConfigContainer
+ //related to session
+ GlobalSessions *session.Manager //GlobalSessions
+ SessionOn bool // whether auto start session,default is false
+ SessionProvider string // default session provider memory mysql redis
+ SessionName string // sessionName cookie's name
+ SessionGCMaxLifetime int64 // session's gc maxlifetime
+ SessionSavePath string // session savepath if use mysql/redis/file this set to the connectinfo
+ UseFcgi bool
+ MaxMemory int64
+ EnableGzip bool // enable gzip
+ DirectoryIndex bool //enable DirectoryIndex default is false
+ EnableHotUpdate bool //enable HotUpdate default is false
+ HttpServerTimeOut int64 //set httpserver timeout
+ ErrorsShow bool //set weather show errors
+ XSRFKEY string //set XSRF
+ EnableXSRF bool
+ XSRFExpire int
+ CopyRequestBody bool //When in raw application, You want to the reqeustbody
+ TemplateLeft string
+ TemplateRight string
)
-// A Config represents the configuration.
-type Config struct {
- filename string
- comment map[int][]string // id: []{comment, key...}; id 1 is for main comment.
- data map[string]string // key: value
- offset map[string]int64 // key: offset; for editing.
- sync.RWMutex
-}
-
-// ParseFile creates a new Config and parses the file configuration from the
-// named file.
-func LoadConfig(name string) (*Config, error) {
- file, err := os.Open(name)
- if err != nil {
- return nil, err
- }
-
- cfg := &Config{
- file.Name(),
- make(map[int][]string),
- make(map[string]string),
- make(map[string]int64),
- sync.RWMutex{},
- }
- cfg.Lock()
- defer cfg.Unlock()
- defer file.Close()
-
- var comment bytes.Buffer
- buf := bufio.NewReader(file)
-
- for nComment, off := 0, int64(1); ; {
- line, _, err := buf.ReadLine()
- if err == io.EOF {
- break
- }
- if bytes.Equal(line, bEmpty) {
- continue
- }
-
- off += int64(len(line))
-
- if bytes.HasPrefix(line, bComment) {
- line = bytes.TrimLeft(line, "#")
- line = bytes.TrimLeftFunc(line, unicode.IsSpace)
- comment.Write(line)
- comment.WriteByte('\n')
- continue
- }
- if comment.Len() != 0 {
- cfg.comment[nComment] = []string{comment.String()}
- comment.Reset()
- nComment++
- }
-
- val := bytes.SplitN(line, bEqual, 2)
- if bytes.HasPrefix([]byte(strings.TrimSpace(string(val[1]))), bDQuote) {
- val[1] = bytes.Trim([]byte(strings.TrimSpace(string(val[1]))), `"`)
- }
-
- key := strings.TrimSpace(string(val[0]))
- cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key)
- cfg.data[key] = strings.TrimSpace(string(val[1]))
- cfg.offset[key] = off
- }
- return cfg, nil
-}
-
-// Bool returns the boolean value for a given key.
-func (c *Config) Bool(key string) (bool, error) {
- return strconv.ParseBool(c.data[key])
-}
-
-// Int returns the integer value for a given key.
-func (c *Config) Int(key string) (int, error) {
- return strconv.Atoi(c.data[key])
-}
-
-func (c *Config) Int64(key string) (int64, error) {
- return strconv.ParseInt(c.data[key], 10, 64)
-}
-
-// Float returns the float value for a given key.
-func (c *Config) Float(key string) (float64, error) {
- return strconv.ParseFloat(c.data[key], 64)
-}
-
-// String returns the string value for a given key.
-func (c *Config) String(key string) string {
- return c.data[key]
-}
-
-// WriteValue writes a new value for key.
-func (c *Config) SetValue(key, value string) error {
- c.Lock()
- defer c.Unlock()
-
- if _, found := c.data[key]; !found {
- return errors.New("key not found: " + key)
- }
-
- c.data[key] = value
- return nil
+func init() {
+ os.Chdir(path.Dir(os.Args[0]))
+ BeeApp = NewApp()
+ AppPath, _ = os.Getwd()
+ StaticDir = make(map[string]string)
+ TemplateCache = make(map[string]*template.Template)
+ HttpAddr = ""
+ HttpPort = 8080
+ AppName = "beego"
+ RunMode = "dev" //default runmod
+ AutoRender = true
+ RecoverPanic = true
+ PprofOn = false
+ ViewsPath = "views"
+ SessionOn = false
+ SessionProvider = "memory"
+ SessionName = "beegosessionID"
+ SessionGCMaxLifetime = 3600
+ SessionSavePath = ""
+ UseFcgi = false
+ MaxMemory = 1 << 26 //64MB
+ EnableGzip = false
+ StaticDir["/static"] = "static"
+ AppConfigPath = path.Join(AppPath, "conf", "app.conf")
+ HttpServerTimeOut = 0
+ ErrorsShow = true
+ XSRFKEY = "beegoxsrf"
+ XSRFExpire = 60
+ TemplateLeft = "{{"
+ TemplateRight = "}}"
+ ParseConfig()
+ runtime.GOMAXPROCS(runtime.NumCPU())
}
func ParseConfig() (err error) {
- AppConfig, err = LoadConfig(AppConfigPath)
+ AppConfig, err := config.NewConfig("ini", AppConfigPath)
if err != nil {
return err
} else {
@@ -204,6 +164,15 @@ func ParseConfig() (err error) {
if tplright := AppConfig.String("templateright"); tplright != "" {
TemplateRight = tplright
}
+ if httptls, err := AppConfig.Bool("HttpTLS"); err == nil {
+ HttpTLS = httptls
+ }
+ if certfile := AppConfig.String("HttpCertFile"); certfile != "" {
+ HttpCertFile = certfile
+ }
+ if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" {
+ HttpKeyFile = keyfile
+ }
}
return nil
}
diff --git a/context/context.go b/context/context.go
index f985e4e2..b754966c 100644
--- a/context/context.go
+++ b/context/context.go
@@ -16,6 +16,11 @@ func (ctx *Context) Redirect(status int, localurl string) {
ctx.Output.SetStatus(status)
}
+func (ctx *Context) Abort(status int, body string) {
+ ctx.Output.SetStatus(status)
+ ctx.Output.Body([]byte(body))
+}
+
func (ctx *Context) WriteString(content string) {
ctx.Output.Body([]byte(content))
}
diff --git a/context/output.go b/context/output.go
index 91fc63ae..eb8b1c35 100644
--- a/context/output.go
+++ b/context/output.go
@@ -17,7 +17,7 @@ import (
)
type BeegoOutput struct {
- context *Context
+ Context *Context
Status int
EnableGzip bool
res http.ResponseWriter
@@ -35,8 +35,8 @@ func (output *BeegoOutput) Header(key, val string) {
func (output *BeegoOutput) Body(content []byte) {
output_writer := output.res.(io.Writer)
- if output.EnableGzip == true && output.context.Input.Header("Accept-Encoding") != "" {
- splitted := strings.SplitN(output.context.Input.Header("Accept-Encoding"), ",", -1)
+ if output.EnableGzip == true && output.Context.Input.Header("Accept-Encoding") != "" {
+ splitted := strings.SplitN(output.Context.Input.Header("Accept-Encoding"), ",", -1)
encodings := make([]string, len(splitted))
for i, val := range splitted {
@@ -120,23 +120,40 @@ func sanitizeValue(v string) string {
return cookieValueSanitizer.Replace(v)
}
-func (output *BeegoOutput) Json(data string) error {
+func (output *BeegoOutput) Json(data interface{}, hasIndent bool, coding bool) error {
output.Header("Content-Type", "application/json;charset=UTF-8")
- content, err := json.Marshal(data)
+ var content []byte
+ var err error
+ if hasIndent {
+ content, err = json.MarshalIndent(data, "", " ")
+ } else {
+ content, err = json.Marshal(data)
+ }
if err != nil {
+ http.Error(output.res, err.Error(), http.StatusInternalServerError)
return err
}
+ if coding {
+ content = []byte(stringsToJson(string(content)))
+ }
output.Body(content)
return nil
}
-func (output *BeegoOutput) Jsonp(data string) error {
+func (output *BeegoOutput) Jsonp(data interface{}, hasIndent bool) error {
output.Header("Content-Type", "application/javascript;charset=UTF-8")
- content, err := json.Marshal(data)
+ var content []byte
+ var err error
+ if hasIndent {
+ content, err = json.MarshalIndent(data, "", " ")
+ } else {
+ content, err = json.Marshal(data)
+ }
if err != nil {
+ http.Error(output.res, err.Error(), http.StatusInternalServerError)
return err
}
- callback := output.context.Input.Query("callback")
+ callback := output.Context.Input.Query("callback")
if callback == "" {
return errors.New(`"callback" parameter required`)
}
@@ -148,10 +165,17 @@ func (output *BeegoOutput) Jsonp(data string) error {
return nil
}
-func (output *BeegoOutput) Xml(data string) error {
+func (output *BeegoOutput) Xml(data string, hasIndent bool) error {
output.Header("Content-Type", "application/xml;charset=UTF-8")
- content, err := xml.Marshal(data)
+ var content []byte
+ var err error
+ if hasIndent {
+ content, err = xml.MarshalIndent(data, "", " ")
+ } else {
+ content, err = xml.Marshal(data)
+ }
if err != nil {
+ http.Error(output.res, err.Error(), http.StatusInternalServerError)
return err
}
output.Body(content)
@@ -166,7 +190,7 @@ func (output *BeegoOutput) Download(file string) {
output.Header("Expires", "0")
output.Header("Cache-Control", "must-revalidate")
output.Header("Pragma", "public")
- http.ServeFile(output.res, output.context.Request, file)
+ http.ServeFile(output.res, output.Context.Request, file)
}
func (output *BeegoOutput) ContentType(ext string) {
@@ -219,3 +243,17 @@ func (output *BeegoOutput) IsClientError(status int) bool {
func (output *BeegoOutput) IsServerError(status int) bool {
return output.Status >= 500 && output.Status < 600
}
+
+func stringsToJson(str string) string {
+ rs := []rune(str)
+ jsons := ""
+ for _, r := range rs {
+ rint := int(r)
+ if rint < 128 {
+ jsons += string(r)
+ } else {
+ jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json
+ }
+ }
+ return jsons
+}
diff --git a/controller.go b/controller.go
index 7ec43976..3a0909b7 100644
--- a/controller.go
+++ b/controller.go
@@ -2,15 +2,12 @@ package beego
import (
"bytes"
- "compress/flate"
- "compress/gzip"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
- "encoding/json"
- "encoding/xml"
"errors"
"fmt"
+ "github.com/astaxie/beego/context"
"github.com/astaxie/beego/session"
"html/template"
"io"
@@ -26,7 +23,7 @@ import (
)
type Controller struct {
- Ctx *Context
+ Ctx *context.Context
Data map[interface{}]interface{}
ChildName string
TplNames string
@@ -39,7 +36,7 @@ type Controller struct {
}
type ControllerInterface interface {
- Init(ct *Context, cn string)
+ Init(ct *Context, childName string)
Prepare()
Get()
Post()
@@ -52,11 +49,11 @@ type ControllerInterface interface {
Render() error
}
-func (c *Controller) Init(ctx *Context, cn string) {
+func (c *Controller) Init(ctx *context.Context, childName string) {
c.Data = make(map[interface{}]interface{})
c.Layout = ""
c.TplNames = ""
- c.ChildName = cn
+ c.ChildName = childName
c.Ctx = ctx
c.TplExt = "tpl"
}
@@ -109,8 +106,8 @@ func (c *Controller) Render() error {
if err != nil {
return err
} else {
- c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
- c.writeToWriter(rb)
+ c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
+ c.Ctx.Output.Body(rb)
}
return nil
}
@@ -172,41 +169,6 @@ func (c *Controller) RenderBytes() ([]byte, error) {
return []byte{}, nil
}
-func (c *Controller) writeToWriter(rb []byte) {
- output_writer := c.Ctx.ResponseWriter.(io.Writer)
- if EnableGzip == true && c.Ctx.Request.Header.Get("Accept-Encoding") != "" {
- splitted := strings.SplitN(c.Ctx.Request.Header.Get("Accept-Encoding"), ",", -1)
- encodings := make([]string, len(splitted))
-
- for i, val := range splitted {
- encodings[i] = strings.TrimSpace(val)
- }
- for _, val := range encodings {
- if val == "gzip" {
- c.Ctx.ResponseWriter.Header().Set("Content-Encoding", "gzip")
- output_writer, _ = gzip.NewWriterLevel(c.Ctx.ResponseWriter, gzip.BestSpeed)
-
- break
- } else if val == "deflate" {
- c.Ctx.ResponseWriter.Header().Set("Content-Encoding", "deflate")
- output_writer, _ = flate.NewWriter(c.Ctx.ResponseWriter, flate.BestSpeed)
- break
- }
- }
- } else {
- c.Ctx.SetHeader("Content-Length", strconv.Itoa(len(rb)), true)
- }
- output_writer.Write(rb)
- switch output_writer.(type) {
- case *gzip.Writer:
- output_writer.(*gzip.Writer).Close()
- case *flate.Writer:
- output_writer.(*flate.Writer).Close()
- case io.WriteCloser:
- output_writer.(io.WriteCloser).Close()
- }
-}
-
func (c *Controller) Redirect(url string, code int) {
c.Ctx.Redirect(code, url)
}
@@ -216,63 +178,37 @@ func (c *Controller) Abort(code string) {
}
func (c *Controller) ServeJson(encoding ...bool) {
- var content []byte
- var err error
+ var hasIndent bool
+ var hasencoding bool
if RunMode == "prod" {
- content, err = json.Marshal(c.Data["json"])
+ hasIndent = false
} else {
- content, err = json.MarshalIndent(c.Data["json"], "", " ")
+ hasIndent = true
}
- if err != nil {
- http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
- return
- }
- c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/json;charset=UTF-8")
if len(encoding) > 0 && encoding[0] == true {
- content = []byte(stringsToJson(string(content)))
+ hasencoding = true
}
- c.writeToWriter(content)
+ c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding)
}
func (c *Controller) ServeJsonp() {
- var content []byte
- var err error
+ var hasIndent bool
if RunMode == "prod" {
- content, err = json.Marshal(c.Data["jsonp"])
+ hasIndent = false
} else {
- content, err = json.MarshalIndent(c.Data["jsonp"], "", " ")
+ hasIndent = true
}
- if err != nil {
- http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
- return
- }
- callback := c.Ctx.Request.Form.Get("callback")
- if callback == "" {
- http.Error(c.Ctx.ResponseWriter, `"callback" parameter required`, http.StatusInternalServerError)
- return
- }
- callback_content := bytes.NewBufferString(callback)
- callback_content.WriteString("(")
- callback_content.Write(content)
- callback_content.WriteString(");\r\n")
- c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/javascript;charset=UTF-8")
- c.writeToWriter(callback_content.Bytes())
+ c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
}
func (c *Controller) ServeXml() {
- var content []byte
- var err error
+ var hasIndent bool
if RunMode == "prod" {
- content, err = xml.Marshal(c.Data["xml"])
+ hasIndent = false
} else {
- content, err = xml.MarshalIndent(c.Data["xml"], "", " ")
+ hasIndent = true
}
- if err != nil {
- http.Error(c.Ctx.ResponseWriter, err.Error(), http.StatusInternalServerError)
- return
- }
- c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/xml;charset=UTF-8")
- c.writeToWriter(content)
+ c.Ctx.Output.Xml(c.Data["xml"], hasIndent)
}
func (c *Controller) Input() url.Values {
@@ -313,6 +249,10 @@ func (c *Controller) GetBool(key string) (bool, error) {
return strconv.ParseBool(c.Input().Get(key))
}
+func (c *Controller) GetFloat(key string) (float64, error) {
+ return strconv.ParseFloat(c.Input().Get(key), 64)
+}
+
func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
return c.Ctx.Request.FormFile(key)
}
@@ -365,7 +305,7 @@ func (c *Controller) DestroySession() {
}
func (c *Controller) IsAjax() bool {
- return (c.Ctx.Request.Header.Get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest")
+ return c.Ctx.Input.IsAjax()
}
func (c *Controller) XsrfToken() string {
diff --git a/filter.go b/filter.go
new file mode 100644
index 00000000..1f53d129
--- /dev/null
+++ b/filter.go
@@ -0,0 +1,31 @@
+package beego
+
+import (
+ "regexp"
+)
+
+type FilterRouter struct {
+ pattern string
+ regex *regexp.Regexp
+ filterFunc FilterFunc
+ hasregex bool
+}
+
+func (mr *FilterRouter) ValidRouter(router string) bool {
+ if mr.pattern == "" {
+ return true
+ }
+ if router == mr.pattern {
+ return true
+ }
+ if mr.hasregex {
+ if mr.regex.MatchString(router) {
+ return true
+ }
+ matches := mr.regex.FindStringSubmatch(router)
+ if len(matches[0]) == len(router) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/middleware/error.go b/middleware/error.go
new file mode 100644
index 00000000..726290ed
--- /dev/null
+++ b/middleware/error.go
@@ -0,0 +1,280 @@
+package middleware
+
+import (
+ "fmt"
+ "html/template"
+ "net/http"
+ "runtime"
+)
+
+var tpl = `
+
+
+
+
+ beego application error
+
+
+
+
+
+
+
+
+ Request Method: | {{.RequestMethod}} |
+
+
+ Request URL: | {{.RequestURL}} |
+
+
+ RemoteAddr: | {{.RemoteAddr }} |
+
+
+
+
+
+
+
+`
+
+func ShowErr(err interface{}, rw http.ResponseWriter, r *http.Request, Stack string) {
+ t, _ := template.New("beegoerrortemp").Parse(tpl)
+ data := make(map[string]string)
+ data["AppError"] = AppName + ":" + fmt.Sprint(err)
+ data["RequestMethod"] = r.Method
+ data["RequestURL"] = r.RequestURI
+ data["RemoteAddr"] = r.RemoteAddr
+ data["Stack"] = Stack
+ data["BeegoVersion"] = VERSION
+ data["GoVersion"] = runtime.Version()
+ t.Execute(rw, data)
+}
+
+var errtpl = `
+
+
+
+
+ {{.Title}}
+
+
+
+
+
+
+
{{.Title}}
+
+
+ {{.Content}}
+
Go Home
+
+
power by beego {{.BeegoVersion}}
+
+
+
+
+
+`
+
+var ErrorMaps map[string]http.HandlerFunc
+
+func init() {
+ ErrorMaps = make(map[string]http.HandlerFunc)
+}
+
+//404
+func NotFound(rw http.ResponseWriter, r *http.Request) {
+ t, _ := template.New("beegoerrortemp").Parse(errtpl)
+ data := make(map[string]interface{})
+ data["Title"] = "Page Not Found"
+ data["Content"] = template.HTML("
The Page You have requested flown the coop." +
+ "
Perhaps you are here because:" +
+ "
" +
+ "
The page has moved" +
+ "
The page no longer exists" +
+ "
You were looking for your puppy and got lost" +
+ "
You like 404 pages" +
+ "
")
+ data["BeegoVersion"] = VERSION
+ rw.WriteHeader(http.StatusNotFound)
+ t.Execute(rw, data)
+}
+
+//401
+func Unauthorized(rw http.ResponseWriter, r *http.Request) {
+ t, _ := template.New("beegoerrortemp").Parse(errtpl)
+ data := make(map[string]interface{})
+ data["Title"] = "Unauthorized"
+ data["Content"] = template.HTML("
The Page You have requested can't authorized." +
+ "
Perhaps you are here because:" +
+ "
" +
+ "
Check the credentials that you supplied" +
+ "
Check the address for errors" +
+ "
")
+ data["BeegoVersion"] = VERSION
+ rw.WriteHeader(http.StatusUnauthorized)
+ t.Execute(rw, data)
+}
+
+//403
+func Forbidden(rw http.ResponseWriter, r *http.Request) {
+ t, _ := template.New("beegoerrortemp").Parse(errtpl)
+ data := make(map[string]interface{})
+ data["Title"] = "Forbidden"
+ data["Content"] = template.HTML("
The Page You have requested forbidden." +
+ "
Perhaps you are here because:" +
+ "
" +
+ "
Your address may be blocked" +
+ "
The site may be disabled" +
+ "
You need to log in" +
+ "
")
+ data["BeegoVersion"] = VERSION
+ rw.WriteHeader(http.StatusForbidden)
+ t.Execute(rw, data)
+}
+
+//503
+func ServiceUnavailable(rw http.ResponseWriter, r *http.Request) {
+ t, _ := template.New("beegoerrortemp").Parse(errtpl)
+ data := make(map[string]interface{})
+ data["Title"] = "Service Unavailable"
+ data["Content"] = template.HTML("
The Page You have requested unavailable." +
+ "
Perhaps you are here because:" +
+ "
" +
+ "
The page is overloaded" +
+ "
Please try again later." +
+ "
")
+ data["BeegoVersion"] = VERSION
+ rw.WriteHeader(http.StatusServiceUnavailable)
+ t.Execute(rw, data)
+}
+
+//500
+func InternalServerError(rw http.ResponseWriter, r *http.Request) {
+ t, _ := template.New("beegoerrortemp").Parse(errtpl)
+ data := make(map[string]interface{})
+ data["Title"] = "Internal Server Error"
+ data["Content"] = template.HTML("
The Page You have requested has down now." +
+ "
" +
+ "
simply try again later" +
+ "
you should report the fault to the website administrator" +
+ "
")
+ data["BeegoVersion"] = VERSION
+ rw.WriteHeader(http.StatusInternalServerError)
+ t.Execute(rw, data)
+}
+
+func registerErrorHander() {
+ if _, ok := ErrorMaps["404"]; !ok {
+ ErrorMaps["404"] = NotFound
+ }
+
+ if _, ok := ErrorMaps["401"]; !ok {
+ ErrorMaps["401"] = Unauthorized
+ }
+
+ if _, ok := ErrorMaps["403"]; !ok {
+ ErrorMaps["403"] = Forbidden
+ }
+
+ if _, ok := ErrorMaps["503"]; !ok {
+ ErrorMaps["503"] = ServiceUnavailable
+ }
+
+ if _, ok := ErrorMaps["500"]; !ok {
+ ErrorMaps["500"] = InternalServerError
+ }
+}
diff --git a/middleware/i18n.go b/middleware/i18n.go
new file mode 100644
index 00000000..5ccdd705
--- /dev/null
+++ b/middleware/i18n.go
@@ -0,0 +1 @@
+package middleware
\ No newline at end of file
diff --git a/middleware/profile.go b/middleware/profile.go
new file mode 100644
index 00000000..51e47f41
--- /dev/null
+++ b/middleware/profile.go
@@ -0,0 +1,226 @@
+package middleware
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "runtime"
+ "runtime/debug"
+ "runtime/pprof"
+ "strconv"
+ "sync/atomic"
+ "time"
+)
+
+var heapProfileCounter int32
+var startTime = time.Now()
+var pid int
+
+func init() {
+ pid = os.Getpid()
+}
+
+func StartCPUProfile() {
+ f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
+ if err != nil {
+ log.Fatal(err)
+ }
+ pprof.StartCPUProfile(f)
+}
+
+func StopCPUProfile() {
+ pprof.StopCPUProfile()
+}
+
+func StartBlockProfile(rate int) {
+ runtime.SetBlockProfileRate(rate)
+}
+
+func StopBlockProfile() {
+ filename := "block-" + strconv.Itoa(pid) + ".pprof"
+ f, err := os.Create(filename)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if err = pprof.Lookup("block").WriteTo(f, 0); err != nil {
+ log.Fatalf(" can't write %s: %s", filename, err)
+ }
+ f.Close()
+}
+
+func SetMemProfileRate(rate int) {
+ runtime.MemProfileRate = rate
+}
+
+func GC() {
+ runtime.GC()
+}
+
+func DumpHeap() {
+ filename := "heap-" + strconv.Itoa(pid) + "-" + strconv.Itoa(int(atomic.AddInt32(&heapProfileCounter, 1))) + ".pprof"
+ f, err := os.Create(filename)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "testing: %s", err)
+ return
+ }
+ if err = pprof.WriteHeapProfile(f); err != nil {
+ fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", filename, err)
+ }
+ f.Close()
+}
+
+//func showSystemStat(interval time.Duration, count int) {
+
+// usage1 := &syscall.Rusage{}
+// var lastUtime int64
+// var lastStime int64
+
+// counter := 0
+// for {
+
+// //http://man7.org/linux/man-pages/man3/vtimes.3.html
+// syscall.Getrusage(syscall.RUSAGE_SELF, usage1)
+
+// utime := (usage1.Utime.Sec * 1000000000) + int64(usage1.Utime.Usec)
+// stime := (usage1.Stime.Sec * 1000000000) + int64(usage1.Stime.Usec)
+// userCPUUtil := float64(utime-lastUtime) * 100 / float64(interval)
+// sysCPUUtil := float64(stime-lastStime) * 100 / float64(interval)
+// memUtil := usage1.Maxrss * 1024
+
+// lastUtime = utime
+// lastStime = stime
+
+// if counter > 0 {
+// fmt.Printf("cpu: %3.2f%% us %3.2f%% sy, mem:%s \n", userCPUUtil, sysCPUUtil, toH(uint64(memUtil)))
+// }
+
+// counter += 1
+// if count >= 1 && count < counter {
+// return
+// }
+// time.Sleep(interval)
+// }
+
+//}
+
+//func ShowSystemStat(seconds int) {
+// go func() {
+// interval := time.Duration(seconds) * time.Second
+// showSystemStat(interval, 0)
+// }()
+//}
+
+//func PrintSystemStats() {
+// interval := time.Duration(1) * time.Second
+// showSystemStat(interval, 1)
+//}
+
+func ShowGCStat() {
+ go func() {
+ var numGC int64
+
+ interval := time.Duration(100) * time.Millisecond
+ gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
+ memStats := &runtime.MemStats{}
+ for {
+ debug.ReadGCStats(gcstats)
+ if gcstats.NumGC > numGC {
+ runtime.ReadMemStats(memStats)
+
+ printGC(memStats, gcstats)
+ numGC = gcstats.NumGC
+ }
+ time.Sleep(interval)
+ }
+ }()
+}
+
+func PrintGCSummary() {
+ memStats := &runtime.MemStats{}
+ runtime.ReadMemStats(memStats)
+ gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)}
+ debug.ReadGCStats(gcstats)
+
+ printGC(memStats, gcstats)
+}
+
+func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats) {
+
+ if gcstats.NumGC > 0 {
+ lastPause := gcstats.Pause[0]
+ elapsed := time.Now().Sub(startTime)
+ overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100
+ allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
+
+ fmt.Printf("NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n",
+ gcstats.NumGC,
+ toS(lastPause),
+ toS(avg(gcstats.Pause)),
+ overhead,
+ toH(memStats.Alloc),
+ toH(memStats.Sys),
+ toH(uint64(allocatedRate)),
+ toS(gcstats.PauseQuantiles[94]),
+ toS(gcstats.PauseQuantiles[98]),
+ toS(gcstats.PauseQuantiles[99]))
+ } else {
+ // while GC has disabled
+ elapsed := time.Now().Sub(startTime)
+ allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds()
+
+ fmt.Printf("Alloc:%s Sys:%s Alloc(Rate):%s/s\n",
+ toH(memStats.Alloc),
+ toH(memStats.Sys),
+ toH(uint64(allocatedRate)))
+ }
+}
+
+func avg(items []time.Duration) time.Duration {
+ var sum time.Duration
+ for _, item := range items {
+ sum += item
+ }
+ return time.Duration(int64(sum) / int64(len(items)))
+}
+
+// human readable format
+func toH(bytes uint64) string {
+ switch {
+ case bytes < 1024:
+ return fmt.Sprintf("%dB", bytes)
+ case bytes < 1024*1024:
+ return fmt.Sprintf("%.2fK", float64(bytes)/1024)
+ case bytes < 1024*1024*1024:
+ return fmt.Sprintf("%.2fM", float64(bytes)/1024/1024)
+ default:
+ return fmt.Sprintf("%.2fG", float64(bytes)/1024/1024/1024)
+ }
+}
+
+// short string format
+func toS(d time.Duration) string {
+
+ u := uint64(d)
+ if u < uint64(time.Second) {
+ switch {
+ case u == 0:
+ return "0"
+ case u < uint64(time.Microsecond):
+ return fmt.Sprintf("%.2fns", float64(u))
+ case u < uint64(time.Millisecond):
+ return fmt.Sprintf("%.2fus", float64(u)/1000)
+ default:
+ return fmt.Sprintf("%.2fms", float64(u)/1000/1000)
+ }
+ } else {
+ switch {
+ case u < uint64(time.Minute):
+ return fmt.Sprintf("%.2fs", float64(u)/1000/1000/1000)
+ case u < uint64(time.Hour):
+ return fmt.Sprintf("%.2fm", float64(u)/1000/1000/1000/60)
+ default:
+ return fmt.Sprintf("%.2fh", float64(u)/1000/1000/1000/60/60)
+ }
+ }
+
+}
diff --git a/middleware/router.go b/middleware/router.go
deleted file mode 100644
index e69de29b..00000000
diff --git a/middleware/session.go b/middleware/session.go
new file mode 100644
index 00000000..48beba91
--- /dev/null
+++ b/middleware/session.go
@@ -0,0 +1,14 @@
+package middleware
+
+import (
+ "github.com/astaxie/beego/session"
+)
+
+var (
+ GlobalSessions *session.Manager
+)
+
+func StartSession(provideName, cookieName string, maxlifetime int64, savePath string) {
+ GlobalSessions, _ = session.NewManager(provideName, cookieName, maxlifetime, savePath)
+ go GlobalSessions.GC()
+}
diff --git a/pprof.go b/pprof.go
deleted file mode 100644
index 774e0290..00000000
--- a/pprof.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package beego
-
-import (
- "net/http/pprof"
-)
-
-type ProfController struct {
- Controller
-}
-
-func (this *ProfController) Get() {
- switch this.Ctx.Params[":pp"] {
- default:
- pprof.Index(this.Ctx.ResponseWriter, this.Ctx.Request)
- case "":
- pprof.Index(this.Ctx.ResponseWriter, this.Ctx.Request)
- case "cmdline":
- pprof.Cmdline(this.Ctx.ResponseWriter, this.Ctx.Request)
- case "profile":
- pprof.Profile(this.Ctx.ResponseWriter, this.Ctx.Request)
- case "symbol":
- pprof.Symbol(this.Ctx.ResponseWriter, this.Ctx.Request)
- }
- this.Ctx.ResponseWriter.WriteHeader(200)
-}
diff --git a/router.go b/router.go
index 157f0618..4099d5c7 100644
--- a/router.go
+++ b/router.go
@@ -1,9 +1,8 @@
package beego
import (
- "bytes"
"fmt"
- "io/ioutil"
+ beecontext "github.com/astaxie/beego/context"
"net/http"
"net/url"
"os"
@@ -25,31 +24,20 @@ type controllerInfo struct {
hasMethod bool
}
-type userHandler struct {
- pattern string
- regex *regexp.Regexp
- params map[int]string
- h http.Handler
-}
-
type ControllerRegistor struct {
routers []*controllerInfo
fixrouters []*controllerInfo
enableFilter bool
- filters []http.HandlerFunc
- enableAfter bool
- afterFilters []http.HandlerFunc
- enableUser bool
- userHandlers map[string]*userHandler
+ filters map[string][]*FilterRouter
enableAuto bool
autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type
}
func NewControllerRegistor() *ControllerRegistor {
return &ControllerRegistor{
- routers: make([]*controllerInfo, 0),
- userHandlers: make(map[string]*userHandler),
- autoRouter: make(map[string]map[string]reflect.Type),
+ routers: make([]*controllerInfo, 0),
+ autoRouter: make(map[string]map[string]reflect.Type),
+ filters: make(map[string][]*FilterRouter),
}
}
@@ -182,35 +170,39 @@ func (p *ControllerRegistor) AddAuto(c ControllerInterface) {
}
}
-func (p *ControllerRegistor) AddHandler(pattern string, c http.Handler) {
- p.enableUser = true
- parts := strings.Split(pattern, "/")
+// Filter adds the middleware filter.
+func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc) {
+ p.enableFilter = true
+ mr := new(FilterRouter)
+ mr.filterFunc = filter
+ parts := strings.Split(pattern, "/")
j := 0
- params := make(map[int]string)
for i, part := range parts {
if strings.HasPrefix(part, ":") {
- expr := "([^/]+)"
+ expr := "(.+)"
//a user may choose to override the defult expression
// similar to expressjs: ‘/user/:id([0-9]+)’
if index := strings.Index(part, "("); index != -1 {
expr = part[index:]
part = part[:index]
+ //match /user/:id:int ([0-9]+)
+ //match /post/:username:string ([\w]+)
+ } else if lindex := strings.LastIndex(part, ":"); lindex != 0 {
+ switch part[lindex:] {
+ case ":int":
+ expr = "([0-9]+)"
+ part = part[:lindex]
+ case ":string":
+ expr = `([\w]+)`
+ part = part[:lindex]
+ }
}
- params[j] = part
parts[i] = expr
j++
}
}
- if j == 0 {
- //now create the Route
- uh := &userHandler{}
- uh.pattern = pattern
- uh.h = c
- p.userHandlers[pattern] = uh
- } else { // add regexp routers
- //recreate the url pattern, with parameters replaced
- //by regular expressions. then compile the regex
+ if j != 0 {
pattern = strings.Join(parts, "/")
regex, regexErr := regexp.Compile(pattern)
if regexErr != nil {
@@ -218,73 +210,11 @@ func (p *ControllerRegistor) AddHandler(pattern string, c http.Handler) {
panic(regexErr)
return
}
-
- //now create the Route
- uh := &userHandler{}
- uh.regex = regex
- uh.params = params
- uh.pattern = pattern
- uh.h = c
- p.userHandlers[pattern] = uh
+ mr.regex = regex
+ mr.hasregex = true
}
-}
-
-// Filter adds the middleware filter.
-func (p *ControllerRegistor) Filter(filter http.HandlerFunc) {
- p.enableFilter = true
- p.filters = append(p.filters, filter)
-}
-
-// FilterParam adds the middleware filter if the REST URL parameter exists.
-func (p *ControllerRegistor) FilterParam(param string, filter http.HandlerFunc) {
- if !strings.HasPrefix(param, ":") {
- param = ":" + param
- }
-
- p.Filter(func(w http.ResponseWriter, r *http.Request) {
- p := r.URL.Query().Get(param)
- if len(p) > 0 {
- filter(w, r)
- }
- })
-}
-
-// FilterPrefixPath adds the middleware filter if the prefix path exists.
-func (p *ControllerRegistor) FilterPrefixPath(path string, filter http.HandlerFunc) {
- p.Filter(func(w http.ResponseWriter, r *http.Request) {
- if strings.HasPrefix(r.URL.Path, path) {
- filter(w, r)
- }
- })
-}
-
-// Filter adds the middleware after filter.
-func (p *ControllerRegistor) FilterAfter(filter http.HandlerFunc) {
- p.enableAfter = true
- p.afterFilters = append(p.afterFilters, filter)
-}
-
-// FilterParam adds the middleware filter if the REST URL parameter exists.
-func (p *ControllerRegistor) FilterParamAfter(param string, filter http.HandlerFunc) {
- if !strings.HasPrefix(param, ":") {
- param = ":" + param
- }
-
- p.FilterAfter(func(w http.ResponseWriter, r *http.Request) {
- p := r.URL.Query().Get(param)
- if len(p) > 0 {
- filter(w, r)
- }
- })
-}
-
-// FilterPrefixPath adds the middleware filter if the prefix path exists.
-func (p *ControllerRegistor) FilterPrefixPathAfter(path string, filter http.HandlerFunc) {
- p.FilterAfter(func(w http.ResponseWriter, r *http.Request) {
- if strings.HasPrefix(r.URL.Path, path) {
- filter(w, r)
- }
- })
+ mr.pattern = pattern
+ p.filters[action] = append(p.filters[action], mr)
}
// AutoRoute
@@ -320,13 +250,30 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}()
w := &responseWriter{writer: rw}
-
w.Header().Set("Server", "beegoServer")
+ context := &beecontext.Context{
+ ResponseWriter: w,
+ Request: r,
+ Input: beecontext.NewInput(r),
+ Output: beecontext.NewOutput(w),
+ }
+ context.Output.Context = context
var runrouter *controllerInfo
var findrouter bool
params := make(map[string]string)
+ context.Input.Param = params
+ if p.enableFilter {
+ if l, ok := p.filters["BeforRouter"]; ok {
+ for _, filterR := range l {
+ if filterR.ValidRouter(r.URL.Path) {
+ filterR.filterFunc(context)
+ }
+ }
+ }
+ }
+
//static file server
for prefix, staticDir := range StaticDir {
if r.URL.Path == "/favicon.ico" {
@@ -359,62 +306,23 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
}
+ if p.enableFilter {
+ if l, ok := p.filters["AfterStatic"]; ok {
+ for _, filterR := range l {
+ if filterR.ValidRouter(r.URL.Path) {
+ filterR.filterFunc(context)
+ }
+ }
+ }
+ }
requestPath := r.URL.Path
- var requestbody []byte
-
if CopyRequestBody {
- requestbody, _ = ioutil.ReadAll(r.Body)
-
- r.Body.Close()
-
- bf := bytes.NewBuffer(requestbody)
-
- r.Body = ioutil.NopCloser(bf)
+ context.Input.Body()
}
r.ParseMultipartForm(MaxMemory)
- //user defined Handler
- if p.enableUser {
- for pattern, c := range p.userHandlers {
- if c.regex == nil && pattern == requestPath {
- c.h.ServeHTTP(rw, r)
- return
- } else if c.regex == nil {
- continue
- }
-
- //check if Route pattern matches url
- if !c.regex.MatchString(requestPath) {
- continue
- }
-
- //get submatches (params)
- matches := c.regex.FindStringSubmatch(requestPath)
-
- //double check that the Route matches the URL pattern.
- if len(matches[0]) != len(requestPath) {
- continue
- }
-
- if len(c.params) > 0 {
- //add url parameters to the query param map
- values := r.URL.Query()
- for i, match := range matches[1:] {
- values.Add(c.params[i], match)
- r.Form.Add(c.params[i], match)
- params[c.params[i]] = match
- }
- //reassemble query params and add to RawQuery
- r.URL.RawQuery = url.Values(values).Encode() + "&" + r.URL.RawQuery
- //r.URL.RawQuery = url.Values(values).Encode()
- }
- c.h.ServeHTTP(rw, r)
- return
- }
- }
-
//first find path from the fixrouters to Improve Performance
for _, route := range p.fixrouters {
n := len(requestPath)
@@ -471,23 +379,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if runrouter != nil {
//execute middleware filters
if p.enableFilter {
- for _, filter := range p.filters {
- filter(w, r)
- if w.started {
- return
+ if l, ok := p.filters["BeforExec"]; ok {
+ for _, filterR := range l {
+ if filterR.ValidRouter(r.URL.Path) {
+ filterR.filterFunc(context)
+ }
}
}
}
-
//Invoke the request handler
vc := reflect.New(runrouter.controllerType)
//call the controller init function
init := vc.MethodByName("Init")
in := make([]reflect.Value, 2)
- ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody}
-
- in[0] = reflect.ValueOf(ct)
+ in[0] = reflect.ValueOf(context)
in[1] = reflect.ValueOf(runrouter.controllerType.Name())
init.Call(in)
//call prepare function
@@ -617,14 +523,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
}
}
+
method = vc.MethodByName("Finish")
method.Call(in)
//execute middleware filters
- if p.enableAfter {
- for _, filter := range p.afterFilters {
- filter(w, r)
- if w.started {
- return
+ if p.enableFilter {
+ if l, ok := p.filters["AfterExec"]; ok {
+ for _, filterR := range l {
+ if filterR.ValidRouter(r.URL.Path) {
+ filterR.filterFunc(context)
+ }
}
}
}
@@ -651,10 +559,11 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) {
//execute middleware filters
if p.enableFilter {
- for _, filter := range p.filters {
- filter(w, r)
- if w.started {
- return
+ if l, ok := p.filters["BeforExec"]; ok {
+ for _, filterR := range l {
+ if filterR.ValidRouter(r.URL.Path) {
+ filterR.filterFunc(context)
+ }
}
}
}
@@ -672,9 +581,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
//call the controller init function
init := vc.MethodByName("Init")
in := make([]reflect.Value, 2)
- ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody}
-
- in[0] = reflect.ValueOf(ct)
+ in[0] = reflect.ValueOf(context)
in[1] = reflect.ValueOf(controllerType.Name())
init.Call(in)
//call prepare function
@@ -702,11 +609,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
method = vc.MethodByName("Finish")
method.Call(in)
//execute middleware filters
- if p.enableAfter {
- for _, filter := range p.afterFilters {
- filter(w, r)
- if w.started {
- return
+ if p.enableFilter {
+ if l, ok := p.filters["AfterExec"]; ok {
+ for _, filterR := range l {
+ if filterR.ValidRouter(r.URL.Path) {
+ filterR.filterFunc(context)
+ }
}
}
}
@@ -714,6 +622,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
method.Call(in)
// set find
findrouter = true
+ goto Last
}
}
}
@@ -721,6 +630,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
}
+Last:
//if no matches to url, throw a not found exception
if !findrouter {
if h, ok := ErrorMaps["404"]; ok {