From bd61dd9ffcfa0d38e1b6ac6dda8dcc9ec1e60f12 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 10 Sep 2013 00:00:11 +0800 Subject: [PATCH] change a log about new version --- app.go | 108 ++++++++++++++++ beego.go | 264 +++------------------------------------ config.go | 199 +++++++++++++----------------- context/context.go | 5 + context/output.go | 60 +++++++-- controller.go | 112 ++++------------- filter.go | 31 +++++ middleware/error.go | 280 ++++++++++++++++++++++++++++++++++++++++++ middleware/i18n.go | 1 + middleware/profile.go | 226 ++++++++++++++++++++++++++++++++++ middleware/router.go | 0 middleware/session.go | 14 +++ pprof.go | 25 ---- router.go | 256 +++++++++++++------------------------- 14 files changed, 923 insertions(+), 658 deletions(-) create mode 100644 app.go create mode 100644 filter.go create mode 100644 middleware/error.go create mode 100644 middleware/i18n.go create mode 100644 middleware/profile.go delete mode 100644 middleware/router.go create mode 100644 middleware/session.go delete mode 100644 pprof.go 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 }}
+
+ Stack +
{{.Stack}}
+
+
+ + + +` + +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}} + + + +
+
+ +
+ {{.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:" + + "

") + 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:" + + "

") + 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:" + + "

") + 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:" + + "

") + 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." + + "

") + 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 {