1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-25 22:11:29 +00:00

change a log about new version

This commit is contained in:
astaxie 2013-09-10 00:00:11 +08:00
parent 5b9ae54441
commit bd61dd9ffc
14 changed files with 923 additions and 658 deletions

108
app.go Normal file
View File

@ -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
}

256
beego.go
View File

@ -1,221 +1,13 @@
package beego package beego
import ( import (
"fmt"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
"html/template"
"net"
"net/http" "net/http"
"net/http/fcgi"
"os"
"path" "path"
"runtime"
"time"
) )
const VERSION = "0.9.0" 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 { func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App {
BeeApp.Router(rootpath, c, mappingMethods...) BeeApp.Router(rootpath, c, mappingMethods...)
return BeeApp return BeeApp
@ -232,11 +24,6 @@ func AutoRouter(c ControllerInterface) *App {
return BeeApp 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 { func Errorhandler(err string, h http.HandlerFunc) *App {
ErrorMaps[err] = h ErrorMaps[err] = h
return BeeApp return BeeApp
@ -257,37 +44,18 @@ func DelStaticPath(url string) *App {
return BeeApp return BeeApp
} }
func Filter(filter http.HandlerFunc) *App { //action has four values:
BeeApp.Filter(filter) //BeforRouter
return BeeApp //AfterStatic
} //BeforExec
//AfterExec
func FilterParam(param string, filter http.HandlerFunc) *App { func AddFilter(pattern, action string, filter FilterFunc) *App {
BeeApp.FilterParam(param, filter) BeeApp.Filter(pattern, action, 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)
return BeeApp return BeeApp
} }
func Run() { func Run() {
//if AppConfigPath not In the conf/app.conf reParse config
if AppConfigPath != path.Join(AppPath, "conf", "app.conf") { if AppConfigPath != path.Join(AppPath, "conf", "app.conf") {
err := ParseConfig() err := ParseConfig()
if err != nil { if err != nil {
@ -296,20 +64,20 @@ func Run() {
} }
} }
} }
if PprofOn {
BeeApp.Router(`/debug/pprof`, &ProfController{})
BeeApp.Router(`/debug/pprof/:pp([\w]+)`, &ProfController{})
}
if SessionOn { if SessionOn {
GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime, SessionSavePath) GlobalSessions, _ = session.NewManager(SessionProvider, SessionName, SessionGCMaxLifetime, SessionSavePath)
go GlobalSessions.GC() go GlobalSessions.GC()
} }
if AutoRender {
err := BuildTemplate(ViewsPath) err := BuildTemplate(ViewsPath)
if err != nil { if err != nil {
if RunMode == "dev" { if RunMode == "dev" {
Warn(err) Warn(err)
} }
} }
}
registerErrorHander() registerErrorHander()
BeeApp.Run() BeeApp.Run()
} }

199
config.go
View File

@ -1,131 +1,91 @@
package beego package beego
import ( import (
"bufio" "github.com/astaxie/beego/config"
"bytes" "github.com/astaxie/beego/session"
"errors" "html/template"
"io"
"os" "os"
"path"
"runtime"
"strconv" "strconv"
"strings"
"sync"
"unicode"
) )
var ( var (
bComment = []byte{'#'} BeeApp *App
bEmpty = []byte{} AppName string
bEqual = []byte{'='} AppPath string
bDQuote = []byte{'"'} 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. func init() {
type Config struct { os.Chdir(path.Dir(os.Args[0]))
filename string BeeApp = NewApp()
comment map[int][]string // id: []{comment, key...}; id 1 is for main comment. AppPath, _ = os.Getwd()
data map[string]string // key: value StaticDir = make(map[string]string)
offset map[string]int64 // key: offset; for editing. TemplateCache = make(map[string]*template.Template)
sync.RWMutex HttpAddr = ""
} HttpPort = 8080
AppName = "beego"
// ParseFile creates a new Config and parses the file configuration from the RunMode = "dev" //default runmod
// named file. AutoRender = true
func LoadConfig(name string) (*Config, error) { RecoverPanic = true
file, err := os.Open(name) PprofOn = false
if err != nil { ViewsPath = "views"
return nil, err SessionOn = false
} SessionProvider = "memory"
SessionName = "beegosessionID"
cfg := &Config{ SessionGCMaxLifetime = 3600
file.Name(), SessionSavePath = ""
make(map[int][]string), UseFcgi = false
make(map[string]string), MaxMemory = 1 << 26 //64MB
make(map[string]int64), EnableGzip = false
sync.RWMutex{}, StaticDir["/static"] = "static"
} AppConfigPath = path.Join(AppPath, "conf", "app.conf")
cfg.Lock() HttpServerTimeOut = 0
defer cfg.Unlock() ErrorsShow = true
defer file.Close() XSRFKEY = "beegoxsrf"
XSRFExpire = 60
var comment bytes.Buffer TemplateLeft = "{{"
buf := bufio.NewReader(file) TemplateRight = "}}"
ParseConfig()
for nComment, off := 0, int64(1); ; { runtime.GOMAXPROCS(runtime.NumCPU())
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 ParseConfig() (err error) { func ParseConfig() (err error) {
AppConfig, err = LoadConfig(AppConfigPath) AppConfig, err := config.NewConfig("ini", AppConfigPath)
if err != nil { if err != nil {
return err return err
} else { } else {
@ -204,6 +164,15 @@ func ParseConfig() (err error) {
if tplright := AppConfig.String("templateright"); tplright != "" { if tplright := AppConfig.String("templateright"); tplright != "" {
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 return nil
} }

View File

@ -16,6 +16,11 @@ func (ctx *Context) Redirect(status int, localurl string) {
ctx.Output.SetStatus(status) 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) { func (ctx *Context) WriteString(content string) {
ctx.Output.Body([]byte(content)) ctx.Output.Body([]byte(content))
} }

View File

@ -17,7 +17,7 @@ import (
) )
type BeegoOutput struct { type BeegoOutput struct {
context *Context Context *Context
Status int Status int
EnableGzip bool EnableGzip bool
res http.ResponseWriter res http.ResponseWriter
@ -35,8 +35,8 @@ func (output *BeegoOutput) Header(key, val string) {
func (output *BeegoOutput) Body(content []byte) { func (output *BeegoOutput) Body(content []byte) {
output_writer := output.res.(io.Writer) output_writer := output.res.(io.Writer)
if output.EnableGzip == true && output.context.Input.Header("Accept-Encoding") != "" { if output.EnableGzip == true && output.Context.Input.Header("Accept-Encoding") != "" {
splitted := strings.SplitN(output.context.Input.Header("Accept-Encoding"), ",", -1) splitted := strings.SplitN(output.Context.Input.Header("Accept-Encoding"), ",", -1)
encodings := make([]string, len(splitted)) encodings := make([]string, len(splitted))
for i, val := range splitted { for i, val := range splitted {
@ -120,23 +120,40 @@ func sanitizeValue(v string) string {
return cookieValueSanitizer.Replace(v) 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") 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 { if err != nil {
http.Error(output.res, err.Error(), http.StatusInternalServerError)
return err return err
} }
if coding {
content = []byte(stringsToJson(string(content)))
}
output.Body(content) output.Body(content)
return nil 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") 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 { if err != nil {
http.Error(output.res, err.Error(), http.StatusInternalServerError)
return err return err
} }
callback := output.context.Input.Query("callback") callback := output.Context.Input.Query("callback")
if callback == "" { if callback == "" {
return errors.New(`"callback" parameter required`) return errors.New(`"callback" parameter required`)
} }
@ -148,10 +165,17 @@ func (output *BeegoOutput) Jsonp(data string) error {
return nil 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") 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 { if err != nil {
http.Error(output.res, err.Error(), http.StatusInternalServerError)
return err return err
} }
output.Body(content) output.Body(content)
@ -166,7 +190,7 @@ func (output *BeegoOutput) Download(file string) {
output.Header("Expires", "0") output.Header("Expires", "0")
output.Header("Cache-Control", "must-revalidate") output.Header("Cache-Control", "must-revalidate")
output.Header("Pragma", "public") 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) { func (output *BeegoOutput) ContentType(ext string) {
@ -219,3 +243,17 @@ func (output *BeegoOutput) IsClientError(status int) bool {
func (output *BeegoOutput) IsServerError(status int) bool { func (output *BeegoOutput) IsServerError(status int) bool {
return output.Status >= 500 && output.Status < 600 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
}

View File

@ -2,15 +2,12 @@ package beego
import ( import (
"bytes" "bytes"
"compress/flate"
"compress/gzip"
"crypto/hmac" "crypto/hmac"
"crypto/sha1" "crypto/sha1"
"encoding/base64" "encoding/base64"
"encoding/json"
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
"html/template" "html/template"
"io" "io"
@ -26,7 +23,7 @@ import (
) )
type Controller struct { type Controller struct {
Ctx *Context Ctx *context.Context
Data map[interface{}]interface{} Data map[interface{}]interface{}
ChildName string ChildName string
TplNames string TplNames string
@ -39,7 +36,7 @@ type Controller struct {
} }
type ControllerInterface interface { type ControllerInterface interface {
Init(ct *Context, cn string) Init(ct *Context, childName string)
Prepare() Prepare()
Get() Get()
Post() Post()
@ -52,11 +49,11 @@ type ControllerInterface interface {
Render() error 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.Data = make(map[interface{}]interface{})
c.Layout = "" c.Layout = ""
c.TplNames = "" c.TplNames = ""
c.ChildName = cn c.ChildName = childName
c.Ctx = ctx c.Ctx = ctx
c.TplExt = "tpl" c.TplExt = "tpl"
} }
@ -109,8 +106,8 @@ func (c *Controller) Render() error {
if err != nil { if err != nil {
return err return err
} else { } else {
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8") c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8")
c.writeToWriter(rb) c.Ctx.Output.Body(rb)
} }
return nil return nil
} }
@ -172,41 +169,6 @@ func (c *Controller) RenderBytes() ([]byte, error) {
return []byte{}, nil 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) { func (c *Controller) Redirect(url string, code int) {
c.Ctx.Redirect(code, url) c.Ctx.Redirect(code, url)
} }
@ -216,63 +178,37 @@ func (c *Controller) Abort(code string) {
} }
func (c *Controller) ServeJson(encoding ...bool) { func (c *Controller) ServeJson(encoding ...bool) {
var content []byte var hasIndent bool
var err error var hasencoding bool
if RunMode == "prod" { if RunMode == "prod" {
content, err = json.Marshal(c.Data["json"]) hasIndent = false
} else { } 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 { 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() { func (c *Controller) ServeJsonp() {
var content []byte var hasIndent bool
var err error
if RunMode == "prod" { if RunMode == "prod" {
content, err = json.Marshal(c.Data["jsonp"]) hasIndent = false
} else { } else {
content, err = json.MarshalIndent(c.Data["jsonp"], "", " ") hasIndent = true
} }
if err != nil { c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent)
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())
} }
func (c *Controller) ServeXml() { func (c *Controller) ServeXml() {
var content []byte var hasIndent bool
var err error
if RunMode == "prod" { if RunMode == "prod" {
content, err = xml.Marshal(c.Data["xml"]) hasIndent = false
} else { } else {
content, err = xml.MarshalIndent(c.Data["xml"], "", " ") hasIndent = true
} }
if err != nil { c.Ctx.Output.Xml(c.Data["xml"], hasIndent)
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)
} }
func (c *Controller) Input() url.Values { 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)) 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) { func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, error) {
return c.Ctx.Request.FormFile(key) return c.Ctx.Request.FormFile(key)
} }
@ -365,7 +305,7 @@ func (c *Controller) DestroySession() {
} }
func (c *Controller) IsAjax() bool { 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 { func (c *Controller) XsrfToken() string {

31
filter.go Normal file
View File

@ -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
}

280
middleware/error.go Normal file
View File

@ -0,0 +1,280 @@
package middleware
import (
"fmt"
"html/template"
"net/http"
"runtime"
)
var tpl = `
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>beego application error</title>
<style>
html, body, body * {padding: 0; margin: 0;}
#header {background:#ffd; border-bottom:solid 2px #A31515; padding: 20px 10px;}
#header h2{ }
#footer {border-top:solid 1px #aaa; padding: 5px 10px; font-size: 12px; color:green;}
#content {padding: 5px;}
#content .stack b{ font-size: 13px; color: red;}
#content .stack pre{padding-left: 10px;}
table {}
td.t {text-align: right; padding-right: 5px; color: #888;}
</style>
<script type="text/javascript">
</script>
</head>
<body>
<div id="header">
<h2>{{.AppError}}</h2>
</div>
<div id="content">
<table>
<tr>
<td class="t">Request Method: </td><td>{{.RequestMethod}}</td>
</tr>
<tr>
<td class="t">Request URL: </td><td>{{.RequestURL}}</td>
</tr>
<tr>
<td class="t">RemoteAddr: </td><td>{{.RemoteAddr }}</td>
</tr>
</table>
<div class="stack">
<b>Stack</b>
<pre>{{.Stack}}</pre>
</div>
</div>
<div id="footer">
<p>beego {{ .BeegoVersion }} (beego framework)</p>
<p>golang version: {{.GoVersion}}</p>
</div>
</body>
</html>
`
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 = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{.Title}}</title>
<style type="text/css">
* {
margin:0;
padding:0;
}
body {
background-color:#EFEFEF;
font: .9em "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}
#wrapper{
width:600px;
margin:40px auto 0;
text-align:center;
-moz-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
-webkit-box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
box-shadow: 5px 5px 10px rgba(0,0,0,0.3);
}
#wrapper h1{
color:#FFF;
text-align:center;
margin-bottom:20px;
}
#wrapper a{
display:block;
font-size:.9em;
padding-top:20px;
color:#FFF;
text-decoration:none;
text-align:center;
}
#container {
width:600px;
padding-bottom:15px;
background-color:#FFFFFF;
}
.navtop{
height:40px;
background-color:#24B2EB;
padding:13px;
}
.content {
padding:10px 10px 25px;
background: #FFFFFF;
margin:;
color:#333;
}
a.button{
color:white;
padding:15px 20px;
text-shadow:1px 1px 0 #00A5FF;
font-weight:bold;
text-align:center;
border:1px solid #24B2EB;
margin:0px 200px;
clear:both;
background-color: #24B2EB;
border-radius:100px;
-moz-border-radius:100px;
-webkit-border-radius:100px;
}
a.button:hover{
text-decoration:none;
background-color: #24B2EB;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="container">
<div class="navtop">
<h1>{{.Title}}</h1>
</div>
<div id="content">
{{.Content}}
<a href="/" title="Home" class="button">Go Home</a><br />
<br>power by beego {{.BeegoVersion}}
</div>
</div>
</div>
</body>
</html>
`
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("<br>The Page You have requested flown the coop." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>The page has moved" +
"<br>The page no longer exists" +
"<br>You were looking for your puppy and got lost" +
"<br>You like 404 pages" +
"</ul>")
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("<br>The Page You have requested can't authorized." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Check the credentials that you supplied" +
"<br>Check the address for errors" +
"</ul>")
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("<br>The Page You have requested forbidden." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br>Your address may be blocked" +
"<br>The site may be disabled" +
"<br>You need to log in" +
"</ul>")
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("<br>The Page You have requested unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br><br>The page is overloaded" +
"<br>Please try again later." +
"</ul>")
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("<br>The Page You have requested has down now." +
"<br><br><ul>" +
"<br>simply try again later" +
"<br>you should report the fault to the website administrator" +
"</ul>")
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
}
}

1
middleware/i18n.go Normal file
View File

@ -0,0 +1 @@
package middleware

226
middleware/profile.go Normal file
View File

@ -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)
}
}
}

View File

14
middleware/session.go Normal file
View File

@ -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()
}

View File

@ -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)
}

252
router.go
View File

@ -1,9 +1,8 @@
package beego package beego
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" beecontext "github.com/astaxie/beego/context"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -25,22 +24,11 @@ type controllerInfo struct {
hasMethod bool hasMethod bool
} }
type userHandler struct {
pattern string
regex *regexp.Regexp
params map[int]string
h http.Handler
}
type ControllerRegistor struct { type ControllerRegistor struct {
routers []*controllerInfo routers []*controllerInfo
fixrouters []*controllerInfo fixrouters []*controllerInfo
enableFilter bool enableFilter bool
filters []http.HandlerFunc filters map[string][]*FilterRouter
enableAfter bool
afterFilters []http.HandlerFunc
enableUser bool
userHandlers map[string]*userHandler
enableAuto bool enableAuto bool
autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type autoRouter map[string]map[string]reflect.Type //key:controller key:method value:reflect.type
} }
@ -48,8 +36,8 @@ type ControllerRegistor struct {
func NewControllerRegistor() *ControllerRegistor { func NewControllerRegistor() *ControllerRegistor {
return &ControllerRegistor{ return &ControllerRegistor{
routers: make([]*controllerInfo, 0), routers: make([]*controllerInfo, 0),
userHandlers: make(map[string]*userHandler),
autoRouter: make(map[string]map[string]reflect.Type), 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) { // Filter adds the middleware filter.
p.enableUser = true func (p *ControllerRegistor) AddFilter(pattern, action string, filter FilterFunc) {
parts := strings.Split(pattern, "/") p.enableFilter = true
mr := new(FilterRouter)
mr.filterFunc = filter
parts := strings.Split(pattern, "/")
j := 0 j := 0
params := make(map[int]string)
for i, part := range parts { for i, part := range parts {
if strings.HasPrefix(part, ":") { if strings.HasPrefix(part, ":") {
expr := "([^/]+)" expr := "(.+)"
//a user may choose to override the defult expression //a user may choose to override the defult expression
// similar to expressjs: /user/:id([0-9]+) // similar to expressjs: /user/:id([0-9]+)
if index := strings.Index(part, "("); index != -1 { if index := strings.Index(part, "("); index != -1 {
expr = part[index:] expr = part[index:]
part = 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 parts[i] = expr
j++ j++
} }
} }
if j == 0 { 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
pattern = strings.Join(parts, "/") pattern = strings.Join(parts, "/")
regex, regexErr := regexp.Compile(pattern) regex, regexErr := regexp.Compile(pattern)
if regexErr != nil { if regexErr != nil {
@ -218,73 +210,11 @@ func (p *ControllerRegistor) AddHandler(pattern string, c http.Handler) {
panic(regexErr) panic(regexErr)
return return
} }
mr.regex = regex
//now create the Route mr.hasregex = true
uh := &userHandler{}
uh.regex = regex
uh.params = params
uh.pattern = pattern
uh.h = c
p.userHandlers[pattern] = uh
} }
} mr.pattern = pattern
p.filters[action] = append(p.filters[action], mr)
// 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)
}
})
} }
// AutoRoute // AutoRoute
@ -320,13 +250,30 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}() }()
w := &responseWriter{writer: rw} w := &responseWriter{writer: rw}
w.Header().Set("Server", "beegoServer") 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 runrouter *controllerInfo
var findrouter bool var findrouter bool
params := make(map[string]string) 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 //static file server
for prefix, staticDir := range StaticDir { for prefix, staticDir := range StaticDir {
if r.URL.Path == "/favicon.ico" { 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 requestPath := r.URL.Path
var requestbody []byte
if CopyRequestBody { if CopyRequestBody {
requestbody, _ = ioutil.ReadAll(r.Body) context.Input.Body()
r.Body.Close()
bf := bytes.NewBuffer(requestbody)
r.Body = ioutil.NopCloser(bf)
} }
r.ParseMultipartForm(MaxMemory) 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 //first find path from the fixrouters to Improve Performance
for _, route := range p.fixrouters { for _, route := range p.fixrouters {
n := len(requestPath) n := len(requestPath)
@ -471,23 +379,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if runrouter != nil { if runrouter != nil {
//execute middleware filters //execute middleware filters
if p.enableFilter { if p.enableFilter {
for _, filter := range p.filters { if l, ok := p.filters["BeforExec"]; ok {
filter(w, r) for _, filterR := range l {
if w.started { if filterR.ValidRouter(r.URL.Path) {
return filterR.filterFunc(context)
}
} }
} }
} }
//Invoke the request handler //Invoke the request handler
vc := reflect.New(runrouter.controllerType) vc := reflect.New(runrouter.controllerType)
//call the controller init function //call the controller init function
init := vc.MethodByName("Init") init := vc.MethodByName("Init")
in := make([]reflect.Value, 2) in := make([]reflect.Value, 2)
ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} in[0] = reflect.ValueOf(context)
in[0] = reflect.ValueOf(ct)
in[1] = reflect.ValueOf(runrouter.controllerType.Name()) in[1] = reflect.ValueOf(runrouter.controllerType.Name())
init.Call(in) init.Call(in)
//call prepare function //call prepare function
@ -617,14 +523,16 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
} }
} }
method = vc.MethodByName("Finish") method = vc.MethodByName("Finish")
method.Call(in) method.Call(in)
//execute middleware filters //execute middleware filters
if p.enableAfter { if p.enableFilter {
for _, filter := range p.afterFilters { if l, ok := p.filters["AfterExec"]; ok {
filter(w, r) for _, filterR := range l {
if w.started { if filterR.ValidRouter(r.URL.Path) {
return 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)) { if strings.HasPrefix(strings.ToLower(requestPath), "/"+cName+"/"+strings.ToLower(mName)) {
//execute middleware filters //execute middleware filters
if p.enableFilter { if p.enableFilter {
for _, filter := range p.filters { if l, ok := p.filters["BeforExec"]; ok {
filter(w, r) for _, filterR := range l {
if w.started { if filterR.ValidRouter(r.URL.Path) {
return filterR.filterFunc(context)
}
} }
} }
} }
@ -672,9 +581,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
//call the controller init function //call the controller init function
init := vc.MethodByName("Init") init := vc.MethodByName("Init")
in := make([]reflect.Value, 2) in := make([]reflect.Value, 2)
ct := &Context{ResponseWriter: w, Request: r, Params: params, RequestBody: requestbody} in[0] = reflect.ValueOf(context)
in[0] = reflect.ValueOf(ct)
in[1] = reflect.ValueOf(controllerType.Name()) in[1] = reflect.ValueOf(controllerType.Name())
init.Call(in) init.Call(in)
//call prepare function //call prepare function
@ -702,11 +609,12 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
method = vc.MethodByName("Finish") method = vc.MethodByName("Finish")
method.Call(in) method.Call(in)
//execute middleware filters //execute middleware filters
if p.enableAfter { if p.enableFilter {
for _, filter := range p.afterFilters { if l, ok := p.filters["AfterExec"]; ok {
filter(w, r) for _, filterR := range l {
if w.started { if filterR.ValidRouter(r.URL.Path) {
return filterR.filterFunc(context)
}
} }
} }
} }
@ -714,6 +622,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request)
method.Call(in) method.Call(in)
// set find // set find
findrouter = true 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 no matches to url, throw a not found exception
if !findrouter { if !findrouter {
if h, ok := ErrorMaps["404"]; ok { if h, ok := ErrorMaps["404"]; ok {