init framwork
Showing
10 changed files
with
890 additions
and
0 deletions
File moved
beego.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "net/http" | ||
| 6 | "os" | ||
| 7 | "path" | ||
| 8 | ) | ||
| 9 | |||
| 10 | var ( | ||
| 11 | BeeApp *App | ||
| 12 | AppName string | ||
| 13 | AppPath string | ||
| 14 | StaticDir map[string]string | ||
| 15 | HttpAddr string | ||
| 16 | HttpPort int | ||
| 17 | RecoverPanic bool | ||
| 18 | AutoRender bool | ||
| 19 | ViewsPath string | ||
| 20 | RunMode string //"dev" or "prod" | ||
| 21 | AppConfig *Config | ||
| 22 | ) | ||
| 23 | |||
| 24 | func init() { | ||
| 25 | BeeApp = NewApp() | ||
| 26 | AppPath, _ = os.Getwd() | ||
| 27 | StaticDir = make(map[string]string) | ||
| 28 | var err error | ||
| 29 | AppConfig, err = LoadConfig(path.Join(AppPath, "conf", "app.conf")) | ||
| 30 | if err != nil { | ||
| 31 | Trace("open Config err:", err) | ||
| 32 | HttpAddr = "" | ||
| 33 | HttpPort = 8080 | ||
| 34 | AppName = "beego" | ||
| 35 | RunMode = "prod" | ||
| 36 | AutoRender = true | ||
| 37 | RecoverPanic = true | ||
| 38 | ViewsPath = "views" | ||
| 39 | } else { | ||
| 40 | HttpAddr = AppConfig.String("httpaddr") | ||
| 41 | if v, err := AppConfig.Int("httpport"); err != nil { | ||
| 42 | HttpPort = 8080 | ||
| 43 | } else { | ||
| 44 | HttpPort = v | ||
| 45 | } | ||
| 46 | AppName = AppConfig.String("appname") | ||
| 47 | if runmode := AppConfig.String("runmode"); runmode != "" { | ||
| 48 | RunMode = runmode | ||
| 49 | } else { | ||
| 50 | RunMode = "prod" | ||
| 51 | } | ||
| 52 | if ar, err := AppConfig.Bool("autorender"); err != nil { | ||
| 53 | AutoRender = true | ||
| 54 | } else { | ||
| 55 | AutoRender = ar | ||
| 56 | } | ||
| 57 | if ar, err := AppConfig.Bool("autorecover"); err != nil { | ||
| 58 | RecoverPanic = true | ||
| 59 | } else { | ||
| 60 | RecoverPanic = ar | ||
| 61 | } | ||
| 62 | if views := AppConfig.String("viewspath"); views == "" { | ||
| 63 | ViewsPath = "views" | ||
| 64 | } else { | ||
| 65 | ViewsPath = views | ||
| 66 | } | ||
| 67 | } | ||
| 68 | StaticDir["/static"] = "static" | ||
| 69 | |||
| 70 | } | ||
| 71 | |||
| 72 | type App struct { | ||
| 73 | Handlers *ControllerRegistor | ||
| 74 | } | ||
| 75 | |||
| 76 | // New returns a new PatternServeMux. | ||
| 77 | func NewApp() *App { | ||
| 78 | cr := NewControllerRegistor() | ||
| 79 | app := &App{Handlers: cr} | ||
| 80 | return app | ||
| 81 | } | ||
| 82 | |||
| 83 | func (app *App) Run() { | ||
| 84 | addr := fmt.Sprintf("%s:%d", HttpAddr, HttpPort) | ||
| 85 | err := http.ListenAndServe(addr, app.Handlers) | ||
| 86 | if err != nil { | ||
| 87 | BeeLogger.Fatal("ListenAndServe: ", err) | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | func (app *App) RegisterController(path string, c ControllerInterface) *App { | ||
| 92 | app.Handlers.Add(path, c) | ||
| 93 | return app | ||
| 94 | } | ||
| 95 | |||
| 96 | func (app *App) SetViewsPath(path string) *App { | ||
| 97 | ViewsPath = path | ||
| 98 | return app | ||
| 99 | } | ||
| 100 | |||
| 101 | func (app *App) SetStaticPath(url string, path string) *App { | ||
| 102 | StaticDir[url] = path | ||
| 103 | return app | ||
| 104 | } | ||
| 105 | |||
| 106 | func (app *App) ErrorLog(ctx *Context) { | ||
| 107 | 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) | ||
| 108 | } | ||
| 109 | |||
| 110 | func (app *App) AccessLog(ctx *Context) { | ||
| 111 | 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) | ||
| 112 | } |
config.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bufio" | ||
| 5 | "bytes" | ||
| 6 | "errors" | ||
| 7 | "io" | ||
| 8 | "os" | ||
| 9 | "strconv" | ||
| 10 | "strings" | ||
| 11 | "sync" | ||
| 12 | "unicode" | ||
| 13 | ) | ||
| 14 | |||
| 15 | var ( | ||
| 16 | bComment = []byte{'#'} | ||
| 17 | bEmpty = []byte{} | ||
| 18 | bEqual = []byte{'='} | ||
| 19 | bDQuote = []byte{'"'} | ||
| 20 | ) | ||
| 21 | |||
| 22 | // A Config represents the configuration. | ||
| 23 | type Config struct { | ||
| 24 | filename string | ||
| 25 | comment map[int][]string // id: []{comment, key...}; id 1 is for main comment. | ||
| 26 | data map[string]string // key: value | ||
| 27 | offset map[string]int64 // key: offset; for editing. | ||
| 28 | sync.RWMutex | ||
| 29 | } | ||
| 30 | |||
| 31 | // ParseFile creates a new Config and parses the file configuration from the | ||
| 32 | // named file. | ||
| 33 | func LoadConfig(name string) (*Config, error) { | ||
| 34 | file, err := os.Open(name) | ||
| 35 | if err != nil { | ||
| 36 | return nil, err | ||
| 37 | } | ||
| 38 | |||
| 39 | cfg := &Config{ | ||
| 40 | file.Name(), | ||
| 41 | make(map[int][]string), | ||
| 42 | make(map[string]string), | ||
| 43 | make(map[string]int64), | ||
| 44 | sync.RWMutex{}, | ||
| 45 | } | ||
| 46 | cfg.Lock() | ||
| 47 | defer cfg.Unlock() | ||
| 48 | defer file.Close() | ||
| 49 | |||
| 50 | var comment bytes.Buffer | ||
| 51 | buf := bufio.NewReader(file) | ||
| 52 | |||
| 53 | for nComment, off := 0, int64(1); ; { | ||
| 54 | line, _, err := buf.ReadLine() | ||
| 55 | if err == io.EOF { | ||
| 56 | break | ||
| 57 | } | ||
| 58 | if bytes.Equal(line, bEmpty) { | ||
| 59 | continue | ||
| 60 | } | ||
| 61 | |||
| 62 | off += int64(len(line)) | ||
| 63 | |||
| 64 | if bytes.HasPrefix(line, bComment) { | ||
| 65 | line = bytes.TrimLeft(line, "#") | ||
| 66 | line = bytes.TrimLeftFunc(line, unicode.IsSpace) | ||
| 67 | comment.Write(line) | ||
| 68 | comment.WriteByte('\n') | ||
| 69 | continue | ||
| 70 | } | ||
| 71 | if comment.Len() != 0 { | ||
| 72 | cfg.comment[nComment] = []string{comment.String()} | ||
| 73 | comment.Reset() | ||
| 74 | nComment++ | ||
| 75 | } | ||
| 76 | |||
| 77 | val := bytes.SplitN(line, bEqual, 2) | ||
| 78 | if bytes.HasPrefix(val[1], bDQuote) { | ||
| 79 | val[1] = bytes.Trim(val[1], `"`) | ||
| 80 | } | ||
| 81 | |||
| 82 | key := strings.TrimSpace(string(val[0])) | ||
| 83 | cfg.comment[nComment-1] = append(cfg.comment[nComment-1], key) | ||
| 84 | cfg.data[key] = strings.TrimSpace(string(val[1])) | ||
| 85 | cfg.offset[key] = off | ||
| 86 | } | ||
| 87 | return cfg, nil | ||
| 88 | } | ||
| 89 | |||
| 90 | // Bool returns the boolean value for a given key. | ||
| 91 | func (c *Config) Bool(key string) (bool, error) { | ||
| 92 | return strconv.ParseBool(c.data[key]) | ||
| 93 | } | ||
| 94 | |||
| 95 | // Int returns the integer value for a given key. | ||
| 96 | func (c *Config) Int(key string) (int, error) { | ||
| 97 | return strconv.Atoi(c.data[key]) | ||
| 98 | } | ||
| 99 | |||
| 100 | // Float returns the float value for a given key. | ||
| 101 | func (c *Config) Float(key string) (float64, error) { | ||
| 102 | return strconv.ParseFloat(c.data[key], 64) | ||
| 103 | } | ||
| 104 | |||
| 105 | // String returns the string value for a given key. | ||
| 106 | func (c *Config) String(key string) string { | ||
| 107 | return c.data[key] | ||
| 108 | } | ||
| 109 | |||
| 110 | // WriteValue writes a new value for key. | ||
| 111 | func (c *Config) SetValue(key, value string) error { | ||
| 112 | c.Lock() | ||
| 113 | defer c.Unlock() | ||
| 114 | |||
| 115 | if _, found := c.data[key]; !found { | ||
| 116 | return errors.New("key not found: " + key) | ||
| 117 | } | ||
| 118 | |||
| 119 | c.data[key] = value | ||
| 120 | return nil | ||
| 121 | } |
context.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "fmt" | ||
| 5 | "mime" | ||
| 6 | "net/http" | ||
| 7 | "strings" | ||
| 8 | "time" | ||
| 9 | ) | ||
| 10 | |||
| 11 | type Context struct { | ||
| 12 | ResponseWriter http.ResponseWriter | ||
| 13 | Request *http.Request | ||
| 14 | Params map[string]string | ||
| 15 | } | ||
| 16 | |||
| 17 | func (ctx *Context) WriteString(content string) { | ||
| 18 | ctx.ResponseWriter.Write([]byte(content)) | ||
| 19 | } | ||
| 20 | |||
| 21 | func (ctx *Context) Abort(status int, body string) { | ||
| 22 | ctx.ResponseWriter.WriteHeader(status) | ||
| 23 | ctx.ResponseWriter.Write([]byte(body)) | ||
| 24 | } | ||
| 25 | |||
| 26 | func (ctx *Context) Redirect(status int, url_ string) { | ||
| 27 | ctx.ResponseWriter.Header().Set("Location", url_) | ||
| 28 | ctx.ResponseWriter.WriteHeader(status) | ||
| 29 | ctx.ResponseWriter.Write([]byte("Redirecting to: " + url_)) | ||
| 30 | } | ||
| 31 | |||
| 32 | func (ctx *Context) NotModified() { | ||
| 33 | ctx.ResponseWriter.WriteHeader(304) | ||
| 34 | } | ||
| 35 | |||
| 36 | func (ctx *Context) NotFound(message string) { | ||
| 37 | ctx.ResponseWriter.WriteHeader(404) | ||
| 38 | ctx.ResponseWriter.Write([]byte(message)) | ||
| 39 | } | ||
| 40 | |||
| 41 | //Sets the content type by extension, as defined in the mime package. | ||
| 42 | //For example, ctx.ContentType("json") sets the content-type to "application/json" | ||
| 43 | func (ctx *Context) ContentType(ext string) { | ||
| 44 | if !strings.HasPrefix(ext, ".") { | ||
| 45 | ext = "." + ext | ||
| 46 | } | ||
| 47 | ctype := mime.TypeByExtension(ext) | ||
| 48 | if ctype != "" { | ||
| 49 | ctx.ResponseWriter.Header().Set("Content-Type", ctype) | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | func (ctx *Context) SetHeader(hdr string, val string, unique bool) { | ||
| 54 | if unique { | ||
| 55 | ctx.ResponseWriter.Header().Set(hdr, val) | ||
| 56 | } else { | ||
| 57 | ctx.ResponseWriter.Header().Add(hdr, val) | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | //Sets a cookie -- duration is the amount of time in seconds. 0 = forever | ||
| 62 | func (ctx *Context) SetCookie(name string, value string, age int64) { | ||
| 63 | var utctime time.Time | ||
| 64 | if age == 0 { | ||
| 65 | // 2^31 - 1 seconds (roughly 2038) | ||
| 66 | utctime = time.Unix(2147483647, 0) | ||
| 67 | } else { | ||
| 68 | utctime = time.Unix(time.Now().Unix()+age, 0) | ||
| 69 | } | ||
| 70 | cookie := fmt.Sprintf("%s=%s; expires=%s", name, value, webTime(utctime)) | ||
| 71 | ctx.SetHeader("Set-Cookie", cookie, false) | ||
| 72 | } |
controller.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "bytes" | ||
| 5 | "encoding/json" | ||
| 6 | "encoding/xml" | ||
| 7 | "html/template" | ||
| 8 | "io/ioutil" | ||
| 9 | "net/http" | ||
| 10 | "path" | ||
| 11 | "strconv" | ||
| 12 | ) | ||
| 13 | |||
| 14 | type Controller struct { | ||
| 15 | Ct *Context | ||
| 16 | Tpl *template.Template | ||
| 17 | Data map[interface{}]interface{} | ||
| 18 | ChildName string | ||
| 19 | TplNames string | ||
| 20 | Layout string | ||
| 21 | TplExt string | ||
| 22 | } | ||
| 23 | |||
| 24 | type ControllerInterface interface { | ||
| 25 | Init(ct *Context, cn string) | ||
| 26 | Prepare() | ||
| 27 | Get() | ||
| 28 | Post() | ||
| 29 | Delete() | ||
| 30 | Put() | ||
| 31 | Head() | ||
| 32 | Patch() | ||
| 33 | Options() | ||
| 34 | Finish() | ||
| 35 | Render() error | ||
| 36 | } | ||
| 37 | |||
| 38 | func (c *Controller) Init(ct *Context, cn string) { | ||
| 39 | c.Data = make(map[interface{}]interface{}) | ||
| 40 | c.Tpl = template.New(cn + ct.Request.Method) | ||
| 41 | c.Tpl = c.Tpl.Funcs(beegoTplFuncMap) | ||
| 42 | c.Layout = "" | ||
| 43 | c.TplNames = "" | ||
| 44 | c.ChildName = cn | ||
| 45 | c.Ct = ct | ||
| 46 | c.TplExt = "tpl" | ||
| 47 | |||
| 48 | } | ||
| 49 | |||
| 50 | func (c *Controller) Prepare() { | ||
| 51 | |||
| 52 | } | ||
| 53 | |||
| 54 | func (c *Controller) Finish() { | ||
| 55 | |||
| 56 | } | ||
| 57 | |||
| 58 | func (c *Controller) Get() { | ||
| 59 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 60 | } | ||
| 61 | |||
| 62 | func (c *Controller) Post() { | ||
| 63 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 64 | } | ||
| 65 | |||
| 66 | func (c *Controller) Delete() { | ||
| 67 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 68 | } | ||
| 69 | |||
| 70 | func (c *Controller) Put() { | ||
| 71 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 72 | } | ||
| 73 | |||
| 74 | func (c *Controller) Head() { | ||
| 75 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 76 | } | ||
| 77 | |||
| 78 | func (c *Controller) Patch() { | ||
| 79 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 80 | } | ||
| 81 | |||
| 82 | func (c *Controller) Options() { | ||
| 83 | http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405) | ||
| 84 | } | ||
| 85 | |||
| 86 | func (c *Controller) Render() error { | ||
| 87 | //if the controller has set layout, then first get the tplname's content set the content to the layout | ||
| 88 | if c.Layout != "" { | ||
| 89 | if c.TplNames == "" { | ||
| 90 | c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt | ||
| 91 | } | ||
| 92 | t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames), path.Join(ViewsPath, c.Layout)) | ||
| 93 | if err != nil { | ||
| 94 | Trace("template ParseFiles err:", err) | ||
| 95 | } | ||
| 96 | _, file := path.Split(c.TplNames) | ||
| 97 | newbytes := bytes.NewBufferString("") | ||
| 98 | t.ExecuteTemplate(newbytes, file, c.Data) | ||
| 99 | tplcontent, _ := ioutil.ReadAll(newbytes) | ||
| 100 | c.Data["LayoutContent"] = template.HTML(string(tplcontent)) | ||
| 101 | _, file = path.Split(c.Layout) | ||
| 102 | err = t.ExecuteTemplate(c.Ct.ResponseWriter, file, c.Data) | ||
| 103 | if err != nil { | ||
| 104 | Trace("template Execute err:", err) | ||
| 105 | } | ||
| 106 | } else { | ||
| 107 | if c.TplNames == "" { | ||
| 108 | c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt | ||
| 109 | } | ||
| 110 | t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames)) | ||
| 111 | if err != nil { | ||
| 112 | Trace("template ParseFiles err:", err) | ||
| 113 | } | ||
| 114 | _, file := path.Split(c.TplNames) | ||
| 115 | err = t.ExecuteTemplate(c.Ct.ResponseWriter, file, c.Data) | ||
| 116 | if err != nil { | ||
| 117 | Trace("template Execute err:", err) | ||
| 118 | } | ||
| 119 | } | ||
| 120 | return nil | ||
| 121 | } | ||
| 122 | |||
| 123 | func (c *Controller) Redirect(url string, code int) { | ||
| 124 | c.Ct.Redirect(code, url) | ||
| 125 | } | ||
| 126 | |||
| 127 | func (c *Controller) ServeJson() { | ||
| 128 | content, err := json.MarshalIndent(c.Data, "", " ") | ||
| 129 | if err != nil { | ||
| 130 | http.Error(c.Ct.ResponseWriter, err.Error(), http.StatusInternalServerError) | ||
| 131 | return | ||
| 132 | } | ||
| 133 | c.Ct.SetHeader("Content-Length", strconv.Itoa(len(content)), true) | ||
| 134 | c.Ct.ContentType("json") | ||
| 135 | c.Ct.ResponseWriter.Write(content) | ||
| 136 | } | ||
| 137 | |||
| 138 | func (c *Controller) ServeXml() { | ||
| 139 | content, err := xml.Marshal(c.Data) | ||
| 140 | if err != nil { | ||
| 141 | http.Error(c.Ct.ResponseWriter, err.Error(), http.StatusInternalServerError) | ||
| 142 | return | ||
| 143 | } | ||
| 144 | c.Ct.SetHeader("Content-Length", strconv.Itoa(len(content)), true) | ||
| 145 | c.Ct.ContentType("xml") | ||
| 146 | c.Ct.ResponseWriter.Write(content) | ||
| 147 | } |
log.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "log" | ||
| 5 | "os" | ||
| 6 | ) | ||
| 7 | |||
| 8 | //-------------------- | ||
| 9 | // LOG LEVEL | ||
| 10 | //-------------------- | ||
| 11 | |||
| 12 | // Log levels to control the logging output. | ||
| 13 | const ( | ||
| 14 | LevelTrace = iota | ||
| 15 | LevelDebug | ||
| 16 | LevelInfo | ||
| 17 | LevelWarning | ||
| 18 | LevelError | ||
| 19 | LevelCritical | ||
| 20 | ) | ||
| 21 | |||
| 22 | // logLevel controls the global log level used by the logger. | ||
| 23 | var level = LevelTrace | ||
| 24 | |||
| 25 | // LogLevel returns the global log level and can be used in | ||
| 26 | // own implementations of the logger interface. | ||
| 27 | func Level() int { | ||
| 28 | return level | ||
| 29 | } | ||
| 30 | |||
| 31 | // SetLogLevel sets the global log level used by the simple | ||
| 32 | // logger. | ||
| 33 | func SetLevel(l int) { | ||
| 34 | level = l | ||
| 35 | } | ||
| 36 | |||
| 37 | // logger references the used application logger. | ||
| 38 | var BeeLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime) | ||
| 39 | |||
| 40 | // SetLogger sets a new logger. | ||
| 41 | func SetLogger(l *log.Logger) { | ||
| 42 | BeeLogger = l | ||
| 43 | } | ||
| 44 | |||
| 45 | // Trace logs a message at trace level. | ||
| 46 | func Trace(v ...interface{}) { | ||
| 47 | if level <= LevelTrace { | ||
| 48 | BeeLogger.Printf("[T] %v\n", v) | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | // Debug logs a message at debug level. | ||
| 53 | func Debug(v ...interface{}) { | ||
| 54 | if level <= LevelDebug { | ||
| 55 | BeeLogger.Printf("[D] %v\n", v) | ||
| 56 | } | ||
| 57 | } | ||
| 58 | |||
| 59 | // Info logs a message at info level. | ||
| 60 | func Info(v ...interface{}) { | ||
| 61 | if level <= LevelInfo { | ||
| 62 | BeeLogger.Printf("[I] %v\n", v) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | // Warning logs a message at warning level. | ||
| 67 | func Warn(v ...interface{}) { | ||
| 68 | if level <= LevelWarning { | ||
| 69 | BeeLogger.Printf("[W] %v\n", v) | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | // Error logs a message at error level. | ||
| 74 | func Error(v ...interface{}) { | ||
| 75 | if level <= LevelError { | ||
| 76 | BeeLogger.Printf("[E] %v\n", v) | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | // Critical logs a message at critical level. | ||
| 81 | func Critical(v ...interface{}) { | ||
| 82 | if level <= LevelCritical { | ||
| 83 | BeeLogger.Printf("[C] %v\n", v) | ||
| 84 | } | ||
| 85 | } |
router.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "net/http" | ||
| 5 | "net/url" | ||
| 6 | "reflect" | ||
| 7 | "regexp" | ||
| 8 | "runtime" | ||
| 9 | "strings" | ||
| 10 | ) | ||
| 11 | |||
| 12 | type controllerInfo struct { | ||
| 13 | pattern string | ||
| 14 | regex *regexp.Regexp | ||
| 15 | params map[int]string | ||
| 16 | controllerType reflect.Type | ||
| 17 | } | ||
| 18 | |||
| 19 | type ControllerRegistor struct { | ||
| 20 | routers []*controllerInfo | ||
| 21 | fixrouters []*controllerInfo | ||
| 22 | filters []http.HandlerFunc | ||
| 23 | } | ||
| 24 | |||
| 25 | func NewControllerRegistor() *ControllerRegistor { | ||
| 26 | return &ControllerRegistor{routers: make([]*controllerInfo, 0)} | ||
| 27 | } | ||
| 28 | |||
| 29 | func (p *ControllerRegistor) Add(pattern string, c ControllerInterface) { | ||
| 30 | parts := strings.Split(pattern, "/") | ||
| 31 | |||
| 32 | j := 0 | ||
| 33 | params := make(map[int]string) | ||
| 34 | for i, part := range parts { | ||
| 35 | if strings.HasPrefix(part, ":") { | ||
| 36 | expr := "([^/]+)" | ||
| 37 | //a user may choose to override the defult expression | ||
| 38 | // similar to expressjs: ‘/user/:id([0-9]+)’ | ||
| 39 | if index := strings.Index(part, "("); index != -1 { | ||
| 40 | expr = part[index:] | ||
| 41 | part = part[:index] | ||
| 42 | } | ||
| 43 | params[j] = part | ||
| 44 | parts[i] = expr | ||
| 45 | j++ | ||
| 46 | } | ||
| 47 | } | ||
| 48 | if j == 0 { | ||
| 49 | //now create the Route | ||
| 50 | t := reflect.Indirect(reflect.ValueOf(c)).Type() | ||
| 51 | route := &controllerInfo{} | ||
| 52 | route.pattern = pattern | ||
| 53 | route.controllerType = t | ||
| 54 | |||
| 55 | p.fixrouters = append(p.fixrouters, route) | ||
| 56 | } else { // add regexp routers | ||
| 57 | //recreate the url pattern, with parameters replaced | ||
| 58 | //by regular expressions. then compile the regex | ||
| 59 | pattern = strings.Join(parts, "/") | ||
| 60 | regex, regexErr := regexp.Compile(pattern) | ||
| 61 | if regexErr != nil { | ||
| 62 | //TODO add error handling here to avoid panic | ||
| 63 | panic(regexErr) | ||
| 64 | return | ||
| 65 | } | ||
| 66 | |||
| 67 | //now create the Route | ||
| 68 | t := reflect.Indirect(reflect.ValueOf(c)).Type() | ||
| 69 | route := &controllerInfo{} | ||
| 70 | route.regex = regex | ||
| 71 | route.params = params | ||
| 72 | route.controllerType = t | ||
| 73 | |||
| 74 | p.routers = append(p.routers, route) | ||
| 75 | } | ||
| 76 | |||
| 77 | } | ||
| 78 | |||
| 79 | // Filter adds the middleware filter. | ||
| 80 | func (p *ControllerRegistor) Filter(filter http.HandlerFunc) { | ||
| 81 | p.filters = append(p.filters, filter) | ||
| 82 | } | ||
| 83 | |||
| 84 | // FilterParam adds the middleware filter if the REST URL parameter exists. | ||
| 85 | func (p *ControllerRegistor) FilterParam(param string, filter http.HandlerFunc) { | ||
| 86 | if !strings.HasPrefix(param, ":") { | ||
| 87 | param = ":" + param | ||
| 88 | } | ||
| 89 | |||
| 90 | p.Filter(func(w http.ResponseWriter, r *http.Request) { | ||
| 91 | p := r.URL.Query().Get(param) | ||
| 92 | if len(p) > 0 { | ||
| 93 | filter(w, r) | ||
| 94 | } | ||
| 95 | }) | ||
| 96 | } | ||
| 97 | |||
| 98 | // FilterPrefixPath adds the middleware filter if the prefix path exists. | ||
| 99 | func (p *ControllerRegistor) FilterPrefixPath(path string, filter http.HandlerFunc) { | ||
| 100 | p.Filter(func(w http.ResponseWriter, r *http.Request) { | ||
| 101 | if strings.HasPrefix(r.URL.Path, path) { | ||
| 102 | filter(w, r) | ||
| 103 | } | ||
| 104 | }) | ||
| 105 | } | ||
| 106 | |||
| 107 | // AutoRoute | ||
| 108 | func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { | ||
| 109 | defer func() { | ||
| 110 | if err := recover(); err != nil { | ||
| 111 | if !RecoverPanic { | ||
| 112 | // go back to panic | ||
| 113 | panic(err) | ||
| 114 | } else { | ||
| 115 | Critical("Handler crashed with error", err) | ||
| 116 | for i := 1; ; i += 1 { | ||
| 117 | _, file, line, ok := runtime.Caller(i) | ||
| 118 | if !ok { | ||
| 119 | break | ||
| 120 | } | ||
| 121 | Critical(file, line) | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | }() | ||
| 126 | w := &responseWriter{writer: rw} | ||
| 127 | |||
| 128 | var runrouter *controllerInfo | ||
| 129 | var findrouter bool | ||
| 130 | |||
| 131 | params := make(map[string]string) | ||
| 132 | |||
| 133 | //static file server | ||
| 134 | for prefix, staticDir := range StaticDir { | ||
| 135 | if strings.HasPrefix(r.URL.Path, prefix) { | ||
| 136 | file := staticDir + r.URL.Path[len(prefix):] | ||
| 137 | http.ServeFile(w, r, file) | ||
| 138 | w.started = true | ||
| 139 | return | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | requestPath := r.URL.Path | ||
| 144 | |||
| 145 | //first find path from the fixrouters to Improve Performance | ||
| 146 | for _, route := range p.fixrouters { | ||
| 147 | n := len(requestPath) | ||
| 148 | if (requestPath[n-1] != '/' && route.pattern == requestPath) || | ||
| 149 | (len(route.pattern) >= n && requestPath[0:n] == route.pattern) { | ||
| 150 | runrouter = route | ||
| 151 | findrouter = true | ||
| 152 | break | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | if !findrouter { | ||
| 157 | //find a matching Route | ||
| 158 | for _, route := range p.routers { | ||
| 159 | |||
| 160 | //check if Route pattern matches url | ||
| 161 | if !route.regex.MatchString(requestPath) { | ||
| 162 | continue | ||
| 163 | } | ||
| 164 | |||
| 165 | //get submatches (params) | ||
| 166 | matches := route.regex.FindStringSubmatch(requestPath) | ||
| 167 | |||
| 168 | //double check that the Route matches the URL pattern. | ||
| 169 | if len(matches[0]) != len(requestPath) { | ||
| 170 | continue | ||
| 171 | } | ||
| 172 | |||
| 173 | if len(route.params) > 0 { | ||
| 174 | //add url parameters to the query param map | ||
| 175 | values := r.URL.Query() | ||
| 176 | for i, match := range matches[1:] { | ||
| 177 | values.Add(route.params[i], match) | ||
| 178 | params[route.params[i]] = match | ||
| 179 | } | ||
| 180 | //reassemble query params and add to RawQuery | ||
| 181 | r.URL.RawQuery = url.Values(values).Encode() + "&" + r.URL.RawQuery | ||
| 182 | //r.URL.RawQuery = url.Values(values).Encode() | ||
| 183 | } | ||
| 184 | runrouter = route | ||
| 185 | findrouter = true | ||
| 186 | break | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | if runrouter != nil { | ||
| 191 | //execute middleware filters | ||
| 192 | for _, filter := range p.filters { | ||
| 193 | filter(w, r) | ||
| 194 | if w.started { | ||
| 195 | return | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | //Invoke the request handler | ||
| 200 | vc := reflect.New(runrouter.controllerType) | ||
| 201 | |||
| 202 | //call the controller init function | ||
| 203 | init := vc.MethodByName("Init") | ||
| 204 | in := make([]reflect.Value, 2) | ||
| 205 | ct := &Context{ResponseWriter: w, Request: r, Params: params} | ||
| 206 | in[0] = reflect.ValueOf(ct) | ||
| 207 | in[1] = reflect.ValueOf(runrouter.controllerType.Name()) | ||
| 208 | init.Call(in) | ||
| 209 | //call prepare function | ||
| 210 | in = make([]reflect.Value, 0) | ||
| 211 | method := vc.MethodByName("Prepare") | ||
| 212 | method.Call(in) | ||
| 213 | |||
| 214 | //if response has written,yes don't run next | ||
| 215 | if !w.started { | ||
| 216 | if r.Method == "GET" { | ||
| 217 | method = vc.MethodByName("Get") | ||
| 218 | method.Call(in) | ||
| 219 | } else if r.Method == "POST" { | ||
| 220 | method = vc.MethodByName("Post") | ||
| 221 | method.Call(in) | ||
| 222 | } else if r.Method == "HEAD" { | ||
| 223 | method = vc.MethodByName("Head") | ||
| 224 | method.Call(in) | ||
| 225 | } else if r.Method == "DELETE" { | ||
| 226 | method = vc.MethodByName("Delete") | ||
| 227 | method.Call(in) | ||
| 228 | } else if r.Method == "PUT" { | ||
| 229 | method = vc.MethodByName("Put") | ||
| 230 | method.Call(in) | ||
| 231 | } else if r.Method == "PATCH" { | ||
| 232 | method = vc.MethodByName("Patch") | ||
| 233 | method.Call(in) | ||
| 234 | } else if r.Method == "OPTIONS" { | ||
| 235 | method = vc.MethodByName("Options") | ||
| 236 | method.Call(in) | ||
| 237 | } | ||
| 238 | if !w.started { | ||
| 239 | if AutoRender { | ||
| 240 | method = vc.MethodByName("Render") | ||
| 241 | method.Call(in) | ||
| 242 | } | ||
| 243 | if !w.started { | ||
| 244 | method = vc.MethodByName("Finish") | ||
| 245 | method.Call(in) | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | //if no matches to url, throw a not found exception | ||
| 252 | if w.started == false { | ||
| 253 | http.NotFound(w, r) | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 257 | //responseWriter is a wrapper for the http.ResponseWriter | ||
| 258 | //started set to true if response was written to then don't execute other handler | ||
| 259 | type responseWriter struct { | ||
| 260 | writer http.ResponseWriter | ||
| 261 | started bool | ||
| 262 | status int | ||
| 263 | } | ||
| 264 | |||
| 265 | // Header returns the header map that will be sent by WriteHeader. | ||
| 266 | func (w *responseWriter) Header() http.Header { | ||
| 267 | return w.writer.Header() | ||
| 268 | } | ||
| 269 | |||
| 270 | // Write writes the data to the connection as part of an HTTP reply, | ||
| 271 | // and sets `started` to true | ||
| 272 | func (w *responseWriter) Write(p []byte) (int, error) { | ||
| 273 | w.started = true | ||
| 274 | return w.writer.Write(p) | ||
| 275 | } | ||
| 276 | |||
| 277 | // WriteHeader sends an HTTP response header with status code, | ||
| 278 | // and sets `started` to true | ||
| 279 | func (w *responseWriter) WriteHeader(code int) { | ||
| 280 | w.status = code | ||
| 281 | w.started = true | ||
| 282 | w.writer.WriteHeader(code) | ||
| 283 | } |
template.go
0 → 100644
| 1 | package beego | ||
| 2 | |||
| 3 | //@todo add template funcs | ||
| 4 | |||
| 5 | import ( | ||
| 6 | //"fmt" | ||
| 7 | "errors" | ||
| 8 | "fmt" | ||
| 9 | "github.com/russross/blackfriday" | ||
| 10 | "html/template" | ||
| 11 | "strings" | ||
| 12 | "time" | ||
| 13 | ) | ||
| 14 | |||
| 15 | var beegoTplFuncMap template.FuncMap | ||
| 16 | |||
| 17 | func init() { | ||
| 18 | beegoTplFuncMap = make(template.FuncMap) | ||
| 19 | beegoTplFuncMap["markdown"] = MarkDown | ||
| 20 | beegoTplFuncMap["dateformat"] = DateFormat | ||
| 21 | beegoTplFuncMap["compare"] = Compare | ||
| 22 | } | ||
| 23 | |||
| 24 | // MarkDown parses a string in MarkDown format and returns HTML. Used by the template parser as "markdown" | ||
| 25 | func MarkDown(raw string) (output template.HTML) { | ||
| 26 | input := []byte(raw) | ||
| 27 | bOutput := blackfriday.MarkdownBasic(input) | ||
| 28 | output = template.HTML(string(bOutput)) | ||
| 29 | return | ||
| 30 | } | ||
| 31 | |||
| 32 | // DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat" | ||
| 33 | func DateFormat(t time.Time, layout string) (datestring string) { | ||
| 34 | datestring = t.Format(layout) | ||
| 35 | return | ||
| 36 | } | ||
| 37 | |||
| 38 | // Compare is a quick and dirty comparison function. It will convert whatever you give it to strings and see if the two values are equal. | ||
| 39 | // Whitespace is trimmed. Used by the template parser as "eq" | ||
| 40 | func Compare(a, b interface{}) (equal bool) { | ||
| 41 | equal = false | ||
| 42 | if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) { | ||
| 43 | equal = true | ||
| 44 | } | ||
| 45 | return | ||
| 46 | } | ||
| 47 | |||
| 48 | // AddFuncMap let user to register a func in the template | ||
| 49 | func AddFuncMap(key string, funname interface{}) error { | ||
| 50 | if _, ok := beegoTplFuncMap["key"]; ok { | ||
| 51 | beegoTplFuncMap[key] = funname | ||
| 52 | return nil | ||
| 53 | } | ||
| 54 | return errors.New("funcmap already has the key") | ||
| 55 | } |
-
Please register or sign in to post a comment