mirror of
https://github.com/astaxie/beego.git
synced 2024-12-22 16:30:48 +00:00
init framwork
This commit is contained in:
parent
afebcd891e
commit
f593c1f3fd
112
beego.go
Normal file
112
beego.go
Normal file
@ -0,0 +1,112 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var (
|
||||
BeeApp *App
|
||||
AppName string
|
||||
AppPath string
|
||||
StaticDir map[string]string
|
||||
HttpAddr string
|
||||
HttpPort int
|
||||
RecoverPanic bool
|
||||
AutoRender bool
|
||||
ViewsPath string
|
||||
RunMode string //"dev" or "prod"
|
||||
AppConfig *Config
|
||||
)
|
||||
|
||||
func init() {
|
||||
BeeApp = NewApp()
|
||||
AppPath, _ = os.Getwd()
|
||||
StaticDir = make(map[string]string)
|
||||
var err error
|
||||
AppConfig, err = LoadConfig(path.Join(AppPath, "conf", "app.conf"))
|
||||
if err != nil {
|
||||
Trace("open Config err:", err)
|
||||
HttpAddr = ""
|
||||
HttpPort = 8080
|
||||
AppName = "beego"
|
||||
RunMode = "prod"
|
||||
AutoRender = true
|
||||
RecoverPanic = true
|
||||
ViewsPath = "views"
|
||||
} else {
|
||||
HttpAddr = AppConfig.String("httpaddr")
|
||||
if v, err := AppConfig.Int("httpport"); err != nil {
|
||||
HttpPort = 8080
|
||||
} else {
|
||||
HttpPort = v
|
||||
}
|
||||
AppName = AppConfig.String("appname")
|
||||
if runmode := AppConfig.String("runmode"); runmode != "" {
|
||||
RunMode = runmode
|
||||
} else {
|
||||
RunMode = "prod"
|
||||
}
|
||||
if ar, err := AppConfig.Bool("autorender"); err != nil {
|
||||
AutoRender = true
|
||||
} else {
|
||||
AutoRender = ar
|
||||
}
|
||||
if ar, err := AppConfig.Bool("autorecover"); err != nil {
|
||||
RecoverPanic = true
|
||||
} else {
|
||||
RecoverPanic = ar
|
||||
}
|
||||
if views := AppConfig.String("viewspath"); views == "" {
|
||||
ViewsPath = "views"
|
||||
} else {
|
||||
ViewsPath = views
|
||||
}
|
||||
}
|
||||
StaticDir["/static"] = "static"
|
||||
|
||||
}
|
||||
|
||||
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 := fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
|
||||
err := http.ListenAndServe(addr, app.Handlers)
|
||||
if err != nil {
|
||||
BeeLogger.Fatal("ListenAndServe: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) RegisterController(path string, c ControllerInterface) *App {
|
||||
app.Handlers.Add(path, c)
|
||||
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) 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)
|
||||
}
|
121
config.go
Normal file
121
config.go
Normal file
@ -0,0 +1,121 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
var (
|
||||
bComment = []byte{'#'}
|
||||
bEmpty = []byte{}
|
||||
bEqual = []byte{'='}
|
||||
bDQuote = []byte{'"'}
|
||||
)
|
||||
|
||||
// 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(val[1], bDQuote) {
|
||||
val[1] = bytes.Trim(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])
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
72
context.go
Normal file
72
context.go
Normal file
@ -0,0 +1,72 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
ResponseWriter http.ResponseWriter
|
||||
Request *http.Request
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
func (ctx *Context) WriteString(content string) {
|
||||
ctx.ResponseWriter.Write([]byte(content))
|
||||
}
|
||||
|
||||
func (ctx *Context) Abort(status int, body string) {
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
ctx.ResponseWriter.Write([]byte(body))
|
||||
}
|
||||
|
||||
func (ctx *Context) Redirect(status int, url_ string) {
|
||||
ctx.ResponseWriter.Header().Set("Location", url_)
|
||||
ctx.ResponseWriter.WriteHeader(status)
|
||||
ctx.ResponseWriter.Write([]byte("Redirecting to: " + url_))
|
||||
}
|
||||
|
||||
func (ctx *Context) NotModified() {
|
||||
ctx.ResponseWriter.WriteHeader(304)
|
||||
}
|
||||
|
||||
func (ctx *Context) NotFound(message string) {
|
||||
ctx.ResponseWriter.WriteHeader(404)
|
||||
ctx.ResponseWriter.Write([]byte(message))
|
||||
}
|
||||
|
||||
//Sets the content type by extension, as defined in the mime package.
|
||||
//For example, ctx.ContentType("json") sets the content-type to "application/json"
|
||||
func (ctx *Context) ContentType(ext string) {
|
||||
if !strings.HasPrefix(ext, ".") {
|
||||
ext = "." + ext
|
||||
}
|
||||
ctype := mime.TypeByExtension(ext)
|
||||
if ctype != "" {
|
||||
ctx.ResponseWriter.Header().Set("Content-Type", ctype)
|
||||
}
|
||||
}
|
||||
|
||||
func (ctx *Context) SetHeader(hdr string, val string, unique bool) {
|
||||
if unique {
|
||||
ctx.ResponseWriter.Header().Set(hdr, val)
|
||||
} else {
|
||||
ctx.ResponseWriter.Header().Add(hdr, val)
|
||||
}
|
||||
}
|
||||
|
||||
//Sets a cookie -- duration is the amount of time in seconds. 0 = forever
|
||||
func (ctx *Context) SetCookie(name string, value string, age int64) {
|
||||
var utctime time.Time
|
||||
if age == 0 {
|
||||
// 2^31 - 1 seconds (roughly 2038)
|
||||
utctime = time.Unix(2147483647, 0)
|
||||
} else {
|
||||
utctime = time.Unix(time.Now().Unix()+age, 0)
|
||||
}
|
||||
cookie := fmt.Sprintf("%s=%s; expires=%s", name, value, webTime(utctime))
|
||||
ctx.SetHeader("Set-Cookie", cookie, false)
|
||||
}
|
147
controller.go
Normal file
147
controller.go
Normal file
@ -0,0 +1,147 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
Ct *Context
|
||||
Tpl *template.Template
|
||||
Data map[interface{}]interface{}
|
||||
ChildName string
|
||||
TplNames string
|
||||
Layout string
|
||||
TplExt string
|
||||
}
|
||||
|
||||
type ControllerInterface interface {
|
||||
Init(ct *Context, cn string)
|
||||
Prepare()
|
||||
Get()
|
||||
Post()
|
||||
Delete()
|
||||
Put()
|
||||
Head()
|
||||
Patch()
|
||||
Options()
|
||||
Finish()
|
||||
Render() error
|
||||
}
|
||||
|
||||
func (c *Controller) Init(ct *Context, cn string) {
|
||||
c.Data = make(map[interface{}]interface{})
|
||||
c.Tpl = template.New(cn + ct.Request.Method)
|
||||
c.Tpl = c.Tpl.Funcs(beegoTplFuncMap)
|
||||
c.Layout = ""
|
||||
c.TplNames = ""
|
||||
c.ChildName = cn
|
||||
c.Ct = ct
|
||||
c.TplExt = "tpl"
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) Prepare() {
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) Finish() {
|
||||
|
||||
}
|
||||
|
||||
func (c *Controller) Get() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Post() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Delete() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Put() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Head() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Patch() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Options() {
|
||||
http.Error(c.Ct.ResponseWriter, "Method Not Allowed", 405)
|
||||
}
|
||||
|
||||
func (c *Controller) Render() error {
|
||||
//if the controller has set layout, then first get the tplname's content set the content to the layout
|
||||
if c.Layout != "" {
|
||||
if c.TplNames == "" {
|
||||
c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt
|
||||
}
|
||||
t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames), path.Join(ViewsPath, c.Layout))
|
||||
if err != nil {
|
||||
Trace("template ParseFiles err:", err)
|
||||
}
|
||||
_, file := path.Split(c.TplNames)
|
||||
newbytes := bytes.NewBufferString("")
|
||||
t.ExecuteTemplate(newbytes, file, c.Data)
|
||||
tplcontent, _ := ioutil.ReadAll(newbytes)
|
||||
c.Data["LayoutContent"] = template.HTML(string(tplcontent))
|
||||
_, file = path.Split(c.Layout)
|
||||
err = t.ExecuteTemplate(c.Ct.ResponseWriter, file, c.Data)
|
||||
if err != nil {
|
||||
Trace("template Execute err:", err)
|
||||
}
|
||||
} else {
|
||||
if c.TplNames == "" {
|
||||
c.TplNames = c.ChildName + "/" + c.Ct.Request.Method + "." + c.TplExt
|
||||
}
|
||||
t, err := c.Tpl.ParseFiles(path.Join(ViewsPath, c.TplNames))
|
||||
if err != nil {
|
||||
Trace("template ParseFiles err:", err)
|
||||
}
|
||||
_, file := path.Split(c.TplNames)
|
||||
err = t.ExecuteTemplate(c.Ct.ResponseWriter, file, c.Data)
|
||||
if err != nil {
|
||||
Trace("template Execute err:", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) Redirect(url string, code int) {
|
||||
c.Ct.Redirect(code, url)
|
||||
}
|
||||
|
||||
func (c *Controller) ServeJson() {
|
||||
content, err := json.MarshalIndent(c.Data, "", " ")
|
||||
if err != nil {
|
||||
http.Error(c.Ct.ResponseWriter, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.Ct.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
|
||||
c.Ct.ContentType("json")
|
||||
c.Ct.ResponseWriter.Write(content)
|
||||
}
|
||||
|
||||
func (c *Controller) ServeXml() {
|
||||
content, err := xml.Marshal(c.Data)
|
||||
if err != nil {
|
||||
http.Error(c.Ct.ResponseWriter, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.Ct.SetHeader("Content-Length", strconv.Itoa(len(content)), true)
|
||||
c.Ct.ContentType("xml")
|
||||
c.Ct.ResponseWriter.Write(content)
|
||||
}
|
85
log.go
Normal file
85
log.go
Normal file
@ -0,0 +1,85 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
//--------------------
|
||||
// LOG LEVEL
|
||||
//--------------------
|
||||
|
||||
// Log levels to control the logging output.
|
||||
const (
|
||||
LevelTrace = iota
|
||||
LevelDebug
|
||||
LevelInfo
|
||||
LevelWarning
|
||||
LevelError
|
||||
LevelCritical
|
||||
)
|
||||
|
||||
// logLevel controls the global log level used by the logger.
|
||||
var level = LevelTrace
|
||||
|
||||
// LogLevel returns the global log level and can be used in
|
||||
// own implementations of the logger interface.
|
||||
func Level() int {
|
||||
return level
|
||||
}
|
||||
|
||||
// SetLogLevel sets the global log level used by the simple
|
||||
// logger.
|
||||
func SetLevel(l int) {
|
||||
level = l
|
||||
}
|
||||
|
||||
// logger references the used application logger.
|
||||
var BeeLogger = log.New(os.Stdout, "", log.Ldate|log.Ltime)
|
||||
|
||||
// SetLogger sets a new logger.
|
||||
func SetLogger(l *log.Logger) {
|
||||
BeeLogger = l
|
||||
}
|
||||
|
||||
// Trace logs a message at trace level.
|
||||
func Trace(v ...interface{}) {
|
||||
if level <= LevelTrace {
|
||||
BeeLogger.Printf("[T] %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logs a message at debug level.
|
||||
func Debug(v ...interface{}) {
|
||||
if level <= LevelDebug {
|
||||
BeeLogger.Printf("[D] %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs a message at info level.
|
||||
func Info(v ...interface{}) {
|
||||
if level <= LevelInfo {
|
||||
BeeLogger.Printf("[I] %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Warning logs a message at warning level.
|
||||
func Warn(v ...interface{}) {
|
||||
if level <= LevelWarning {
|
||||
BeeLogger.Printf("[W] %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs a message at error level.
|
||||
func Error(v ...interface{}) {
|
||||
if level <= LevelError {
|
||||
BeeLogger.Printf("[E] %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
// Critical logs a message at critical level.
|
||||
func Critical(v ...interface{}) {
|
||||
if level <= LevelCritical {
|
||||
BeeLogger.Printf("[C] %v\n", v)
|
||||
}
|
||||
}
|
283
router.go
Normal file
283
router.go
Normal file
@ -0,0 +1,283 @@
|
||||
package beego
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type controllerInfo struct {
|
||||
pattern string
|
||||
regex *regexp.Regexp
|
||||
params map[int]string
|
||||
controllerType reflect.Type
|
||||
}
|
||||
|
||||
type ControllerRegistor struct {
|
||||
routers []*controllerInfo
|
||||
fixrouters []*controllerInfo
|
||||
filters []http.HandlerFunc
|
||||
}
|
||||
|
||||
func NewControllerRegistor() *ControllerRegistor {
|
||||
return &ControllerRegistor{routers: make([]*controllerInfo, 0)}
|
||||
}
|
||||
|
||||
func (p *ControllerRegistor) Add(pattern string, c ControllerInterface) {
|
||||
parts := strings.Split(pattern, "/")
|
||||
|
||||
j := 0
|
||||
params := make(map[int]string)
|
||||
for i, part := range parts {
|
||||
if strings.HasPrefix(part, ":") {
|
||||
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]
|
||||
}
|
||||
params[j] = part
|
||||
parts[i] = expr
|
||||
j++
|
||||
}
|
||||
}
|
||||
if j == 0 {
|
||||
//now create the Route
|
||||
t := reflect.Indirect(reflect.ValueOf(c)).Type()
|
||||
route := &controllerInfo{}
|
||||
route.pattern = pattern
|
||||
route.controllerType = t
|
||||
|
||||
p.fixrouters = append(p.fixrouters, route)
|
||||
} else { // add regexp routers
|
||||
//recreate the url pattern, with parameters replaced
|
||||
//by regular expressions. then compile the regex
|
||||
pattern = strings.Join(parts, "/")
|
||||
regex, regexErr := regexp.Compile(pattern)
|
||||
if regexErr != nil {
|
||||
//TODO add error handling here to avoid panic
|
||||
panic(regexErr)
|
||||
return
|
||||
}
|
||||
|
||||
//now create the Route
|
||||
t := reflect.Indirect(reflect.ValueOf(c)).Type()
|
||||
route := &controllerInfo{}
|
||||
route.regex = regex
|
||||
route.params = params
|
||||
route.controllerType = t
|
||||
|
||||
p.routers = append(p.routers, route)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Filter adds the middleware filter.
|
||||
func (p *ControllerRegistor) Filter(filter http.HandlerFunc) {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AutoRoute
|
||||
func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
if !RecoverPanic {
|
||||
// go back to panic
|
||||
panic(err)
|
||||
} else {
|
||||
Critical("Handler crashed with error", err)
|
||||
for i := 1; ; i += 1 {
|
||||
_, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
Critical(file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
w := &responseWriter{writer: rw}
|
||||
|
||||
var runrouter *controllerInfo
|
||||
var findrouter bool
|
||||
|
||||
params := make(map[string]string)
|
||||
|
||||
//static file server
|
||||
for prefix, staticDir := range StaticDir {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
file := staticDir + r.URL.Path[len(prefix):]
|
||||
http.ServeFile(w, r, file)
|
||||
w.started = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
requestPath := r.URL.Path
|
||||
|
||||
//first find path from the fixrouters to Improve Performance
|
||||
for _, route := range p.fixrouters {
|
||||
n := len(requestPath)
|
||||
if (requestPath[n-1] != '/' && route.pattern == requestPath) ||
|
||||
(len(route.pattern) >= n && requestPath[0:n] == route.pattern) {
|
||||
runrouter = route
|
||||
findrouter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !findrouter {
|
||||
//find a matching Route
|
||||
for _, route := range p.routers {
|
||||
|
||||
//check if Route pattern matches url
|
||||
if !route.regex.MatchString(requestPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
//get submatches (params)
|
||||
matches := route.regex.FindStringSubmatch(requestPath)
|
||||
|
||||
//double check that the Route matches the URL pattern.
|
||||
if len(matches[0]) != len(requestPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(route.params) > 0 {
|
||||
//add url parameters to the query param map
|
||||
values := r.URL.Query()
|
||||
for i, match := range matches[1:] {
|
||||
values.Add(route.params[i], match)
|
||||
params[route.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()
|
||||
}
|
||||
runrouter = route
|
||||
findrouter = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if runrouter != nil {
|
||||
//execute middleware filters
|
||||
for _, filter := range p.filters {
|
||||
filter(w, r)
|
||||
if w.started {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//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}
|
||||
in[0] = reflect.ValueOf(ct)
|
||||
in[1] = reflect.ValueOf(runrouter.controllerType.Name())
|
||||
init.Call(in)
|
||||
//call prepare function
|
||||
in = make([]reflect.Value, 0)
|
||||
method := vc.MethodByName("Prepare")
|
||||
method.Call(in)
|
||||
|
||||
//if response has written,yes don't run next
|
||||
if !w.started {
|
||||
if r.Method == "GET" {
|
||||
method = vc.MethodByName("Get")
|
||||
method.Call(in)
|
||||
} else if r.Method == "POST" {
|
||||
method = vc.MethodByName("Post")
|
||||
method.Call(in)
|
||||
} else if r.Method == "HEAD" {
|
||||
method = vc.MethodByName("Head")
|
||||
method.Call(in)
|
||||
} else if r.Method == "DELETE" {
|
||||
method = vc.MethodByName("Delete")
|
||||
method.Call(in)
|
||||
} else if r.Method == "PUT" {
|
||||
method = vc.MethodByName("Put")
|
||||
method.Call(in)
|
||||
} else if r.Method == "PATCH" {
|
||||
method = vc.MethodByName("Patch")
|
||||
method.Call(in)
|
||||
} else if r.Method == "OPTIONS" {
|
||||
method = vc.MethodByName("Options")
|
||||
method.Call(in)
|
||||
}
|
||||
if !w.started {
|
||||
if AutoRender {
|
||||
method = vc.MethodByName("Render")
|
||||
method.Call(in)
|
||||
}
|
||||
if !w.started {
|
||||
method = vc.MethodByName("Finish")
|
||||
method.Call(in)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if no matches to url, throw a not found exception
|
||||
if w.started == false {
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
//responseWriter is a wrapper for the http.ResponseWriter
|
||||
//started set to true if response was written to then don't execute other handler
|
||||
type responseWriter struct {
|
||||
writer http.ResponseWriter
|
||||
started bool
|
||||
status int
|
||||
}
|
||||
|
||||
// Header returns the header map that will be sent by WriteHeader.
|
||||
func (w *responseWriter) Header() http.Header {
|
||||
return w.writer.Header()
|
||||
}
|
||||
|
||||
// Write writes the data to the connection as part of an HTTP reply,
|
||||
// and sets `started` to true
|
||||
func (w *responseWriter) Write(p []byte) (int, error) {
|
||||
w.started = true
|
||||
return w.writer.Write(p)
|
||||
}
|
||||
|
||||
// WriteHeader sends an HTTP response header with status code,
|
||||
// and sets `started` to true
|
||||
func (w *responseWriter) WriteHeader(code int) {
|
||||
w.status = code
|
||||
w.started = true
|
||||
w.writer.WriteHeader(code)
|
||||
}
|
55
template.go
Normal file
55
template.go
Normal file
@ -0,0 +1,55 @@
|
||||
package beego
|
||||
|
||||
//@todo add template funcs
|
||||
|
||||
import (
|
||||
//"fmt"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/russross/blackfriday"
|
||||
"html/template"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var beegoTplFuncMap template.FuncMap
|
||||
|
||||
func init() {
|
||||
beegoTplFuncMap = make(template.FuncMap)
|
||||
beegoTplFuncMap["markdown"] = MarkDown
|
||||
beegoTplFuncMap["dateformat"] = DateFormat
|
||||
beegoTplFuncMap["compare"] = Compare
|
||||
}
|
||||
|
||||
// MarkDown parses a string in MarkDown format and returns HTML. Used by the template parser as "markdown"
|
||||
func MarkDown(raw string) (output template.HTML) {
|
||||
input := []byte(raw)
|
||||
bOutput := blackfriday.MarkdownBasic(input)
|
||||
output = template.HTML(string(bOutput))
|
||||
return
|
||||
}
|
||||
|
||||
// DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat"
|
||||
func DateFormat(t time.Time, layout string) (datestring string) {
|
||||
datestring = t.Format(layout)
|
||||
return
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Whitespace is trimmed. Used by the template parser as "eq"
|
||||
func Compare(a, b interface{}) (equal bool) {
|
||||
equal = false
|
||||
if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) {
|
||||
equal = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AddFuncMap let user to register a func in the template
|
||||
func AddFuncMap(key string, funname interface{}) error {
|
||||
if _, ok := beegoTplFuncMap["key"]; ok {
|
||||
beegoTplFuncMap[key] = funname
|
||||
return nil
|
||||
}
|
||||
return errors.New("funcmap already has the key")
|
||||
}
|
Loading…
Reference in New Issue
Block a user