├── VERSION ├── zh-CN ├── database.md ├── middleware │ ├── recovery.md │ ├── accesslog.md │ ├── nocache.md │ ├── gzip.md │ ├── static.md │ ├── session.md │ └── requestcache.md ├── component.md ├── component │ ├── bat.md │ ├── router.md │ ├── log.md │ ├── setting.md │ ├── render.md │ ├── pongo2.md │ ├── pool.md │ └── cache.md ├── log.md ├── middleware.md ├── di.md ├── baa.md ├── README.md ├── project.md ├── context.md └── router.md ├── code ├── README.md ├── baa.go ├── middleware │ ├── recovery.go │ ├── accesslog.go │ ├── nocache.go │ ├── static.go │ ├── middleware.go │ ├── gzip.go │ ├── requestcache.go │ └── session.go ├── context │ ├── main.go │ └── template │ │ └── index.html ├── di │ └── di.go ├── component │ └── cache.go └── router │ └── router.go ├── README.md └── en-US └── README.md /VERSION: -------------------------------------------------------------------------------- 1 | 1.2.24 -------------------------------------------------------------------------------- /zh-CN/database.md: -------------------------------------------------------------------------------- 1 | # 数据库 2 | 3 | baa 本身并不提供任何数据库的内容,仅列出一些常见的数据库ORM库供参考: 4 | 5 | * [gorm](http://jinzhu.me/gorm/) 6 | * [xorm](http://xorm.io/) 7 | * [mgo](https://labix.org/mgo) 8 | -------------------------------------------------------------------------------- /code/README.md: -------------------------------------------------------------------------------- 1 | # Baa 2 | 3 | an express Go web framework with routing, middleware, dependency injection, http context. 4 | 5 | Baa is ``no reflect``, ``no regexp``. 6 | 7 | ## Code 8 | 9 | example code for baa document 10 | -------------------------------------------------------------------------------- /code/baa.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-baa/baa" 5 | ) 6 | 7 | func main() { 8 | app := baa.Default() 9 | app.Get("/", func(c *baa.Context) { 10 | c.String(200, "Hello, 世界") 11 | }) 12 | app.Run(":1323") 13 | } 14 | -------------------------------------------------------------------------------- /code/middleware/recovery.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/recovery" 5 | "github.com/go-baa/baa" 6 | ) 7 | 8 | func mainRecovery() { 9 | app := baa.Default() 10 | app.Use(recovery.Recovery()) 11 | 12 | app.Get("/", func(c *baa.Context) { 13 | c.String(200, "Hello, 世界") 14 | }) 15 | 16 | app.Run(":1323") 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Baa 2 | 3 | an express Go web framework with routing, middleware, dependency injection, http context. 4 | 5 | Baa is ``no reflect``, ``no regexp``. 6 | 7 | ## Document 8 | 9 | * [简体中文](https://github.com/go-baa/doc/tree/master/zh-CN) 10 | * [English](https://github.com/go-baa/doc/tree/master/en-US) 11 | * [Doc Code](https://github.com/go-baa/doc/tree/master/code) -------------------------------------------------------------------------------- /code/context/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import baa "github.com/go-baa/baa" 4 | 5 | func main() { 6 | app := baa.New() 7 | app.Get("/", func(c *baa.Context) { 8 | c.Set("title", "this is title") 9 | c.Set("content", "this is content") 10 | c.Set("show", true) 11 | c.Set("list", []string{"111", "222", "333"}) 12 | c.HTML(200, "template/index.html") 13 | }) 14 | app.Run(":1323") 15 | } 16 | -------------------------------------------------------------------------------- /code/middleware/accesslog.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/accesslog" 5 | "github.com/baa-middleware/recovery" 6 | "github.com/go-baa/baa" 7 | ) 8 | 9 | func mainAccesslog() { 10 | app := baa.Default() 11 | app.Use(recovery.Recovery()) 12 | app.Use(accesslog.Logger()) 13 | 14 | app.Get("/", func(c *baa.Context) { 15 | c.String(200, "Hello, 世界") 16 | }) 17 | 18 | app.Run(":1323") 19 | } 20 | -------------------------------------------------------------------------------- /code/di/di.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "os" 6 | 7 | "github.com/go-baa/baa" 8 | ) 9 | 10 | func main() { 11 | app := baa.Default() 12 | app.SetDI("logger", log.New(os.Stderr, "[BaaDI] ", log.LstdFlags)) 13 | 14 | app.Get("/", func(c *baa.Context) { 15 | // use di 16 | logger := c.DI("logger").(*log.Logger) 17 | logger.Println("i am use logger di") 18 | 19 | c.String(200, "Hello, 世界") 20 | }) 21 | 22 | app.Run(":1323") 23 | } 24 | -------------------------------------------------------------------------------- /code/middleware/nocache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/accesslog" 5 | "github.com/baa-middleware/nocache" 6 | "github.com/baa-middleware/recovery" 7 | "github.com/go-baa/baa" 8 | ) 9 | 10 | func mainNocache() { 11 | app := baa.Default() 12 | app.Use(recovery.Recovery()) 13 | app.Use(accesslog.Logger()) 14 | app.Use(nocache.New()) 15 | 16 | app.Get("/", func(c *baa.Context) { 17 | c.String(200, "Hello, 世界") 18 | }, nocache.NewFunc()) 19 | 20 | app.Run(":1323") 21 | } 22 | -------------------------------------------------------------------------------- /code/middleware/static.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/accesslog" 5 | "github.com/baa-middleware/recovery" 6 | "github.com/baa-middleware/static" 7 | "github.com/go-baa/baa" 8 | ) 9 | 10 | func mainStatic() { 11 | app := baa.Default() 12 | app.Use(recovery.Recovery()) 13 | app.Use(accesslog.Logger()) 14 | 15 | // static 16 | app.Use(static.Static("/assets", "public/assets", false, nil)) 17 | 18 | app.Get("/", func(c *baa.Context) { 19 | c.String(200, "Hello, 世界") 20 | }) 21 | 22 | app.Run(":1323") 23 | } 24 | -------------------------------------------------------------------------------- /code/middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-baa/baa" 7 | ) 8 | 9 | func main() { 10 | app := baa.Default() 11 | app.Use(func(c *baa.Context) { 12 | // 进入,记录时间 13 | start := time.Now() 14 | 15 | // 接着执行其他中间件 16 | c.Next() 17 | 18 | // 执行完其他的,最后,输出请求日志 19 | c.Baa().Logger().Printf("%s %s %s %v %v", c.RemoteAddr(), c.Req.Method, c.URL(false), c.Resp.Status(), time.Since(start)) 20 | }) 21 | 22 | app.Get("/", func(c *baa.Context) { 23 | c.String(200, "Hello, 世界") 24 | }) 25 | 26 | app.Run(":1323") 27 | } 28 | -------------------------------------------------------------------------------- /code/middleware/gzip.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/accesslog" 5 | "github.com/baa-middleware/gzip" 6 | "github.com/baa-middleware/recovery" 7 | "github.com/go-baa/baa" 8 | ) 9 | 10 | func mainGzip() { 11 | app := baa.Default() 12 | app.Use(recovery.Recovery()) 13 | app.Use(accesslog.Logger()) 14 | 15 | if baa.Env == baa.PROD { 16 | app.Use(gzip.Gzip(gzip.Options{ 17 | CompressionLevel: 9, 18 | })) 19 | } 20 | 21 | app.Get("/", func(c *baa.Context) { 22 | c.String(200, "Hello, 世界") 23 | }) 24 | 25 | app.Run(":1323") 26 | } 27 | -------------------------------------------------------------------------------- /zh-CN/middleware/recovery.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 错误恢复 2 | 3 | `github.com/baa-middleware/recovery` 4 | 5 | 错误恢复中间件提供了当业务逻辑`Panic`时记录日志和返回500错误,并且`recovey`Go的程序,防止一个业务中的错误导致应用崩溃。 6 | 7 | ## 使用 8 | 9 | ``` 10 | package main 11 | 12 | import ( 13 | "github.com/baa-middleware/recovery" 14 | "github.com/go-baa/baa" 15 | ) 16 | 17 | func main() { 18 | app := baa.Default() 19 | app.Use(recovery.Recovery()) 20 | 21 | app.Get("/", func(c *baa.Context) { 22 | c.String(200, "Hello, 世界") 23 | }) 24 | 25 | app.Run(":1323") 26 | } 27 | ``` 28 | 29 | 建议该中间件的注册顺序为`第一`,防止其他中间件本身就有错误导致应用崩溃。 30 | -------------------------------------------------------------------------------- /zh-CN/component.md: -------------------------------------------------------------------------------- 1 | # Baa 组件 2 | 3 | 在日常工作,我们结合 baa 开发了一些常用组件。 4 | 5 | * baa开发工具 [bat](https://github.com/go-baa/doc/tree/master/zh-CN/component/bat.md) 6 | * 缓存库 [cache](https://github.com/go-baa/doc/tree/master/zh-CN/component/cache.md) 7 | * 连接池 [pool](https://github.com/go-baa/doc/tree/master/zh-CN/component/pool.md) 8 | * pongo2插件 for baa [pongo2](https://github.com/go-baa/doc/tree/master/zh-CN/component/pongo2.md) 9 | * 增强的模板渲染 for baa [render](https://github.com/go-baa/doc/tree/master/zh-CN/component/render.md) 10 | * 正则路由 for baa [router](https://github.com/go-baa/doc/tree/master/zh-CN/component/router.md) 11 | -------------------------------------------------------------------------------- /code/context/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ .title }} 6 | 7 | 8 | 9 |
10 | {{ .content}} 11 |
12 | 13 |
14 | 15 | {{ if .show}} 16 | i want show! 17 | {{ else }} 18 | i was hidden 19 | {{ end }} 20 | 21 |
22 | 23 |
24 | {{ range .list }} 25 | {{ . }} 26 | {{ end }} 27 |
28 | 29 |
30 | {{ . }} 31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /code/component/cache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/go-baa/baa" 5 | "github.com/go-baa/cache" 6 | ) 7 | 8 | func main() { 9 | // new app 10 | app := baa.New() 11 | 12 | // register cache 13 | app.SetDI("cache", cache.New(cache.Options{ 14 | Name: "cache", 15 | Prefix: "MyApp", 16 | Adapter: "memory", 17 | Config: map[string]interface{}{}, 18 | })) 19 | 20 | // router 21 | app.Get("/", func(c *baa.Context) { 22 | ca := c.DI("cache").(cache.Cacher) 23 | ca.Set("test", "baa", 10) 24 | var v string 25 | ca.Get("test", &v) 26 | c.String(200, v) 27 | }) 28 | 29 | // run app 30 | app.Run(":1323") 31 | } 32 | -------------------------------------------------------------------------------- /zh-CN/component/bat.md: -------------------------------------------------------------------------------- 1 | # Baa bat 2 | 3 | `https://github.com/go-baa/bat` 4 | 5 | bat 是一个帮助快速开发 baa 程序的小工具。 6 | 7 | 代码fork自 [beego](https://github.com/astaxie/beego/) 的 [bee](https://github.com/beego/bee)。 8 | 9 | ## 使用 10 | 11 | ### 安装 12 | 13 | ``` 14 | go get -u github.com/go-baa/bat 15 | ``` 16 | 17 | ## 运行baa程序 18 | 19 | ``` 20 | bat run [-x=.go -x=.ini] [-a=../model] [-e=Godeps -e=folderToExclude] [-tags=goBuildTags] 21 | ``` 22 | 23 | 默认情况下监控运行目录下的 `.go`文件,发生变化就会重新编译并运行。 24 | 25 | ### godeps 26 | 27 | bat 默认开启了 `godeps` 支持,如果项目下存在 `Godeps`目录,每次重新构建都会重建 `Godeps`。 28 | 29 | 可以通过以下参数关闭该特性: 30 | 31 | ``` 32 | bat run -godeps=false 33 | ``` 34 | -------------------------------------------------------------------------------- /zh-CN/middleware/accesslog.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 访问日志 2 | 3 | `github.com/baa-middleware/accesslog` 4 | 5 | 访问日志中间件提供了简单的HTTP访问日志,记录了请求方法,来源IP,URL地址,响应码,发送数据,以及访问时间。 6 | 7 | 可以应付简单的日志需求,而且这个中间件只有三行代码,你可以任意定制修改。 8 | 9 | 这个中间件是学习中间件姿势的`范例`。 10 | 11 | ## 使用 12 | 13 | ``` 14 | package main 15 | 16 | import ( 17 | "github.com/baa-middleware/accesslog" 18 | "github.com/baa-middleware/recovery" 19 | "github.com/go-baa/baa" 20 | ) 21 | 22 | func main() { 23 | app := baa.Default() 24 | app.Use(recovery.Recovery()) 25 | app.Use(accesslog.Logger()) 26 | 27 | app.Get("/", func(c *baa.Context) { 28 | c.String(200, "Hello, 世界") 29 | }) 30 | 31 | app.Run(":1323") 32 | } 33 | ``` 34 | 35 | 建议该中间件的注册顺序为`第二`,可以更加精准的获取业务的执行时间。 36 | -------------------------------------------------------------------------------- /zh-CN/component/router.md: -------------------------------------------------------------------------------- 1 | # Baa router 2 | 3 | `https://github.com/go-baa/router` 4 | 5 | 路由项目提供了额外的路由器实现。 6 | 7 | * regexp 完全使用正则表达式的路由器,在复杂路由多为正则表达式的情况下,效率稍好。 8 | * regtree 使用正则表达式和基数树组合优化的路由器,在多数路由为普通路由,少量正则路由条目的情况下,效率较好。 9 | 10 | 如果没有大量的正则表达式路由条目,建议使用 `regtree` 作为默认的 `正则路由`。 11 | 12 | ## 使用 13 | 14 | 通过 `DI` 替换掉全局的 `router` 即可。 15 | 16 | ``` 17 | package main 18 | 19 | import ( 20 | "github.com/go-baa/router/regtree" 21 | "github.com/go-baa/baa" 22 | ) 23 | 24 | func main() { 25 | app := baa.Default() 26 | app.SetDI("router", regtree.New(app)) 27 | 28 | app.Get("/view-:id(\\d+)", func(c *baa.Context) { 29 | c.String(200, c.Param("id")) 30 | }) 31 | app.Get("/view-:id(\\d+)/project", func(c *baa.Context) { 32 | c.String(200, c.Param("id")+"/project") 33 | }) 34 | app.Run(":1323") 35 | } 36 | ``` 37 | -------------------------------------------------------------------------------- /zh-CN/middleware/nocache.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 强制不缓存 2 | 3 | `github.com/baa-middleware/nocache` 4 | 5 | 强制不缓存中间件给所有请求增加了NO-CACHE Header头,防止浏览器或CDN缓存。 6 | 7 | ## 使用 8 | 9 | ``` 10 | package main 11 | 12 | import ( 13 | "github.com/baa-middleware/accesslog" 14 | "github.com/baa-middleware/nocache" 15 | "github.com/baa-middleware/recovery" 16 | "github.com/go-baa/baa" 17 | ) 18 | 19 | func main() { 20 | app := baa.Default() 21 | app.Use(recovery.Recovery()) 22 | app.Use(accesslog.Logger()) 23 | app.Use(nocache.New()) 24 | 25 | app.Get("/", func(c *baa.Context) { 26 | c.String(200, "Hello, 世界") 27 | }, nocache.NewFunc()) 28 | 29 | app.Run(":1323") 30 | } 31 | 32 | ``` 33 | 34 | 强制不缓存中间件提供了两个方法: 35 | 36 | ``` 37 | nocache.New() 38 | ``` 39 | 40 | 返回一个中间件,用于中间件注册。 41 | 42 | ``` 43 | nocache.NewFunc() 44 | ``` 45 | 46 | 返回一个路由处理函数,用于给指定的路由请求增加NO-CACHE设定。 47 | -------------------------------------------------------------------------------- /zh-CN/component/log.md: -------------------------------------------------------------------------------- 1 | # Baa log 2 | 3 | `https://github.com/go-baa/log` 4 | 5 | 一个增强的日志管理器实现。 6 | 7 | 相比标准库中的 `log` 增加了如下特性: 8 | 9 | * 可配置日志级别,Info/Warn/Error/Debug 10 | * 更多的日志方法 11 | * 优化的日志写入 12 | * 开箱即用的设置 13 | 14 | ## 安装 15 | 16 | * 依赖 [setting](github.com/go-baa/setting) 包 17 | * 可选配置文件:`conf/app.ini` 18 | 19 | ``` 20 | go get -u github.com/go-baa/setting 21 | go get -u github.com/go-baa/log 22 | ``` 23 | 24 | ## 使用 25 | 26 | ``` 27 | package main 28 | 29 | import ( 30 | "github.com/go-baa/log" 31 | ) 32 | 33 | func main() { 34 | log.Println("xx") 35 | log.Debugln("xx") 36 | } 37 | ``` 38 | 39 | ## 配置 40 | 41 | 配置,依赖 `setting` 配置文件,请在配置文件中加入以下配置: 42 | 43 | ``` 44 | // conf/app.ini 45 | [default] 46 | # output log to os.Stderr or filepath 47 | log.file = os.Stderr 48 | # 0 off, 1 fatal, 2 panic, 5 error, 6 warn, 10 info, 11 debug 49 | log.level = 11 50 | ``` 51 | 52 | `log.file` 指定日志输出路径,可以是具体的文件,也可以是 `os.Stderr` 或 `os.Stdout` 53 | `log.level` 日志级别,默认是 `5`,级别越大输出的错误越详细 54 | -------------------------------------------------------------------------------- /code/middleware/requestcache.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/accesslog" 5 | "github.com/baa-middleware/recovery" 6 | "github.com/baa-middleware/requestcache" 7 | "github.com/go-baa/baa" 8 | "github.com/go-baa/cache" 9 | ) 10 | 11 | func mainRequestcache() { 12 | app := baa.Default() 13 | app.Use(recovery.Recovery()) 14 | app.Use(accesslog.Logger()) 15 | 16 | // request cache middleware 17 | app.Use(requestcache.Middleware(requestcache.Option{ 18 | Enabled: true, 19 | Expires: requestcache.DefaultExpires, // 1 minute 20 | Headers: map[string]string{ 21 | "X-DIY": "baa", 22 | }, 23 | ContextRelated: false, 24 | })) 25 | 26 | // request cache depend cacher 27 | app.SetDI("cache", cache.New(cache.Options{ 28 | Name: "pageCache", 29 | Prefix: "MyApp", 30 | Adapter: "memory", 31 | Config: map[string]interface{}{ 32 | "bytesLimit": int64(128 * 1024 * 1024), // 128m 33 | }, 34 | })) 35 | 36 | app.Get("/", func(c *baa.Context) { 37 | c.String(200, "Hello, 世界") 38 | }) 39 | 40 | app.Run(":1323") 41 | } 42 | -------------------------------------------------------------------------------- /zh-CN/component/setting.md: -------------------------------------------------------------------------------- 1 | # Baa setting 2 | 3 | `https://github.com/go-baa/setting` 4 | 5 | 应用配置和配置文件管理 6 | 7 | > 注意: 配置文件被硬编码为 `./conf/app.ini`,必须放在这个路径 8 | 9 | ## 示例 10 | 11 | ``` 12 | // conf/app.ini 13 | [default] 14 | # app 15 | app.name = baaBlog 16 | app.version = 0.1 17 | app.url = "" 18 | debug = false 19 | 20 | # http 21 | http.address = 0.0.0.0 22 | http.port = 80 23 | http.access_open = off 24 | 25 | # output log to os.Stderr 26 | log.file = os.Stderr 27 | # 0 off, 1 fatal, 2 panic, 5 error, 6 warn, 10 info, 11 debug 28 | log.level = 11 29 | 30 | # development mode overwrite default config 31 | [development] 32 | debug = true 33 | 34 | # production mode overwrite default config 35 | [production] 36 | debug = false 37 | ``` 38 | 39 | ## 使用 40 | 41 | ``` 42 | package main 43 | 44 | import "github.com/go-baa/setting" 45 | 46 | func main() { 47 | appName := setting.AppName 48 | httpAddress := setting.Config.MustString("http.address", "127.0.0.1") 49 | httpPort := setting.Config.MustInt("http.port", 1323) 50 | httpAccessOpen := setting.Config.MustBool("http.access_open", false) 51 | } 52 | `` 53 | -------------------------------------------------------------------------------- /zh-CN/middleware/gzip.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 Gzip 2 | 3 | `github.com/baa-middleware/gzip` 4 | 5 | Gzip中间件提供了HTTP内容输出的Gzip配置和处理,可以减小网络传输。 6 | 7 | ## 使用 8 | 9 | ``` 10 | package main 11 | 12 | import ( 13 | "github.com/baa-middleware/accesslog" 14 | "github.com/baa-middleware/gzip" 15 | "github.com/baa-middleware/recovery" 16 | "github.com/go-baa/baa" 17 | ) 18 | 19 | func main() { 20 | app := baa.Default() 21 | app.Use(recovery.Recovery()) 22 | app.Use(accesslog.Logger()) 23 | 24 | if baa.Env == baa.PROD { 25 | app.Use(gzip.Gzip(gzip.Options{ 26 | CompressionLevel: 9, 27 | })) 28 | } 29 | 30 | app.Get("/", func(c *baa.Context) { 31 | c.String(200, "Hello, 世界") 32 | }) 33 | 34 | app.Run(":1323") 35 | } 36 | ``` 37 | 38 | 在该示例中,注册了 gzip 仅在baa的产品模式开启,因为调试模式下开启gzip会导致500错误时不能正常浏览错误信息。这算不算这个BUG? 39 | 40 | ## 配置 41 | 42 | ### CompressionLevel `int` 43 | 44 | 压缩级别,参数值范围 -1 ~ 9,在 官方包中有定义,还给出了几个常量值的意义: 45 | 46 | https://golang.org/pkg/compress/flate/#pkg-constants 47 | 48 | ``` 49 | const ( 50 | NoCompression = 0 51 | BestSpeed = 1 52 | BestCompression = 9 53 | DefaultCompression = -1 54 | ) 55 | ``` 56 | 57 | -------------------------------------------------------------------------------- /zh-CN/log.md: -------------------------------------------------------------------------------- 1 | # Baa 日志 2 | 3 | baa 中的日志默认使用的是标准包中的 `log`,可以通过 `DI` 来替换全局的日志器。 4 | 5 | 新的日志器要求实现 `baa.Logger` 接口,并且注册的 `DI` 名称为 `logger`,如果不更换默认的日志则名称任意。 6 | 7 | ## 日志接口 8 | 9 | ``` 10 | type Logger interface { 11 | Print(v ...interface{}) 12 | Printf(format string, v ...interface{}) 13 | Println(v ...interface{}) 14 | Fatal(v ...interface{}) 15 | Fatalf(format string, v ...interface{}) 16 | Fatalln(v ...interface{}) 17 | Panic(v ...interface{}) 18 | Panicf(format string, v ...interface{}) 19 | Panicln(v ...interface{}) 20 | } 21 | ``` 22 | 23 | 这个接口其实是对标准包`log`的抽象,最基础的日志接口。 24 | 25 | ## 日志方法 26 | 27 | 假如你实现了新的日志管理,使用的姿势像这样: 28 | 29 | ``` 30 | app := baa.New() 31 | app.SetDI("logger", newLogger.New()) 32 | app.Get("/", func(c *baa.Context) { 33 | lg := c.DI("logger").(*newLogger.Logger) 34 | lg.Println("log line") 35 | }) 36 | ``` 37 | 38 | 其中 `newLogger` 意为你实现的新的日志器。 39 | 40 | ### 记录日志 41 | 42 | `func (b *Baa) Logger() Logger` 43 | 44 | baa 提供的全局日志器可以通过`app.Logger()` 获得到。 45 | 46 | 举个例子: 47 | 48 | ``` 49 | app := baa.New() 50 | app.Get("/", func(c *baa.Context) { 51 | lg := c.Baa().Logger() 52 | lg.Println("log line") 53 | }) 54 | ``` 55 | 56 | 除了 `Println` 你可以使用日志接口中的所有方法。 57 | -------------------------------------------------------------------------------- /zh-CN/middleware/static.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 静态资源优先 2 | 3 | `github.com/baa-middleware/static` 4 | 5 | 静态资源优先中间件,根据配置的`prefix`拦截路由,如果能匹配到静态资源则直接返回静态资源,否则进入路由匹配。 6 | 7 | 因为 `baa` 是一个自由的web开发框架,默认的路由行为就是一切都是 `404`,并不像 nginx/apache 等web服务器,只要有对应路径的静态文件就会显示。 8 | 9 | ## 使用 10 | 11 | ``` 12 | package main 13 | 14 | import ( 15 | "github.com/baa-middleware/accesslog" 16 | "github.com/baa-middleware/recovery" 17 | "github.com/baa-middleware/static" 18 | "github.com/go-baa/baa" 19 | ) 20 | 21 | func mainStatic() { 22 | app := baa.Default() 23 | app.Use(recovery.Recovery()) 24 | app.Use(accesslog.Logger()) 25 | 26 | // static 27 | app.Use(static.Static("/assets", "public/assets", false, nil)) 28 | 29 | app.Get("/", func(c *baa.Context) { 30 | c.String(200, "Hello, 世界") 31 | }) 32 | 33 | app.Run(":1323") 34 | } 35 | ``` 36 | 37 | ## 配置 38 | 39 | ``` 40 | func Static(prefix, dir string, index bool, h baa.HandlerFunc) baa.HandlerFunc 41 | ``` 42 | 43 | ### prefix `string` 44 | 45 | URI匹配前缀,如:/public, /assets 46 | 47 | ### dir `string` 48 | 49 | 静态资源路径,可以使用绝对路径,或者相对于运行路径的路径。 50 | 51 | ### index `bool` 52 | 53 | 是否运行列出目录。 54 | 55 | ### h `baa.HandlerFunc` 56 | 57 | 附件方法,传入一个 baa.Context 运行对输入和输出做处理。 58 | 59 | -------------------------------------------------------------------------------- /code/middleware/session.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/baa-middleware/accesslog" 5 | "github.com/baa-middleware/recovery" 6 | "github.com/baa-middleware/session" 7 | "github.com/go-baa/baa" 8 | ) 9 | 10 | func mainSession() { 11 | app := baa.Default() 12 | app.Use(recovery.Recovery()) 13 | app.Use(accesslog.Logger()) 14 | 15 | // session config 16 | redisOptions := session.RedisOptions{} 17 | redisOptions.Addr = "127.0.0.1:6379" 18 | redisOptions.Prefix = "Prefix:" 19 | 20 | memoryOptions := session.MemoryOptions{ 21 | BytesLimit: 1024 * 1024, 22 | } 23 | 24 | app.Use(session.Middleware(session.Options{ 25 | Name: "BAASESSIONID", 26 | Provider: &session.ProviderOptions{ 27 | Adapter: "memory", // redis / memory 28 | Config: memoryOptions, 29 | }, 30 | })) 31 | 32 | app.Get("/", func(c *baa.Context) { 33 | // get the session handler 34 | session := c.Get("session").(*session.Session) 35 | session.Set("hi", "baa") 36 | 37 | c.String(200, "Hello, 世界") 38 | }) 39 | 40 | app.Get("/session", func(c *baa.Context) { 41 | // get the session handler 42 | session := c.Get("session").(*session.Session) 43 | c.String(200, "SessionID: "+session.ID()+", hi, "+session.Get("hi").(string)) 44 | }) 45 | 46 | app.Run(":1323") 47 | } 48 | -------------------------------------------------------------------------------- /zh-CN/component/render.md: -------------------------------------------------------------------------------- 1 | # Baa render 2 | 3 | `https://github.com/go-baa/render` 4 | 5 | render 是一个 增强型模板引擎,相对 baa 内置的模板引擎有如下特性: 6 | 7 | - 模板容器,可以使用 `template` 语法加载模板片段. 8 | - 模板缓存,在程序启动时即加载所有模板到内存中执行预编译,并且可以感知文件变化自动重新编译. 9 | - 可定制模板目录,模板文件扩张名,支持扩充模板函数. 10 | 11 | ## 使用 12 | 13 | 通过 `DI` 注册 `render` 即可。 14 | 15 | ``` 16 | package main 17 | 18 | import ( 19 | "github.com/go-baa/render" 20 | "github.com/go-baa/baa" 21 | ) 22 | 23 | func main() { 24 | // new app 25 | app := baa.New() 26 | 27 | // register render 28 | // render is template DI for baa, must this name. 29 | app.SetDI("render", render.New(render.Options{ 30 | Baa: app, 31 | Root: "templates/", 32 | Extensions: []string{".html", ".tmpl"}, 33 | })) 34 | 35 | // router 36 | app.Get("/", func(c *baa.Context) { 37 | c.HTML(200, "index") 38 | }) 39 | 40 | // run app 41 | app.Run(":1323") 42 | } 43 | ``` 44 | 45 | ## 配置 `render.Options` 46 | 47 | ### Baa `*baa.Baa` 48 | 49 | render 需要传递 baa 实例 50 | 51 | ### Root `string` 52 | 53 | 模板目录,指定模板目录,`c.HTML` 渲染模板时将从该目录下查找目录,不需要在渲染是指定目录名。 54 | 55 | ### Extensions `[]string` 56 | 57 | 模板文件扩展名,可以指定多个,渲染模板时无需指定扩展名,将根据配置寻找对应的文件。 58 | 59 | ### Functions `map[string]interface{}` 60 | 61 | 扩展函数,参考:[FuncMap](https://godoc.org/html/template#FuncMap) 62 | 63 | ## 扩展语法 64 | 65 | ### 加载模板片段 66 | 67 | ``` 68 | {{ template "share/footer" }} 69 | ``` 70 | -------------------------------------------------------------------------------- /zh-CN/middleware.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 2 | 3 | baa 支持通过 `中间件` 机制注入请求过程,实现类似插件的功能。 4 | 5 | 根据注册的顺序依次执行,在整个执行过程中通过共享 `Context` 来实现数据传递和流程控制。 6 | 7 | 多个中间件的交替运行以 `Context.Next()` 和 `Context.Break()` 方法来控制和转换。 8 | 9 | > 路由执行时,中间件和路由方法会组成链式操作,中间件优先路由方法先进入执行。 10 | 11 | ## 编写中间件 12 | 13 | ``` 14 | func (b *Baa) Use(m ...Middleware) 15 | ``` 16 | 17 | 通过 `Use` 方法就可以注册一个中间件,接收多个 `Middleware` 类型。 18 | 19 | ``` 20 | type Middleware interface{} 21 | ``` 22 | 23 | 举个例子 24 | 25 | ``` 26 | package main 27 | 28 | import ( 29 | "time" 30 | 31 | "github.com/go-baa/baa" 32 | ) 33 | 34 | func main() { 35 | app := baa.Default() 36 | 37 | app.Use(func(c *baa.Context) { 38 | // 进入,记录时间 39 | start := time.Now() 40 | 41 | // 接着执行其他中间件 42 | c.Next() 43 | 44 | // 执行完其他的,最后,输出请求日志 45 | c.Baa().Logger().Printf("%s %s %s %v %v", c.RemoteAddr(), c.Req.Method, c.URL(false), c.Resp.Status(), time.Since(start)) 46 | }) 47 | 48 | app.Get("/", func(c *baa.Context) { 49 | c.String(200, "Hello, 世界") 50 | }) 51 | 52 | app.Run(":1323") 53 | } 54 | ``` 55 | 56 | 看上面的例子,整个中间件只有三句代码,要说明的是,输出日志放在了 `c.Next()` 之后执行时为了获得业务的执行时间,并没有要求必须放在那儿,只要有 `c.Next()` 就可以了。 57 | 58 | 最后,在中间件过程中,如果要中断路由操作提前退出,可以使用 `c.Break()`。 59 | 60 | > 其实,上面的示例就是我们的 [accesslog](https://github.com/baa-middleware/accesslog) 中间件。 61 | 62 | ## 使用中间件 63 | 64 | 我们已经编写了一些常用的中间件,可以直接引入使用。 65 | 66 | 1. import 67 | 2. Use 68 | 69 | 使用示例: 70 | 71 | ``` 72 | package main 73 | 74 | import ( 75 | "github.com/baa-middleware/accesslog" 76 | "github.com/go-baa/baa" 77 | ) 78 | 79 | func main() { 80 | app := baa.Default() 81 | app.Use(accesslog.Logger()) 82 | 83 | app.Get("/", func(c *baa.Context) { 84 | c.String(200, "Hello, 世界") 85 | }) 86 | 87 | app.Run(":1323") 88 | } 89 | ``` 90 | 91 | 不同的中间件在引入的时候,可能有不同的参数,注意看各个组件部分的文档介绍。 92 | -------------------------------------------------------------------------------- /en-US/README.md: -------------------------------------------------------------------------------- 1 | # Baa 2 | 3 | an express Go web framework with routing, middleware, dependency injection, http context. 4 | 5 | Baa is ``no reflect``, ``no regexp``. 6 | 7 | ## Getting Started 8 | 9 | Install: 10 | 11 | ``` 12 | go get -u github.com/go-baa/baa 13 | ``` 14 | 15 | Example: 16 | 17 | ``` 18 | // baa.go 19 | package main 20 | 21 | import ( 22 | "github.com/go-baa/baa" 23 | ) 24 | 25 | func main() { 26 | app := baa.New() 27 | app.Get("/", func(c *baa.Context) { 28 | c.String(200, "Hello, 世界") 29 | }) 30 | app.Run(":1323") 31 | } 32 | ``` 33 | 34 | Run: 35 | 36 | ``` 37 | go run baa.go 38 | ``` 39 | 40 | Explore: 41 | 42 | ``` 43 | http://127.0.0.1:1323/ 44 | ``` 45 | 46 | ## Features 47 | 48 | * route support static, param, group 49 | * route support handler chain 50 | * route support static file serve 51 | * middleware supoort handle chain 52 | * dependency injection support* 53 | * context support JSON/JSONP/XML/HTML response 54 | * centralized HTTP error handling 55 | * centralized log handling 56 | * whichever template engine support(emplement baa.Renderer) 57 | 58 | ## Examples 59 | 60 | https://github.com/go-baa/example 61 | 62 | * [blog](https://github.com/go-baa/example/tree/master/blog) 63 | 64 | ## Middlewares 65 | 66 | * [gzip](https://github.com/baa-middleware/gzip) 67 | * [accesslog](https://github.com/baa-middleware/accesslog) 68 | * [recovery](https://github.com/baa-middleware/recovery) 69 | * [session](https://github.com/baa-middleware/session) 70 | * [static](https://github.com/baa-middleware/static) 71 | * [requestcache](https://github.com/baa-middleware/requestcache) 72 | * [nocache](https://github.com/baa-middleware/nocache) 73 | * [jwt](https://github.com/baa-middleware/jwt) 74 | * [cors](https://github.com/baa-middleware/cors) 75 | 76 | ## Components 77 | 78 | * [cache](https://github.com/go-baa/cache) 79 | * [render](https://github.com/go-baa/render) 80 | * [pongo2](https://github.com/go-baa/pongo2) 81 | * [router](https://github.com/go-baa/router) 82 | * [pool](https://github.com/go-baa/pool) 83 | * [bat](https://github.com/go-baa/bat) 84 | 85 | -------------------------------------------------------------------------------- /code/router/router.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/go-baa/baa" 7 | "github.com/go-baa/router/regtree" 8 | ) 9 | 10 | func main() { 11 | app := baa.New() 12 | app.SetDI("router", regtree.New(app)) 13 | 14 | // normal 15 | app.Get("/", func(c *baa.Context) { 16 | c.String(200, "Hello, 世界") 17 | }) 18 | app.Post("/", func(c *baa.Context) { 19 | c.String(200, c.Req.Method) 20 | }) 21 | app.Get("/admin", func(c *baa.Context) { 22 | if c.GetCookie("login_id") != "admin" { 23 | c.Redirect(302, "/login") 24 | c.Break() 25 | } 26 | }, func(c *baa.Context) { 27 | c.String(200, "恭喜你,看到后台了") 28 | }) 29 | app.Get("/login", func(c *baa.Context) { 30 | c.Resp.Header().Set("Content-Type", "text/html; charset=utf-8") 31 | c.SetCookie("login_id", "admin", 3600, "/") 32 | c.Resp.Write([]byte("登录成功,点击进入后台")) 33 | }) 34 | 35 | // static route 36 | app.Get("/foo", func(c *baa.Context) { 37 | c.String(200, c.URL(true)) 38 | }) 39 | app.Get("/bar", func(c *baa.Context) { 40 | c.String(200, c.URL(true)) 41 | }) 42 | 43 | // param route 44 | app.Get("/user/:id", func(c *baa.Context) { 45 | c.String(200, "My user id is: "+c.Param("id")) 46 | }) 47 | app.Get("/user/:id/project/:pid", func(c *baa.Context) { 48 | id := c.ParamInt("id") 49 | pid := c.ParamInt("pid") 50 | c.String(200, fmt.Sprintf("user id: %d, project id: %d", id, pid)) 51 | }) 52 | 53 | // regexp route 54 | app.Get("/user-:id(\\d+)", func(c *baa.Context) { 55 | c.String(200, "My user id is: "+c.Param("id")) 56 | }) 57 | app.Get("/user-:id(\\d+)-project-:pid(\\d+)", func(c *baa.Context) { 58 | id := c.ParamInt("id") 59 | pid := c.ParamInt("pid") 60 | c.String(200, fmt.Sprintf("user id: %d, project id: %d", id, pid)) 61 | }) 62 | 63 | // group 64 | app.Group("/group", func() { 65 | app.Get("/", func(c *baa.Context) { 66 | c.String(200, "我是组的首页") 67 | }) 68 | app.Group("/user", func() { 69 | app.Get("/", func(c *baa.Context) { 70 | c.String(200, "我是组的用户") 71 | }) 72 | app.Get("/:id", func(c *baa.Context) { 73 | c.String(200, "in group, user id: "+c.Param("id")) 74 | }) 75 | }) 76 | app.Get("/:gid", func(c *baa.Context) { 77 | c.String(200, "in group, group id: "+c.Param("gid")) 78 | }) 79 | }, func(c *baa.Context) { 80 | // 我是组内的前置检测,过不了我这关休想访问组内的资源 81 | }) 82 | 83 | app.Run(":1323") 84 | } 85 | -------------------------------------------------------------------------------- /zh-CN/middleware/session.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 session 2 | 3 | `github.com/baa-middleware/session` 4 | 5 | Go中的HTTP包默认提供了对cookie的管理,但是session并没有,需要各显神通,这就是一个简单的实现。 6 | 7 | session 中间件提供了对session的管理和使用,支持 redis/memory 适配器。 8 | 9 | ## 使用 10 | 11 | ``` 12 | package main 13 | 14 | import ( 15 | "github.com/baa-middleware/accesslog" 16 | "github.com/baa-middleware/recovery" 17 | "github.com/baa-middleware/session" 18 | "github.com/go-baa/baa" 19 | ) 20 | 21 | func main() { 22 | app := baa.Default() 23 | app.Use(recovery.Recovery()) 24 | app.Use(accesslog.Logger()) 25 | 26 | // session config 27 | redisOptions := session.RedisOptions{} 28 | redisOptions.Addr = "127.0.0.1:6379" 29 | redisOptions.Prefix = "Prefix:" 30 | 31 | memoryOptions := session.MemoryOptions{ 32 | BytesLimit: 1024 * 1024, 33 | } 34 | 35 | app.Use(session.Middleware(session.Options{ 36 | Name: "BAASESSIONID", 37 | Provider: &session.ProviderOptions{ 38 | Adapter: "memory", // redis / memory 39 | Config: memoryOptions, 40 | }, 41 | })) 42 | 43 | app.Get("/", func(c *baa.Context) { 44 | // get the session handler 45 | session := c.Get("session").(*session.Session) 46 | session.Set("hi", "baa") 47 | 48 | c.String(200, "Hello, 世界") 49 | }) 50 | 51 | app.Get("/session", func(c *baa.Context) { 52 | // get the session handler 53 | session := c.Get("session").(*session.Session) 54 | c.String(200, "SessionID: "+session.ID()+", hi, "+session.Get("hi").(string)) 55 | }) 56 | 57 | app.Run(":1323") 58 | } 59 | ``` 60 | 61 | 在该示例中,注册了 gzip 仅在baa的产品模式开启,因为调试模式下开启gzip会导致500错误时不能正常浏览错误信息。这算不算这个BUG? 62 | 63 | ## 配置 64 | 65 | ### Name `string` 66 | 67 | Session 的名称 68 | 69 | ### IDLength `int` 70 | 71 | SessionID 的长度,默认 `16` 位 72 | 73 | ### Provider `*ProviderOptions` 74 | 75 | Session的存储器选项 76 | 77 | #### Adapter `string` 78 | 79 | 存储器的适配器类型,支持 `redis` 和 `memory` 80 | 81 | > 注意:`memory` 适配器类型,如果应用重启session将全部失效,在集群模式下也不能共享session。 82 | 83 | #### Config `interface{}` 84 | 85 | 存储器的适配器配置,不同的适配器类型应使用不同的配置, `session.RedisOptions{}` 或者 `session.MemoryOptions{}` 86 | 87 | ### Cookie `*CookieOptions` 88 | 89 | Session的Cookie配置 90 | 91 | #### Domain `string` 92 | 93 | Cookie有效域名,默认 空 94 | 95 | #### Path `string` 96 | 97 | Cookie有效路径,默认 `/` 98 | 99 | #### Secure `bool` 100 | 101 | Cookie是否加密,默认 `false` 102 | 103 | #### LifeTime `int64` 104 | 105 | Cookie有效期,默认 `0`,即是一个浏览器会话类型,关闭响应的页面即失效。 106 | 107 | #### HttpOnly `bool` 108 | 109 | Cookie是否仅浏览器有效,默认 `false` 110 | 111 | ### GCInterval `int64` 112 | 113 | Session GC频率,仅在 `memory` 存储器类型下有效 114 | 115 | ### MaxLifeTime `int64` 116 | 117 | Session有效期,默认 `0` 118 | -------------------------------------------------------------------------------- /zh-CN/middleware/requestcache.md: -------------------------------------------------------------------------------- 1 | # Baa 中间件 请求缓存控制 2 | 3 | `github.com/baa-middleware/requestcache` 4 | 5 | 请求缓存控制中间件可以针对全局或某个路由以请求的URI为KEY进行服务端内容缓存。 6 | 7 | ## 使用 8 | 9 | ``` 10 | package main 11 | 12 | import ( 13 | "github.com/baa-middleware/accesslog" 14 | "github.com/baa-middleware/recovery" 15 | "github.com/baa-middleware/requestcache" 16 | "github.com/go-baa/cache" 17 | "github.com/go-baa/baa" 18 | ) 19 | 20 | func mainRequestcache() { 21 | app := baa.Default() 22 | app.Use(recovery.Recovery()) 23 | app.Use(accesslog.Logger()) 24 | 25 | // request cache middleware 26 | app.Use(requestcache.Middleware(requestcache.Option{ 27 | Enabled: true, 28 | Expires: requestcache.DefaultExpires, // 1 minute 29 | Headers: map[string]string{ 30 | "X-DIY": "baa", 31 | }, 32 | ContextRelated: false, 33 | })) 34 | 35 | // request cache depend cacher 36 | app.SetDI("cache", cache.New(cache.Options{ 37 | Name: "pageCache", 38 | Prefix: "MyApp", 39 | Adapter: "memory", 40 | Config: map[string]interface{}{ 41 | "bytesLimit": int64(128 * 1024 * 1024), // 128m 42 | }, 43 | })) 44 | 45 | app.Get("/", func(c *baa.Context) { 46 | c.String(200, "Hello, 世界") 47 | }) 48 | 49 | app.Run(":1323") 50 | } 51 | ``` 52 | 53 | 建议该中间件的注册顺序为`最后一个`,避免由于其他中间件的逻辑缓存错误的内容。 54 | 55 | ## 配置 56 | 57 | > 本中间件依赖名为 `cache` 的 DI,需要注册一个 [Cacher](https://github.com/go-baa/cache),cacher 的配置见:[依赖注入/缓存](https://github.com/go-baa/doc/tree/master/zh-CN/component/cache.md) 58 | 59 | ### Enabled `bool` 60 | 61 | 是否开启请求缓存控制,默认值:`false` 62 | 63 | ### Expires `int64` 64 | 65 | 缓存过期时间,单位:秒,默认值:`600` 66 | 67 | ### Headers `map[string]string` 68 | 69 | 附加返回头部,默认值:`nil` 70 | 71 | ### ContextRelated `bool` 72 | 73 | 是否将HTTP上下文中传递的数据加入KEY组合,默认 `false` 74 | 75 | 如果开启,通过 `c.Set()` 设置的内容将会作为缓存KEY的一部分,默认缓存的kEY是URI 76 | 77 | ## 更多姿势 78 | 79 | ### 全局使用 80 | 81 | 配置为最后一个中间件。 82 | 83 | ``` 84 | if baa.Env == baa.PROD { 85 | // Gzip 86 | b.Use(gzip.Gzip(gzip.Options{CompressionLevel: 4})) 87 | 88 | // Request Cache 89 | b.Use(requestcache.Middleware(requestcache.Option{ 90 | Enabled: true, 91 | Expires: requestcache.DefaultExpires, 92 | })) 93 | } 94 | ``` 95 | 96 | ### 路由使用 97 | 98 | 配置为某一具体路由使用。 99 | 100 | ``` 101 | cache := requestcache.Middleware(requestcache.Option{ 102 | Enabled: !b.Debug(), 103 | Expires: requestcache.DefaultExpires, 104 | }) 105 | 106 | b.Group("/some-prefix", func() { 107 | // ... 108 | }, cache) 109 | ``` 110 | 111 | ### 使用多个配置 112 | 113 | 可以在不同的路由中使用不同的配置。 114 | 115 | ``` 116 | cache1 := requestcache.Middleware(requestcache.Option{ 117 | Enabled: !b.Debug(), 118 | Expires: 60 * 10, 119 | }) 120 | 121 | b.Group("/some-prefix", func() { 122 | // ... 123 | }, cache1) 124 | 125 | cache2 := requestcache.Middleware(requestcache.Option{ 126 | Enabled: !b.Debug(), 127 | Expires: 60 * 30, 128 | }) 129 | 130 | b.Group("/some-prefix-2", func() { 131 | // ... 132 | }, cache2) 133 | ``` 134 | -------------------------------------------------------------------------------- /zh-CN/component/pongo2.md: -------------------------------------------------------------------------------- 1 | # Baa pongo2 2 | 3 | `https://github.com/go-baa/pongo2` 4 | 5 | pongo2 是 一个将 [Pongo2](https://github.com/flosch/pongo2) 模板引擎应用到 baa的助手库。 6 | 7 | ## 使用 8 | 9 | 通过 `DI` 注册 `render` 即可。 10 | 11 | ``` 12 | package main 13 | 14 | import ( 15 | "github.com/go-baa/pongo2" 16 | "github.com/go-baa/baa" 17 | ) 18 | 19 | func main() { 20 | // new app 21 | app := baa.New() 22 | 23 | // register pongo2 render 24 | // render is template DI for baa, must be this name. 25 | app.SetDI("render", pongo2.New(pongo2.Options{ 26 | Baa: b, 27 | Root: "templates/", 28 | Extensions: []string{".html"}, 29 | Functions: map[string]interface{}{}, 30 | Context: map[string]interface{}{ 31 | "SITE_NAME": "Yet another website", 32 | }, 33 | })) 34 | 35 | // router 36 | app.Get("/", func(c *baa.Context) { 37 | c.HTML(200, "index") 38 | }) 39 | 40 | // run app 41 | app.Run(":1323") 42 | } 43 | ``` 44 | 45 | ## 配置 `pongo2.Options` 46 | 47 | ### Baa `*baa.Baa` 48 | 49 | render 需要传递 baa 实例 50 | 51 | ### Root `string` 52 | 53 | 模板目录,指定模板目录,`c.HTML` 渲染模板时将从该目录下查找目录,不需要在渲染是指定目录名。 54 | 55 | ### Extensions `[]string` 56 | 57 | 模板文件扩展名,可以指定多个,渲染模板时无需指定扩展名,将根据配置寻找对应的文件。 58 | 59 | ### Functions `map[string]interface{}` 60 | 61 | 扩展函数,参考:[FuncMap](https://godoc.org/html/template#FuncMap) 62 | 63 | ### Context `map[string]interface{}` 64 | 65 | 预置数据,模板变量 66 | 67 | ## 扩展语法 68 | 69 | ### 输出变量 70 | 71 | ``` 72 | {{ name }} 73 | ``` 74 | 75 | ### 加载模板片段 76 | 77 | ``` 78 | {% include "path/to/tpl.html" %} 79 | ``` 80 | 81 | 带参数加载 82 | 83 | ``` 84 | {% include "relative/path/to/tpl.html" with foo=var %} 85 | {% include "relative/path/to/tpl.html" with foo="bar" %} 86 | ``` 87 | 88 | > 嵌入的模板接收的参数将作为 `string` 类型。 89 | 90 | ### 条件语句 91 | 92 | ``` 93 | {% if vara %} 94 | {% elif varb %} 95 | {% else %} 96 | {% endif %} 97 | ``` 98 | 99 | ### 循环语句 100 | 101 | ``` 102 | {% for item in items %} 103 | {{ forloop.Counter }} {{ forloop.Counter0 }} {{ forloop.First }} {{ forloop.Last }} {{ forloop.Revcounter }} {{ forloop.Revcounter0 }} 104 | {{ item }} 105 | {% endfor %} 106 | ``` 107 | 108 | ### 内置过滤器 109 | 110 | * escape 111 | * safe 112 | * escapejs 113 | * add 114 | * addslashes 115 | * capfirst 116 | * center 117 | * cut 118 | * date 119 | * default 120 | * default_if_none 121 | * divisibleby 122 | * first 123 | * floatformat 124 | * get_digit 125 | * iriencode 126 | * join 127 | * last 128 | * length 129 | * length_is 130 | * linebreaks 131 | * linebreaksbr 132 | * linenumbers 133 | * ljust 134 | * lower 135 | * make_list 136 | * phone2numeric 137 | * pluralize 138 | * random 139 | * removetags 140 | * rjust 141 | * slice 142 | * stringformat 143 | * striptags 144 | * time 145 | * title 146 | * truncatechars 147 | * truncatechars_html 148 | * truncatewords 149 | * truncatewords_html 150 | * upper 151 | * urlencode 152 | * urlize 153 | * urlizetrunc 154 | * wordcount 155 | * wordwrap 156 | * yesno 157 | * float 158 | * integer 159 | 160 | ### extends / block / macro and so on ... 161 | 162 | 更多内容,参见 [django](https://docs.djangoproject.com/en/dev/ref/templates/language/). 163 | -------------------------------------------------------------------------------- /zh-CN/component/pool.md: -------------------------------------------------------------------------------- 1 | # Baa pool 2 | 3 | `https://github.com/go-baa/pool` 4 | 5 | pool 是一个通用的链接池管理库。 6 | 7 | ## 安装 8 | 9 | ``` 10 | go get -u github.com/go-baa/pool 11 | ``` 12 | 13 | ## 示例 14 | 15 | ``` 16 | package main 17 | 18 | import ( 19 | "log" 20 | "net" 21 | 22 | "github.com/go-baa/pool" 23 | ) 24 | 25 | func main() { 26 | // create, initialize cap, max cap, create function 27 | pl, err := pool.New(2, 10, func() interface{} { 28 | addr, _ := net.ResolveTCPAddr("tcp4", "127.0.0.1:8003") 29 | cli, err := net.DialTCP("tcp4", nil, addr) 30 | if err != nil { 31 | log.Fatalf("create client connection error: %v\n", err) 32 | } 33 | return cli 34 | }) 35 | if err != nil { 36 | log.Fatalf("create pool error: %v\n", err) 37 | } 38 | 39 | pl.Ping = func(conn interface{}) bool { 40 | // check connection status 41 | return true 42 | } 43 | 44 | pl.Close = func(conn interface{}) { 45 | // close connection 46 | conn.(*net.TCPConn).Close() 47 | } 48 | 49 | // get conn from pool 50 | c, err := pl.Get() 51 | if err != nil { 52 | log.Printf("get client error: %v\n", err) 53 | } 54 | conn := c.(*net.TCPConn) 55 | conn.Write([]byte("PING")) 56 | result := make([]byte, 4) 57 | n, err := conn.Read(result) 58 | if err != nil || n < 4 { 59 | log.Printf("read data error: %v, size: %d\n", err, n) 60 | } 61 | log.Printf("got data: %s\n", result) 62 | 63 | // put, back for reuse 64 | pl.Put(conn) 65 | 66 | // len 67 | log.Printf("total connections: %d\n", pl.Len()) 68 | 69 | // destroy, close all connections 70 | pl.Destroy() 71 | } 72 | ``` 73 | 74 | 完整的示例代码 [pool_test.go](https://github.com/go-baa/pool/blob/master/pool_test.go) 75 | 76 | ## 使用 77 | 78 | ### 初始化 79 | 80 | `func New(initCap, maxCap int, newFunc func() interface{}) (*Pool, error)` 81 | 82 | 设定初始化连接数 `initCap`,最大连接数 `maxCap` 和 初始化函数 `newFunc`,返回 连接池和可能的错误。 83 | 84 | ### 连接检测 `Ping` 85 | 86 | `func(interface{}) bool` 87 | 88 | 可以指定一个函数用于连接池获取和存储连接时对连接进行检测,出错的将剔除掉。 89 | 90 | 连接检测函数接受一个参数 `当前连接`,类型为 初始化函数返回的类型,根据检测结果返回 true/false。 91 | 92 | 示例: 93 | 94 | ``` 95 | pl, err := pool.New(2, 10, func() interface{} { 96 | return nil 97 | }) 98 | pl.Ping = func(conn) bool { 99 | return true 100 | } 101 | ``` 102 | 103 | ### 连接关闭 `Close` 104 | 105 | `func(interface{})` 106 | 107 | 可以指定一个函数用于连接池剔除连接时执行关闭动作。 108 | 109 | 连接关闭函数接受一个参数 `当前连接`,类型为 初始化函数返回的类型。 110 | 111 | 示例: 112 | 113 | ``` 114 | pl, err := pool.New(2, 10, func() interface{} { 115 | return nil 116 | }) 117 | pl.Close = func(conn) { 118 | conn.Close() 119 | } 120 | ``` 121 | 122 | ### 使用连接 123 | 124 | 使用时,先从连接池中获取一个连接,使用完毕需要还回来。 125 | 126 | 获取: 127 | 128 | ``` 129 | func (p *Pool) Get() (interface{}, error) 130 | ``` 131 | 132 | 返还: 133 | 134 | ``` 135 | func (p *Pool) Put(v interface{}) 136 | ``` 137 | 138 | 示例: 139 | 140 | ``` 141 | // get conn from pool 142 | c, err := pl.Get() 143 | if err != nil { 144 | log.Printf("get client error: %v\n", err) 145 | } 146 | 147 | // do something 148 | // ... 149 | 150 | pl.Put(c) 151 | ``` 152 | 153 | 也可以使用 `defer`来确保不忘记返还。 154 | 155 | ### 当前连接数 156 | 157 | `func (p *Pool) Len() int` 158 | 159 | 返回当前保持的连接数量。 160 | 161 | ### 销毁连接池 162 | 163 | `func (p *Pool) Destroy()` 164 | 165 | 销毁连接池时,将依次调用连接的 `Close` 方法。 166 | 167 | -------------------------------------------------------------------------------- /zh-CN/di.md: -------------------------------------------------------------------------------- 1 | # Baa 依赖注入 2 | 3 | 依赖注入(dependency injection)简称 DI,是 baa 实现的核心,baa 所有组件基于DI组装起来的。 4 | 5 | 默认的 日志、路由、模板 都是通过 DI 注册进来的,在 [Baa核心#更换内置引擎](https://github.com/go-baa/doc/blob/master/zh-CN/baa.md#更换内置引擎)一节也介绍过。 6 | 7 | Baa的初始化函数是这样写的: 8 | 9 | ``` 10 | // New create a baa application without any config. 11 | func New() *Baa { 12 | b := new(Baa) 13 | b.middleware = make([]HandlerFunc, 0) 14 | b.pool = sync.Pool{ 15 | New: func() interface{} { 16 | return NewContext(nil, nil, b) 17 | }, 18 | } 19 | if Env != PROD { 20 | b.debug = true 21 | } 22 | b.SetDIer(NewDI()) 23 | b.SetDI("router", NewTree(b)) 24 | b.SetDI("logger", log.New(os.Stderr, "[Baa] ", log.LstdFlags)) 25 | b.SetDI("render", newRender()) 26 | b.SetNotFound(b.DefaultNotFoundHandler) 27 | return b 28 | } 29 | ``` 30 | 31 | 代码出处,[baa.go](https://github.com/go-baa/baa/blob/master/baa.go) 32 | 33 | ## 注册 34 | 35 | `func (b *Baa) SetDI(name string, h interface{})` 36 | 37 | DI 的注册和使用都依赖于注册的名称,比如要更换内置组件必须注册为指定的名称。 38 | 39 | ### name string 40 | 41 | 依赖的名称 42 | 43 | ### h interface{} 44 | 45 | 依赖的实例,可以是任意类型 46 | 47 | 使用示例: 48 | 49 | ``` 50 | package main 51 | 52 | import ( 53 | "log" 54 | "os" 55 | 56 | "github.com/go-baa/baa" 57 | ) 58 | 59 | func main() { 60 | app := baa.Default() 61 | app.SetDI("logger", log.New(os.Stderr, "[BaaDI] ", log.LstdFlags)) 62 | 63 | app.Get("/", func(c *baa.Context) { 64 | c.String(200, "Hello, 世界") 65 | }) 66 | 67 | app.Run(":1323") 68 | } 69 | ``` 70 | 71 | ## 使用 72 | 73 | `func (b *Baa) GetDI(name string) interface{}` 74 | 75 | `func (c *Context) DI(name string) interface{}` 76 | 77 | 78 | 可以通过 `baa.GetDI` 或者 `c.DI` 来获取已经注册的依赖,如果未注册,返回 `nil` 79 | 80 | 由于注册的依赖可能是任意类型,故返回类型为 `interface{}`,所以获取后,需要做一次类型断言再使用。 81 | 82 | 使用示例: 83 | 84 | ``` 85 | package main 86 | 87 | import ( 88 | "log" 89 | "os" 90 | 91 | "github.com/go-baa/baa" 92 | ) 93 | 94 | func main() { 95 | app := baa.Default() 96 | app.SetDI("logger", log.New(os.Stderr, "[BaaDI] ", log.LstdFlags)) 97 | 98 | app.Get("/", func(c *baa.Context) { 99 | // use di 100 | logger := c.DI("logger").(*log.Logger) 101 | logger.Println("i am use logger di") 102 | 103 | c.String(200, "Hello, 世界") 104 | }) 105 | 106 | app.Run(":1323") 107 | } 108 | ``` 109 | 110 | ### 日志 111 | 112 | baa 将日志抽象为 `baa.Logger` 接口,只要实现了该接口,就可以注册为全局日志器。 113 | 114 | baa 内置的日志器使用的是标准包的 `log` 实例。 115 | 116 | 更换全局日志器: 117 | 118 | ``` 119 | app := baa.New() 120 | baa.SetDI("logger", newLogger) 121 | ``` 122 | 123 | > logger 是内置名称,该命名被用于全局日志器。 124 | > 125 | > 如果不是要更换全局日志,而是注册一个新的日志器用于其他用途,只需更改注册名称即可,而且也不需要实现 `baa.Logger` 接口。 126 | 127 | ### 路由 128 | 129 | 只要实现接口 `baa.Router` 接口即可。 130 | 131 | ``` 132 | app := baa.New() 133 | baa.SetDI("router", newRender) 134 | ``` 135 | 136 | > router 是内置名称,,该命名被用于全局路由器。 137 | 138 | baa 除了内置的 tree路由,还新增了两个路由器可用,见 [router](https://github.com/go-baa/router) 139 | 140 | ``` 141 | package main 142 | 143 | import ( 144 | "github.com/go-baa/router/regtree" 145 | "github.com/go-baa/baa" 146 | ) 147 | 148 | func main() { 149 | app := baa.Default() 150 | app.SetDI("router", regtree.New(app)) 151 | 152 | app.Get("/view-:id(\\d+)", func(c *baa.Context) { 153 | c.String(200, c.Param("id")) 154 | }) 155 | app.Get("/view-:id(\\d+)/project", func(c *baa.Context) { 156 | c.String(200, c.Param("id")+"/project") 157 | }) 158 | app.Run(":1323") 159 | } 160 | ``` 161 | 162 | ### 模板 163 | 164 | 只要实现接口 `baa.Renderer` 接口即可。 165 | 166 | ``` 167 | app := baa.New() 168 | baa.SetDI("render", newRender) 169 | ``` 170 | 171 | > render 是内置名称,该命名被用于模板渲染。 172 | 173 | baa 除了内置的 render简单模板渲染,还新增了两个模板渲染引擎: 174 | 175 | * [render](https://github.com/go-baa/doc/tree/master/zh-CN/component/render.md) 176 | * [pongo2](https://github.com/go-baa/doc/tree/master/zh-CN/component/pongo2.md) 177 | 178 | ### 缓存 179 | 180 | 缓存不是 baa内置依赖,作为一个常用组件实现,具体见: 181 | 182 | [baa组件/缓存](https://github.com/go-baa/doc/tree/master/zh-CN/component/cache.md) 183 | -------------------------------------------------------------------------------- /zh-CN/baa.md: -------------------------------------------------------------------------------- 1 | # Baa 核心 2 | 3 | ## 创建应用 4 | 5 | `func New() *Baa` 6 | 7 | 快速创建一个新的应用实例。 8 | 9 | `func Instance(name string) *Baa` 10 | 11 | 获取一个命名实例,如果实例不存在则调用 `New()` 创建并且命名。 12 | 13 | 命名实例 用于不同模块间共享 baa实例的场景。在 入口中创建,在其他模块中 `baa.Instance(name)` 可获取指定的实例。 14 | 15 | `func Default() *Baa` 16 | 17 | 使用默认的应用实例。 18 | 19 | Default 是 `Instance` 的一个默认实现,是全局唯一的实例。在共享场景下,不需要传递 baa 直接调用 `baa.Default()` 即可访问同一实例。 20 | 21 | 使用示例: 22 | 23 | ``` 24 | app := baa.New() 25 | app := baa.Default() 26 | app := baa.Instance("myApp") 27 | myApp := baa.Instance("myApp") 28 | ``` 29 | 30 | ## 路由管理 31 | 32 | baa 基于 http resetfull 模式设计了路由管理器,具体见 [路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md) 一节。 33 | 34 | ## 中间件 35 | 36 | baa 支持通过 中间件 机制,注入请求过程,实现类似插件的功能,具体见 [中间件](https://github.com/go-baa/doc/tree/master/zh-CN/middleware.md) 一节。 37 | 38 | ## 依赖注入 39 | 40 | 依赖注入(dependency injection)简称 DI,是 baa 实现的核心,baa 所有组件基于DI组装起来的。 41 | 42 | baa组件的更换,见 [更换内置引擎](#更换内置引擎) 一节。 43 | 44 | DI的具体使用,见 [依赖注入](https://github.com/go-baa/doc/tree/master/zh-CN/di.md) 一节。 45 | 46 | ## 运行应用 47 | 48 | `func (b *Baa) Run(addr string)` 49 | 50 | 指定一个监听地址,启动一个HTTP服务。 51 | 52 | 示例: 53 | 54 | ``` 55 | app := baa.Default() 56 | app.Run(":1323") 57 | ``` 58 | 59 | `func (b *Baa) RunTLS(addr, certfile, keyfile string)` 60 | 61 | 指定监听地址和TLS证书,启动一个HTTPS服务。 62 | 63 | 示例: 64 | 65 | ``` 66 | app := baa.Default() 67 | app.RunTLS(":8443", "cert/cert.cert", "cert/server.key") 68 | ``` 69 | 70 | ## 环境变量 71 | 72 | `BAA_ENV` 73 | 74 | baa 通过 系统环境变量 `BAA_ENV` 来设置运行模式。 75 | 76 | `baa.Env` 77 | 78 | 外部程序可以通过 `baa.Env` 变量来获取 baa 当前的运行模式。 79 | 80 | 运行模式常量 81 | 82 | ``` 83 | // DEV mode 84 | DEV = "development" 85 | // PROD mode 86 | PROD = "production" 87 | // TEST mode 88 | TEST = "test" 89 | ``` 90 | 91 | * baa.DEV 开发模式 92 | * baa.PROD 产品模式 93 | * baa.TEST 测试模式 94 | 95 | 示例代码: 96 | 97 | ``` 98 | if baa.Env == baa.PROD { 99 | // 应用运行在产品模式 100 | } 101 | ``` 102 | 103 | ## 调试 104 | 105 | `func (b *Baa) Debug() bool` 106 | 107 | 返回是否是调试模式,应用可以根据是否运行在调试模式,来输出调试信息。 108 | 109 | `func (b *Baa) SetDebug(v bool)` 110 | 111 | 默认根据运行环境决定是否开启调试模式,可以通过该方法开启/关闭调试模式。 112 | 113 | > 在 产品模式 下,默认关闭调试模式,其他模式下默认开启调试模式。 114 | 115 | `func (b *Baa) Logger() Logger` 116 | 117 | 返回日志器,在应用中可以调用日志器来输出日志。 118 | 119 | 示例: 120 | 121 | ``` 122 | app := baa.New() 123 | log := app.Logger() 124 | log.Println("test") 125 | ``` 126 | 127 | ## 错误处理 128 | 129 | > 错误输出,只是给浏览器返回错误,但并不会阻止接下来绑定的方法。 130 | 131 | `func (b *Baa) NotFound(c *Context)` 132 | 133 | 调用该方法会直接 输出 404错误。 134 | 135 | `func (b *Baa) Error(err error, c *Context)` 136 | 137 | 调用该方法会直接输出 500错误,并根据运行模式决定是否在浏览器中返回具体错误。 138 | 139 | 示例 140 | 141 | ``` 142 | app := baa.New() 143 | app.Get("/", func(c *baa.Context) { 144 | c.Baa().NotFound(c) 145 | }) 146 | app.Get("/e", func(c *baa.Context) { 147 | c.Baa().Error(errors.New("something error"), c) 148 | }) 149 | 150 | ``` 151 | 152 | ## 更换内置引擎 153 | 154 | baa 采用以DI为核心的框架设计,内置模块均可使用新的实现通过DI更换。 155 | 156 | ### 日志器 157 | 158 | baa 将日志抽象为 `baa.Logger` 接口,只要实现了该接口,就可以注册为日志器。 159 | 160 | baa 内置的日志器使用的是标准包的 `log` 实例。 161 | 162 | 更换日志器: 163 | 164 | ``` 165 | app := baa.New() 166 | baa.SetDI("logger", newLogger) 167 | ``` 168 | 169 | > logger 是内置名称,该命名被用于全局日志器。 170 | 171 | ### 路由器 172 | 173 | 只要实现接口 `baa.Router` 接口即可。 174 | 175 | ``` 176 | app := baa.New() 177 | baa.SetDI("router", newRouter) 178 | ``` 179 | 180 | > router 是内置名称,该命名被用于全局路由器。 181 | 182 | baa 除了内置的 tree路由,还新增了两个路由器可用,见 [router](https://github.com/go-baa/router) 183 | 184 | ### 模板引擎 185 | 186 | 只要实现接口 `baa.Renderer` 接口即可。 187 | 188 | ``` 189 | app := baa.New() 190 | baa.SetDI("render", newRender) 191 | ``` 192 | 193 | > render 是内置名称,该命名被用于模板渲染。 194 | 195 | ### DIer 196 | 197 | 甚至依赖注入管理器,自己也能被替换,只要实现 `baa.Dier` 接口即可。 198 | 199 | 请注意要在第一个设置,并且重设以上三个引擎,因为你的注入管理器中默认并没有内置引擎,BAA将发生错误。 200 | 201 | ``` 202 | app := baa.New() 203 | app.SetDIer(newDIer) 204 | app.SetDI("logger", log.New(os.Stderr, "[Baa] ", log.LstdFlags)) 205 | app.SetDI("render", new(baa.Render)) 206 | app.SetDI("router", baa.NewTree(app)) 207 | ``` 208 | -------------------------------------------------------------------------------- /zh-CN/component/cache.md: -------------------------------------------------------------------------------- 1 | # Baa 缓存 2 | 3 | `github.com/go-baa/cache` 4 | 5 | 缓存组件提供了通用的缓存管理能力,采用适配器模式开发,目前支持 memory, memcache, redis 三种存储。 6 | 7 | 支持常用操作命令:Get/Set/Incr/Decr/Delete/Exist/Flush 8 | 9 | ## 使用 10 | 11 | 缓存组件是通用的,和`baa`的结合通过`DI`来引入: 12 | 13 | ``` 14 | package main 15 | 16 | import ( 17 | "github.com/go-baa/cache" 18 | "github.com/go-baa/baa" 19 | ) 20 | 21 | func main() { 22 | // new app 23 | app := baa.New() 24 | 25 | // register cache 26 | app.SetDI("cache", cache.New(cache.Options{ 27 | Name: "cache", 28 | Prefix: "MyApp", 29 | Adapter: "memory", 30 | Config: map[string]interface{}{}, 31 | })) 32 | 33 | // router 34 | app.Get("/", func(c *baa.Context) { 35 | ca := c.DI("cache").(cache.Cacher) 36 | ca.Set("test", "baa", 10) 37 | var v string 38 | ca.Get("test", &v) 39 | c.String(200, v) 40 | }) 41 | 42 | // run app 43 | app.Run(":1323") 44 | } 45 | ``` 46 | 47 | `memory`适配器是默认引入的,使用其他适配器需要先导入: 48 | 49 | ``` 50 | import( 51 | "github.com/go-baa/baa" 52 | "github.com/go-baa/cache" 53 | _ "github.com/go-baa/cache/memcache" 54 | _ "github.com/go-baa/cache/redis" 55 | ) 56 | ``` 57 | 58 | ### 存储 Set 59 | 60 | `func Set(key string, v interface{}, ttl int64) error` 61 | 62 | 根据 `key` 存储 `v` 的值,缓存有效期为 `ttl`秒,返回 `nil` 或者 `error` 63 | 64 | > `ttl` 等于0,表示永不过期,如果是内存适配器,在应用重启后数据将丢失 65 | 66 | ### 获取 Get 67 | 68 | `func Get(key string, out interface{}) error` 69 | 70 | 根据 `key` 获取存储的值赋给 `out`,返回 `nil` 或者 `error` 71 | 72 | > 要求 `out` 为 `引用类型`,Go语言中是按值传递,如果不是引用类型外部无法获取到数据 73 | 74 | ### 删除 Delete 75 | 76 | `func Delete(key string) error` 77 | 78 | 删除指定 `key` 的缓存数据,返回 `nil` 或者 `error` 79 | 80 | ### 递增 Incr 81 | 82 | `func Incr(key string) (int64, error)` 83 | 84 | 根据 `key` 将存储的值 `加1` 更新存储并返回,额外返回 `nil` 或者 `error` 85 | 86 | > `key` 的值应为 数值类型,否则将发生错误 87 | 88 | ### 递减 Decr 89 | 90 | `func Decr(key string) (int64, error)` 91 | 92 | 根据 `key` 将存储的值 `减1` 更新存储并返回,额外返回 `nil` 或者 `error` 93 | 94 | > `key` 的值应为 数值类型,否则将发生错误 95 | 96 | ### 检测是否存在 Exist 97 | 98 | `func Exist(key string) bool` 99 | 100 | 根据 `key` 检查是否存在有效的缓存数据,返回 `true` 表示存在,`false` 表示不存在 101 | 102 | ### 清空缓存 Flush 103 | 104 | `func Flush() error` 105 | 106 | 清空缓存中的数据 107 | 108 | ## 配置 109 | 110 | ### Name `string` 111 | 112 | 缓存实例名称 113 | 114 | ### Prefix `string` 115 | 116 | 缓存索引前缀 117 | 118 | ### Adapter `string` 119 | 120 | 适配器名称,目前支持 memory, memcache, redis 三种存储 121 | 122 | ### Config `map[string]interface{}` 123 | 124 | 适配器配置,不同的适配器有不同的配置。 125 | 126 | ### 适配器 Memory 127 | 128 | #### bytesLimit `int64` 129 | 130 | 内存适配器,只有一个配置参数,内存大小限制,单位 字节,默认为 `128m` 131 | 132 | #### 使用示例 133 | 134 | ``` 135 | app.SetDI("cache", cache.New(cache.Options{ 136 | Name: "cache", 137 | Prefix: "MyApp", 138 | Adapter: "memory", 139 | Config: map[string]interface{}{ 140 | "bytesLimit": int64(128 * 1024 * 1024), // 128m 141 | }, 142 | })) 143 | ``` 144 | 145 | ### 适配器 Memcache 146 | 147 | #### host `string` 148 | 149 | memcached 服务器IP地址,默认为 `127.0.0.1` 150 | 151 | #### port `string` 152 | 153 | memcached 服务器端口,默认为 `11211` 154 | 155 | #### 使用示例 156 | 157 | ``` 158 | app.SetDI("cache", cache.New(cache.Options{ 159 | Name: "cache", 160 | Prefix: "MyApp", 161 | Adapter: "memcache", 162 | Config: map[string]interface{}{ 163 | "host": "127.0.0.1", 164 | "port": "11211", 165 | }, 166 | })) 167 | ``` 168 | 169 | ### 适配器 Redis 170 | 171 | #### host `string` 172 | 173 | redis 服务器IP地址,默认为 `127.0.0.1` 174 | 175 | #### port `string` 176 | 177 | redis 服务器端口,默认为 `6379` 178 | 179 | #### password `string` 180 | 181 | redis 服务器连接密码,默认 `空` 182 | 183 | #### poolsize `int` 184 | 185 | redis 库连接池限制,默认保持 `10` 个连接 186 | 187 | #### 使用示例 188 | 189 | ``` 190 | app.SetDI("cache", cache.New(cache.Options{ 191 | Name: "cache", 192 | Prefix: "MyApp", 193 | Adapter: "redis", 194 | Config: map[string]interface{}{ 195 | "host": "127.0.0.1", 196 | "port": "6379", 197 | "password": "", 198 | "poolsize": 10, 199 | }, 200 | })) 201 | ``` 202 | -------------------------------------------------------------------------------- /zh-CN/README.md: -------------------------------------------------------------------------------- 1 | # Baa 2 | 3 | 一个简单高效的Go web开发框架。主要有路由、中间件,依赖注入和HTTP上下文构成。 4 | 5 | Baa 不使用 ``反射`` 和 ``正则``,没有魔法的实现。 6 | 7 | ## 特性 8 | 9 | * 支持静态路由、参数路由、组路由(前缀路由/命名空间)和路由命名 10 | * 路由支持链式操作 11 | * 路由支持文件/目录服务 12 | * 中间件支持链式操作 13 | * 支持依赖注入* 14 | * 支持JSON/JSONP/XML/HTML格式输出 15 | * 统一的HTTP错误处理 16 | * 统一的日志处理 17 | * 支持任意更换模板引擎(实现baa.Renderer接口即可) 18 | 19 | ## 文档目录 20 | 21 | * [Baa核心](https://github.com/go-baa/doc/tree/master/zh-CN/baa.md) 22 | * [路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md) 23 | * [常规路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#常规路由) 24 | * [路由语法](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#路由语法) 25 | * [静态路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#静态路由) 26 | * [参数路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#参数路由) 27 | * [正则路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#正则路由) 28 | * [路由选项](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#路由选项) 29 | * [组路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#组路由) 30 | * [链式处理](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#链式处理) 31 | * [命名路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#命名路由) 32 | * [文件路由](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#文件路由) 33 | * [自定义错误](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#自定义错误) 34 | * [500错误](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#500错误) 35 | * [404错误](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#404错误) 36 | * [Websocket](https://github.com/go-baa/doc/tree/master/zh-CN/router.md#websocket) 37 | * [中间件](https://github.com/go-baa/doc/tree/master/zh-CN/middleware.md) 38 | * [编写中间件](https://github.com/go-baa/doc/blob/master/zh-CN/middleware.md#编写中间件) 39 | * [使用中间件](https://github.com/go-baa/doc/blob/master/zh-CN/middleware.md#使用中间件) 40 | * [错误恢复](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/recovery.md) 41 | * [访问日志](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/accesslog.md) 42 | * [gzip](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/gzip.md) 43 | * [session](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/session.md) 44 | * [静态资源优先](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/static.md) 45 | * [请求缓存控制](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/requestcache.md) 46 | * [强制不缓存](https://github.com/go-baa/doc/blob/master/zh-CN/middleware/nocache.md) 47 | * [依赖注入 DI](https://github.com/go-baa/doc/tree/master/zh-CN/di.md) 48 | * [注册](https://github.com/go-baa/doc/tree/master/zh-CN/di.md#注册) 49 | * [使用](https://github.com/go-baa/doc/tree/master/zh-CN/di.md#使用) 50 | * [日志](https://github.com/go-baa/doc/tree/master/zh-CN/di.md#日志) 51 | * [路由](https://github.com/go-baa/doc/tree/master/zh-CN/di.md#路由) 52 | * [模板](https://github.com/go-baa/doc/tree/master/zh-CN/di.md#模板) 53 | * [缓存](https://github.com/go-baa/doc/tree/master/zh-CN/component/cache.md) 54 | * [HTTP上下文](https://github.com/go-baa/doc/tree/master/zh-CN/context.md) 55 | * [Request](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#request) 56 | * [URL参数](https://github.com/go-baa/doc/blob/master/zh-CN/context.md#url参数) 57 | * [路由参数](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#路由参数) 58 | * [Cookie](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#cookie) 59 | * [文件上传](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#文件上传) 60 | * [Response](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#response) 61 | * [数据存储](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#数据存储) 62 | * [内容输出](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#内容输出) 63 | * [有用的函数](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#有用的函数) 64 | * [模板渲染](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#模板渲染) 65 | * [模板语法](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#模板语法) 66 | * [模板接口](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#模板接口) 67 | * [render](https://github.com/go-baa/doc/tree/master/zh-CN/component/render.md) 68 | * [pongo2](https://github.com/go-baa/doc/tree/master/zh-CN/component/pongo2.md) 69 | * [日志](https://github.com/go-baa/doc/tree/master/zh-CN/log.md) 70 | * [日志接口](https://github.com/go-baa/doc/tree/master/zh-CN/log.md#日志接口) 71 | * [日志方法](https://github.com/go-baa/doc/tree/master/zh-CN/log.md#日志方法) 72 | * [数据库](https://github.com/go-baa/doc/tree/master/zh-CN/database.md) 73 | * [gorm](http://jinzhu.me/gorm/) 74 | * [xorm](http://xorm.io/) 75 | * [mgo](https://labix.org/mgo) 76 | * [组件](https://github.com/go-baa/doc/tree/master/zh-CN/component.md) 77 | * [bat](https://github.com/go-baa/doc/tree/master/zh-CN/component/bat.md) 78 | * [cache](https://github.com/go-baa/doc/tree/master/zh-CN/component/cache.md) 79 | * [pongo2](https://github.com/go-baa/doc/tree/master/zh-CN/component/pongo2.md) 80 | * [pool](https://github.com/go-baa/doc/tree/master/zh-CN/component/pool.md) 81 | * [render](https://github.com/go-baa/doc/tree/master/zh-CN/component/render.md) 82 | * [router](https://github.com/go-baa/doc/tree/master/zh-CN/component/router.md) 83 | * [log](https://github.com/go-baa/doc/tree/master/zh-CN/component/log.md) 84 | * [setting](https://github.com/go-baa/doc/tree/master/zh-CN/component/setting.md) 85 | * [工程化](https://github.com/go-baa/doc/tree/master/zh-CN/project.md) 86 | * [目录结构](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#目录结构) 87 | * [控制器](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#控制器) 88 | * [数据模型](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#数据模型) 89 | * [配置文件](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#配置文件) 90 | * [模板](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#模板) 91 | * [静态资源](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#静态资源) 92 | * [打包发布](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#打包发布) 93 | * [依赖管理](https://github.com/go-baa/doc/tree/master/zh-CN/project.md#依赖管理) 94 | 95 | 96 | ## 快速上手 97 | 98 | ### 安装 99 | 100 | ``` 101 | go get -u github.com/go-baa/baa 102 | ``` 103 | 104 | ### 代码 105 | 106 | ``` 107 | // baa.go 108 | package main 109 | 110 | import ( 111 | "github.com/go-baa/baa" 112 | ) 113 | 114 | func main() { 115 | app := baa.New() 116 | app.Get("/", func(c *baa.Context) { 117 | c.String(200, "Hello, 世界") 118 | }) 119 | app.Run(":1323") 120 | } 121 | ``` 122 | 123 | ### 运行 124 | 125 | ``` 126 | go run baa.go 127 | ``` 128 | 129 | ### 浏览 130 | 131 | ``` 132 | http://127.0.0.1:1323/ 133 | ``` 134 | 135 | ### 使用中间件 136 | 137 | ``` 138 | package main 139 | 140 | import ( 141 | "github.com/baa-middleware/accesslog" 142 | "github.com/baa-middleware/recovery" 143 | "github.com/go-baa/baa" 144 | ) 145 | 146 | func main() { 147 | app := baa.Default() 148 | app.Use(recovery.Recovery()) 149 | app.Use(accesslog.Logger()) 150 | 151 | app.Get("/", func(c *baa.Context) { 152 | c.String(200, "Hello, 世界") 153 | }) 154 | 155 | app.Run(":1323") 156 | } 157 | ``` 158 | 159 | ## 示例 160 | 161 | https://github.com/go-baa/example 162 | 163 | * [blog](https://github.com/go-baa/example/tree/master/blog) 164 | * [api](http://github.com/go-baa/example/tree/master/api) 165 | * [websocket](http://github.com/go-baa/example/tree/master/websocket) 166 | -------------------------------------------------------------------------------- /zh-CN/project.md: -------------------------------------------------------------------------------- 1 | # Baa 工程化 2 | 3 | 所谓工程化,不是baa的功能,而是在使用baa的过程中总结出的一种姿势,姑且称之为:最佳实践。 4 | 5 | 按照一般MVC的开发规范,一个程序通常会有 数据模型、模板视图、业务控制,辅助的还要有 前端资源文件,应用配置文件,可能还要记录日志文件。 6 | 7 | ## 目录结构 8 | 9 | ``` 10 | project 11 | |-- conf 12 | |-- app.ini 13 | |-- controller 14 | |-- index.go 15 | |-- article.go 16 | |-- user.go 17 | |-- data 18 | |-- log 19 | |-- model 20 | |-- base 21 | |-- base.go 22 | |-- cache.go 23 | |-- base.go 24 | |-- article.go 25 | |-- user.go 26 | |-- module 27 | |-- util 28 | |-- util.go 29 | |-- template 30 | |-- template.go 31 | |-- public 32 | |-- assets 33 | |-- css 34 | |-- images 35 | |-- js 36 | |-- upload 37 | |-- robots.txt 38 | |-- router 39 | |-- init.go 40 | |-- router.go 41 | |-- template 42 | |-- share 43 | |-- header.html 44 | |-- footer.html 45 | |-- article 46 | |-- index.html 47 | |-- show.html 48 | |-- user 49 | |-- show.html 50 | |-- index.html 51 | |-- main.go 52 | |-- README.md 53 | ``` 54 | 55 | 结构说明 56 | 57 | | 路径 | 说明 | 备注 | 58 | |-----------------------------|--------------|--------| 59 | | conf | 配置文件目录 | -- | 60 | | conf/app.ini | 应用配置文件 | [setting](https://github.com/go-baa/setting) 配置库要求的配置文件路由 61 | | controller | 业务控制器目录 | -- | 62 | | controller/*.go | 具体控制器 | 建议每个功能一个控制器文件 | 63 | | data | 数据目录 | -- | 64 | | data/log | 日志目录 | 建议路径,可在配置文件中指定 [log](https://github.com/go-baa/log) 输出路径 | 65 | | model | 数据模型目录 | -- | 66 | | model/base | 数据模型基类 | 提供对于数据库连接,缓存连接等基础操作 | 67 | | model/base.go | 模型基类 | 导入 `model/base` 初始化数据库,是其他模型的基础 | 68 | | model/article.go | 业务模型 | 具体的业务模型,建议每个表对应一个模型文件,命名和表名一致 | 69 | | module | 扩展功能模块 | -- | 70 | | module/util | 助手模块 | 一些常用的功能函数,文件操作,URL操作,加解密等 | 71 | | module/util/util.go | 助手函数 | 常用函数库 | 72 | | module/template | 模板扩展 | -- | 73 | | module/template/template.go | 模板函数库 | 结合 [render](https://github.com/go-baa/render) 模板引擎,可以扩展模板函数 | 74 | | public | 静态资源目录 | -- | 75 | | public/assets | 前端资源目录 | -- | 76 | | public/assets/css,images,js | 前端文件目录 | -- | 77 | | public/uplaod | 上传文件目录 | -- | 78 | | public/robots.txt | 静态文件 | 其他静态文件,可以放在资源目录下 | 79 | | router | 路由设定目录 | -- | 80 | | router/init.go | baa初始化 | 初始化baa,加载中间件,模板组件,缓存组件等 | 81 | | router/router.go | 路由配置 | 独立了路由配置在一个文件中,结构更清晰 | 82 | | template | 模板目录 | -- | 83 | | template/share | 共享目录 | 存储共享的模板片段 | 84 | | template/article | 业务模板 | 具体的业务模板,建议和控制一一对应,每个控制一个目录,每个方法一个文件 | 85 | | template/index.html | 首页模板 | 应用的首页文件 | 86 | | main.go | 应用入口 | -- | 87 | | README.md | 应用说明 | -- | 88 | 89 | 完整结构,参见示例 [blog](https://github.com/go-baa/example/tree/master/blog) 90 | 91 | ## 控制器 92 | 93 | 控制器中按业务划分成了不同的文件,不同的操作还应该有不同的方法对应,在实现上有两种考虑: 94 | 95 | - 一个控制器中所有方法都是函数,使用控制器的名字作为函数名前置防止多个控制中的命名冲突。 96 | - 将一个控制器视为一个类,所有方法都是类的方法,虽然Go中没有明确的类,但也可以实现面向对象编程。 97 | 98 | 两种声音都有支持,你可以根据自己喜欢来做,我们选择了第二种姿势,看起来更舒服一些。 99 | 100 | 最终,一个控制文件可能是这样的: 101 | 102 | ``` 103 | // api/controller/index.go 104 | 105 | package controller 106 | 107 | import ( 108 | "github.com/go-baa/example/api/model" 109 | "github.com/go-baa/log" 110 | "github.com/go-baa/baa" 111 | ) 112 | 113 | type index struct{} 114 | 115 | // IndexController ... 116 | var IndexController = index{} 117 | 118 | // Index list articles 119 | func (index) Index(c *baa.Context) { 120 | page := c.ParamInt("page") 121 | pagesize := 10 122 | 123 | rows, total, err := model.ArticleModel.Search(page, pagesize) 124 | if err != nil { 125 | output(c, 1, err.Error(), nil) 126 | return 127 | } 128 | 129 | log.Debugf("rows: %#v, total: %d\n", rows, total) 130 | 131 | output(c, 0, "", map[string]interface{}{ 132 | "total": total, 133 | "items": rows, 134 | }) 135 | } 136 | 137 | .... 138 | 139 | ``` 140 | 141 | > 该文件来自示例程序 [api](http://github.com/go-baa/example/tree/master/api) 142 | 143 | 为了实现面向对象,创建了一个空的结构体作为方法的承载,所有方法都注册给这个结构体。 144 | 145 | **需要解释的一句是,为什么还要声明一个 `IndexController` 呢?** 146 | 147 | 路由注册时需要将每一个URL对应到具体的方法上来,结构体的方法是不能直接用的,需要先声明一个结构体实例才能使用。 148 | 149 | 在哪儿声明呢?一个是路由注册的时候,一个是控制器定义的时候,我们选择了在控制器定义的时候声明,作为控制器开发的一个规范,路由定义时引入包就可以用了。 150 | 151 | ## 数据模型 152 | 153 | baa本身不提供数据模型的处理,在 [api](http://github.com/go-baa/example/tree/master/api) 示例中使用的是 [grom](http://jinzhu.me/gorm/) 来操作MySQL。 154 | 155 | [xorm](http://xorm.io/) 和 [grom](http://jinzhu.me/gorm/) 有什么区别呢?论功能 `xorm` 可能更强大一些,我们觉得 `grom` 使用更舒服一些。 156 | 157 | 虽然他们都做了很好的封装,但一个项目毕竟还要配置数据库信息,数据库连接,还要各种包调用,显然我们还是要做个简单的封装才好。 158 | 159 | 具体的代码不列出,请参考 [api/model](http://github.com/go-baa/example/tree/master/api/model) 中的base处理。 160 | 161 | 在这个基础上,一个数据模型可能长这个样子: 162 | 163 | ``` 164 | // api/model/user.go 165 | 166 | package model 167 | 168 | // User user data scheme 169 | type User struct { 170 | ID int `json:"id" gorm:"primary_key; type:int(10) UNSIGNED NOT NULL AUTO_INCREMENT;"` 171 | Name string `json:"name" gorm:"type:varchar(50) NOT NULL DEFAULT '';"` 172 | Email string `json:"email" gorm:"type:varchar(100) NOT NULL DEFAULT '';"` 173 | } 174 | 175 | type userModel struct{} 176 | 177 | // UserModel single model instance 178 | var UserModel = new(userModel) 179 | 180 | // Get find a user info 181 | func (t *userModel) Get(id int) (*User, error) { 182 | row := new(User) 183 | err := db.Where("id = ?", id).First(row).Error 184 | return row, err 185 | } 186 | 187 | // Create create a user 188 | func (t *userModel) Create(name, email string) (int, error) { 189 | row := new(User) 190 | row.Name = name 191 | row.Email = email 192 | err := db.Create(row).Error 193 | if err != nil { 194 | return 0, err 195 | } 196 | return row.ID, nil 197 | } 198 | ``` 199 | 200 | > 该文件来自示例程序 [api](http://github.com/go-baa/example/tree/master/api) 201 | 202 | 基本思想和控制器是一样的,先按照表结构声明一个结构体。然后创建一个空结构体将模型的方法进行封装,最后声明了一个 `UserModel`使得在控制器中无需声明就可以直接使用模型。 203 | 204 | **需要注意的是,在模型中每个方法的最后一个参数一定是`error`,表示操作是否出错,不要问为什么,规范,还是规范,这里讲的都是规范。** 205 | 206 | ## 配置文件 207 | 208 | 应用配置文件,只能是 `conf/app.ini`,这个由项目 [setting](https://github.com/go-baa/setting) 决定,为什么把路径写死了呢,为了省事,无论在哪儿引入包就能用,无需配置和传递。 209 | 210 | 更多的配置文件也建议放在 `conf` 目录中,自己去读取。 211 | 212 | 配置示例: 213 | 214 | ``` 215 | // conf/app.ini 216 | [default] 217 | # app 218 | app.name = baaBlog 219 | app.version = 0.1 220 | app.url = "" 221 | debug = false 222 | 223 | # http 224 | http.address = 0.0.0.0 225 | http.port = 80 226 | http.access_open = off 227 | 228 | # output log to os.Stderr 229 | log.file = os.Stderr 230 | # 0 off, 1 fatal, 2 panic, 5 error, 6 warn, 10 info, 11 debug 231 | log.level = 11 232 | 233 | # development mode overwrite default config 234 | [development] 235 | debug = true 236 | 237 | # production mode overwrite default config 238 | [production] 239 | debug = false 240 | ``` 241 | 242 | > 再次说明,这个配置文件依赖 [setting](https://github.com/go-baa/setting) 项目。 243 | 244 | ## 模板 245 | 246 | 模板最简单,按着结构放就好了。 247 | 248 | 模板的初始化和使用,参考: 249 | 250 | * [模板渲染](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#模板渲染) 251 | * [模板语法](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#模板语法) 252 | * [模板接口](https://github.com/go-baa/doc/tree/master/zh-CN/context.md#模板接口) 253 | * [render](https://github.com/go-baa/doc/tree/master/zh-CN/component/render.md) 254 | * [pongo2](https://github.com/go-baa/doc/tree/master/zh-CN/component/pongo2.md) 255 | 256 | 项目示例,参考: 257 | 258 | * [blog](https://github.com/go-baa/example/tree/master/blog) 259 | 260 | ## 静态资源 261 | 262 | 如果是一个API项目,可能没有静态资源,忽略就行。 263 | 264 | 一般的静态资源,放在那里就好了,然后注册静态资源目录: 265 | 266 | ``` 267 | // router/router.go 268 | 269 | app.Static("/assets", "public/assets", false, nil) 270 | app.StaticFile("/robots.txt", "public/robots.txt") 271 | ``` 272 | 273 | 如果你的项目采用了前端构建的姿势,那么你就构建吧,和baa也没什么关系,也不影响, 274 | 275 | 就是建议把构建后的资源放置到 `public`下面,比如:`public/assets/build` 然后注册静态目录,开发过程中的文件不建议放在 `public`下,因为是不可访问资源。 276 | 277 | ## 打包发布 278 | 279 | Go程序的一个好处就是,`go build`然后生成一个二进制文件,Copy到服务上就行了。 280 | 281 | 不过需要注意的是,按照以上介绍的姿势,你还要带上 `配置文件`,`模板`,`静态资源`,最后运行的目录应该是这样的: 282 | 283 | ``` 284 | project 285 | |-- conf 286 | |-- app.ini 287 | |-- public 288 | |-- assets 289 | |-- build 290 | |-- css 291 | |-- images 292 | |-- js 293 | |-- robots.txt 294 | |-- template 295 | |- share 296 | |-- article 297 | |-- show.html 298 | |-- index.html 299 | |-- project // 二进制文件 300 | ``` 301 | 302 | 至于你的发布姿势,是什么发布系统都没关系,要注意,打包的环境和运行的系统环境要一致,mac下编译出来的,linux可不一定能运行。 303 | 304 | > PS:我们发布时采用 gitlab + jenkins 构建 Docker镜像的方式。 305 | 306 | ### 运行 307 | 308 | **运行?** 309 | 310 | 就是 `./project` 就可以了。哦,别忘了设置环境变量: 311 | 312 | ``` 313 | BAA_ENV=production 314 | ``` 315 | 316 | **优雅重启?** 317 | 318 | Go1.8就要提供了,之前无论用什么方案都不可避免的要损失一些正在执行的连接。 319 | 320 | 我们目前采用的是多机部署,上线时,分批进行,保证服务有损但不下线。 321 | 322 | ### 依赖管理 323 | 324 | 依赖管理的工具有很多,我们目前使用的是 [godep](https://github.com/tools/godep),我们将产生的`Godeps`目录上传到了git中,确保构建时的环境一致。 325 | 326 | 327 | -------------------------------------------------------------------------------- /zh-CN/context.md: -------------------------------------------------------------------------------- 1 | # Baa Context 2 | 3 | Context是HTTP上下文的意思,封装了`输入`、`输出`,提供请求处理,结果响应,模板渲染等相关操作。 4 | 5 | ## Request 6 | 7 | `c.Req` 8 | 9 | Request 中包含了所有的请求数据,是标准包中 `http.Request` 的封装,可以通过 `c.Req` 访问原生的请求结构。 10 | 11 | ### URL参数 12 | 13 | `func (c *Context) Posts() map[string]interface{}` 14 | 15 | 获取所有的POST 和 GET 数据,返回一个字典,字典的索引为表单字段的名称,值为字段的值,值如果只有一个则为 `string` 类型,值有多个则为 `[]string` 类型。 16 | 17 | `func (c *Context) Querys() map[string]interface{}` 18 | 19 | 获取所有的 GET 数据,返回一个字典,字典的索引为表单字段的名称,值为字段的值,值如果只有一个则为 `string` 类型,值有多个则为 `[]string` 类型。 20 | 21 | `func (c *Context) Query(name string) string` 22 | 23 | 根据 `name` 获取一个字段的值,返回 `string` 类型,包含 POST 和 GET 数据。 24 | 25 | `func (c *Context) QueryStrings(name string) []string` 26 | 27 | 根据 `name` 获取一个字段的多个值,返回 `[]string` 类型。 28 | 29 | `func (c *Context) QueryEscape(name string) string` 30 | 31 | 根据 `name` 获取一个字段的值,escapre编码后返回 `string` 类型。 32 | 33 | `func (c *Context) QueryTrim(name string) string` 34 | 35 | 根据 `name` 获取一个字段的值,去除两端空白后返回 `string` 类型。 36 | 37 | `func (c *Context) QueryBool(name string) bool` 38 | 39 | 根据 `name` 获取一个字段的值,并强制转化为 `bool` 类型 返回。 40 | 41 | `func (c *Context) QueryFloat(name string) float64` 42 | 43 | 根据 `name` 获取一个字段的值,并强制转化为 `float64` 类型 返回。 44 | 45 | `func (c *Context) QueryInt(name string) int` 46 | 47 | 根据 `name` 获取一个字段的值,并强制转化为 `int` 类型 返回。 48 | 49 | `func (c *Context) QueryInt32(name string) int32` 50 | 51 | 根据 `name` 获取一个字段的值,并强制转化为 `int32` 类型 返回。 52 | 53 | `func (c *Context) QueryInt64(name string) int64` 54 | 55 | 根据 `name` 获取一个字段的值,并强制转化为 `int64` 类型 返回。 56 | 57 | ### 路由参数 58 | 59 | `func (c *Context) Param(name string) string` 60 | 61 | 根据 `name` 获取一个路由参数的值,返回 `string` 类型。 62 | 63 | `func (c *Context) Params() map[string]string` 64 | 65 | 返回所有的路由参数组成的字典,字典的索引是参数名称。 66 | 67 | `func (c *Context) ParamBool(name string) bool` 68 | 69 | 根据 `name` 获取一个路由参数的值,并强制转化为 `bool` 类型 返回。 70 | 71 | `func (c *Context) ParamFloat(name string) float64` 72 | 73 | 根据 `name` 获取一个路由参数的值,并强制转化为 `float64` 类型 返回。 74 | 75 | `func (c *Context) ParamInt(name string) int` 76 | 77 | 根据 `name` 获取一个路由参数的值,并强制转化为 `int` 类型 返回。 78 | 79 | `func (c *Context) ParamInt32(name string) int32` 80 | 81 | 根据 `name` 获取一个路由参数的值,并强制转化为 `int32` 类型 返回。 82 | 83 | `func (c *Context) ParamInt64(name string) int64` 84 | 85 | 根据 `name` 获取一个路由参数的值,并强制转化为 `int64` 类型 返回。 86 | 87 | ### Cookie 88 | 89 | `func (c *Context) GetCookie(name string) string` 90 | 91 | 根据 `name` 获取一个Cookie的值,返回 `string` 类型。 92 | 93 | `func (c *Context) GetCookieBool(name string) bool` 94 | 95 | 根据 `name` 获取一个Cookie的值,并强制转化为 `bool` 类型 返回。 96 | 97 | `func (c *Context) GetCookieFloat64(name string) float64` 98 | 99 | 根据 `name` 获取一个Cookie的值,并强制转化为 `float64` 类型 返回。 100 | 101 | `func (c *Context) GetCookieInt(name string) int` 102 | 103 | 根据 `name` 获取一个Cookie的值,并强制转化为 `int` 类型 返回。 104 | 105 | `func (c *Context) GetCookieInt32(name string) int32` 106 | 107 | 根据 `name` 获取一个Cookie的值,并强制转化为 `int32` 类型 返回。 108 | 109 | `func (c *Context) GetCookieInt64(name string) int64` 110 | 111 | 根据 `name` 获取一个Cookie的值,并强制转化为 `int64` 类型 返回。 112 | 113 | `func (c *Context) SetCookie(name string, value string, others ...interface{})` 114 | 115 | 设置 名称为 `name` 值为 `value` 的Cookie。 116 | 117 | `setCookie` 还可以指定更多Cookie参数,通过可变长度的 `others` 参数可以依次指定: 118 | 119 | ``` 120 | SetCookie(, , , , , , ) 121 | ``` 122 | 123 | 使用姿势: 124 | 125 | ``` 126 | c.SetCookie("mykey", "myvalue") 127 | c.SetCookie("mykey", "myvalue", 3600) 128 | c.SetCookie("mykey", "myvalue", 3600, "/") 129 | c.SetCookie("mykey", "myvalue", 3600, "/", ".vodjk.com") 130 | c.SetCookie("mykey", "myvalue", 3600, "/", ".vodjk.com", true) 131 | c.SetCookie("mykey", "myvalue", 3600, "/", ".vodjk.com", true, true) 132 | ``` 133 | 134 | > 可变参数需依次指定,不能跳过中间的参数 135 | 136 | ### 文件上传 137 | 138 | `func (c *Context) GetFile(name string) (multipart.File, *multipart.FileHeader, error)` 139 | 140 | 通过 `multipart/form-data` 表单上传的文件,可以通过 `c.GetFile` 指定文件字段获得到文件结构 141 | [multipart.File](https://godoc.org/mime/multipart#File) 和 [multipart.FileHeader](https://godoc.org/mime/multipart#FileHeader), 142 | 143 | 然后可以利用这些结构进行文件大小检测,文件类型检测,文件存储等。 144 | 145 | 举个栗子: 146 | 147 | ``` 148 | func Upload(c *baa.Context) { 149 | file, header, err := c.GetFile("file1") 150 | if err != nil { 151 | c.Error(errors.New("没有文件被上传")) 152 | return 153 | } 154 | defer file.Close() 155 | 156 | savedTo := "savedFile.jpg" 157 | newFile, err := os.Create(savedTo) 158 | if err != nil { 159 | c.Error(errors.New("文件创建失败")) 160 | return 161 | } 162 | defer newFile.Close() 163 | 164 | size, err := io.Copy(newFile, file) 165 | msg := fmt.Sprintf("fileName: %s, savedTo: %s, size: %d, err: %v", header.Filename, savedTo, size, err) 166 | fmt.Println(msg) 167 | 168 | c.String(200, msg) 169 | } 170 | 171 | ``` 172 | 173 | `func (c *Context) SaveToFile(name, savePath string) error` 174 | 175 | 快速保存指定的文件字段 `name` 到指定的路径 `savePath` 并返回 `nil` 或者 `error` 176 | 177 | 还是上面的例子,改一下: 178 | 179 | ``` 180 | func Upload2(c *baa.Context) { 181 | savedTo := "savedFile2.jpg" 182 | err := c.SaveToFile("file1", savedTo) 183 | if err != nil { 184 | c.Error(err) 185 | return 186 | } 187 | c.String(200, savedTo) 188 | } 189 | ``` 190 | 191 | ## Response 192 | 193 | `c.Resp` 194 | 195 | Response 中用于处理结果输出,是标准包中 `http.ResponseWriter` 的封装,可以通过 `c.Resp` 访问原生的接口。 196 | 197 | ### 数据存储 198 | 199 | `Context` 提供了临时存储可用于整个请求的生命周期中。 200 | 201 | `func (c *Context) Set(key string, v interface{})` 202 | 203 | 根据 `key` 设置一个值 `v` 到 `Context` 存储中。 204 | 205 | `func (c *Context) Get(key string) interface{}` 206 | 207 | 根据 `key` 从`Context`存储中获取一个值并返回。 208 | 209 | `func (c *Context) Gets() map[string]interface{}` 210 | 211 | 获取`Context`中存储的所有值,返回由这些值组成的字典。 212 | 213 | 举个例子: 214 | 215 | ``` 216 | package main 217 | 218 | import ( 219 | "github.com/go-baa/baa" 220 | ) 221 | 222 | func main() { 223 | app := baa.New() 224 | app.Get("/", func(c *baa.Context) { 225 | c.Set("mykey", "myvalue") 226 | }, func(c *baa.Context) { 227 | val := c.Get("mykey") 228 | fmt.Println(val) // myvalue 229 | 230 | c.String(200, val.(string)) 231 | }) 232 | 233 | app.Run(":1323") 234 | } 235 | ``` 236 | 237 | ### 内容输出 238 | 239 | baa 提供了多种形式的内容输出。 240 | 241 | `func (c *Context) String(code int, s string)` 242 | 243 | 设定输出的 http code 为 `code`,并输出一个字符串 `s`。 244 | 245 | `func (c *Context) Text(code int, s []byte)` 246 | 247 | 设定输出的 http code 为 `code`,并输出一个字节切片 `s`。 248 | 249 | `func (c *Context) JSON(code int, v interface{})` 250 | 251 | 设定输出的 http code 为 `code`,设定内容类型为 `application/json`, 把 结构 `v` 使用JSON编码后输出。 252 | 253 | `func (c *Context) JSONP(code int, callback string, v interface{})` 254 | 255 | 设定输出的 http code 为 `code`,设定内容类型为 `application/json`, 把 结构 `v` 使用JSON编码,并结合 `callback`参数输出。 256 | 257 | `func (c *Context) JSONString(v interface{}) (string, error)` 258 | 259 | 把 结构 `v` 使用JSON编码后返回。 260 | 261 | `func (c *Context) XML(code int, v interface{})` 262 | 263 | 设定输出的 http code 为 `code`,设定内容类型为 `application/json`, 把 结构 `v` 使用XML编码后输出。 264 | 265 | ## 有用的函数 266 | 267 | `func (c *Context) Baa() *Baa` 268 | 269 | `func (c *Context) Body() *RequestBody` 270 | `func (c *Context) Break()` 271 | `func (c *Context) Error(err error)` 272 | 273 | `func (c *Context) Next()` 274 | `func (c *Context) NotFound()` 275 | `func (c *Context) IsAJAX() bool` 276 | `func (c *Context) IsMobile() bool` 277 | 278 | `func (c *Context) Redirect(code int, url string) error` 279 | `func (c *Context) Referer() string` 280 | `func (c *Context) RemoteAddr() string` 281 | `func (c *Context) URL(hasQuery bool) string` 282 | `func (c *Context) UserAgent() string` 283 | 284 | ## 模板渲染 285 | 286 | baa 集成一个简单的模板渲染,使用Go标准库的 [template语法](https://godoc.org/html/template)。 287 | 288 | baa 的模板渲染使用 `Context`存储 中的数据作为模板变量。 289 | 290 | `func (c *Context) Fetch(tpl string) ([]byte, error)` 291 | 292 | 根据 `tpl` 模板文件路径,使用 `Context`存储中的数据,渲染模板并返回渲染后的内容。 293 | 294 | `func (c *Context) Render(code int, tpl string)` 295 | 296 | 设定输出的 http code 为 `code`,设定内容类型为 `text/html`, 渲染模板 `tpl` 并直接输出。 297 | 298 | > 内部就是调用的 `c.Fetch` 然后 把内容输出 299 | 300 | `func (c *Context) HTML(code int, tpl string)` 301 | 302 | `c.Render`的一个别名,用起来更顺手。 303 | 304 | 举个例子: 305 | 306 | ``` 307 | package main 308 | 309 | import baa "github.com/go-baa/baa" 310 | 311 | func main() { 312 | app := baa.New() 313 | app.Get("/", func(c *baa.Context) { 314 | c.Set("title", "this is title") 315 | c.Set("content", "this is content") 316 | c.HTML(200, "template/index.html") 317 | }) 318 | app.Run(":1323") 319 | } 320 | 321 | ``` 322 | 323 | ``` 324 | 325 | 326 | 327 | 328 | 329 | {{ .title }} 330 | 331 | 332 | 333 | {{ .content}} 334 | 335 | 336 | 337 | ``` 338 | 339 | ### 模板语法 340 | 341 | 以下仅做简单介绍,完整文档请见官方 [html/template](https://godoc.org/html/template)。 342 | 343 | * 模板语法以一对 `双大括号` 包裹 344 | * 变量都是以 `.` 开始,`.` 代表所有数据组成的结构 345 | * 结构体中的字段用 `.` 表示子集 346 | 347 | #### 输出变量 348 | 349 | ``` 350 | {{ .var }} 351 | {{ .user.id }} 352 | ``` 353 | 354 | #### 条件语句 355 | 356 | ``` 357 | {{ if .show }} 358 | i want show! 359 | {{ else }} 360 | i was hidden 361 | {{ end }} 362 | ``` 363 | 364 | #### 循环语句 365 | 366 | ``` 367 | {{ range .list }} 368 | {{ .id }} 369 | {{ .name }} 370 | {{ end }} 371 | ``` 372 | 373 | 如果循环体不是结构体,比如就是一个字符串数组,直接用 `.` 即可输出: 374 | 375 | ``` 376 | {{ range .strs }} 377 | {{ . }} 378 | {{ end }} 379 | ``` 380 | 381 | ### 模板接口 382 | 383 | baa 中运行通过 `DI` 替换模板引擎,只要实现 `baa.Renderer` 接口即可。 384 | 385 | Renderer 386 | 387 | ``` 388 | type Renderer interface { 389 | Render(w io.Writer, tpl string, data interface{}) error 390 | } 391 | ``` 392 | 393 | 渲染接口只有一个方法 `Render`,该方法 接收三个参数: 394 | 395 | #### w `io.Writer` 396 | 397 | 一个可写入的类型,用于写入渲染后的数据,这里其实就是 `c.Resp` 。 398 | 399 | #### tpl `string` 400 | 401 | 模板文件路径 402 | 403 | #### data `interface{}` 404 | 405 | 向模板传递的数据(模板变量),这里其实传递过来的就是 `c.Gets` 的结果,是一个 `map[string]interface{}` 类型。 406 | -------------------------------------------------------------------------------- /zh-CN/router.md: -------------------------------------------------------------------------------- 1 | # Baa 路由 2 | 3 | baa 基于 http resetfull 模式设计了路由管理器,提供了常规路由,参数路由,文件路由,静态文件路由,还有组路由。 4 | 5 | ## 常规路由 6 | 7 | ``` 8 | func (b *Baa) Delete(pattern string, h ...HandlerFunc) RouteNode 9 | func (b *Baa) Get(pattern string, h ...HandlerFunc) RouteNode 10 | func (b *Baa) Head(pattern string, h ...HandlerFunc) RouteNode 11 | func (b *Baa) Options(pattern string, h ...HandlerFunc) RouteNode 12 | func (b *Baa) Patch(pattern string, h ...HandlerFunc) RouteNode 13 | func (b *Baa) Post(pattern string, h ...HandlerFunc) RouteNode 14 | func (b *Baa) Put(pattern string, h ...HandlerFunc) RouteNode 15 | ``` 16 | 17 | 接受两个参数,一个是URI路径,另一个是 [HandlerFunc](https://godoc.org/github.com/go-baa/baa#HandlerFunc) 类型,设定匹配到该路径时执行的方法;允许多个,按照设定顺序进行链式处理。 18 | 19 | 返回一个 [RouteNode](https://godoc.org/github.com/go-baa/baa#RouteNode),该Node只有一个方法,`Name(name string)` 用于命名该条路由规则,以备后用。 20 | 21 | 除了以上几个标准方法,还支持多个method设定的路由姿势: 22 | 23 | ``` 24 | func (b *Baa) Route(pattern, methods string, h ...HandlerFunc) RouteNode 25 | func (b *Baa) Any(pattern string, h ...HandlerFunc) RouteNode 26 | ``` 27 | 28 | ## 路由语法 29 | 30 | ### 静态路由 31 | 32 | 静态路由语法就是没有任何参数变量,pattern是一个固定的字符串。 33 | 34 | 使用示例: 35 | 36 | ``` 37 | package main 38 | 39 | import ( 40 | "github.com/go-baa/baa" 41 | ) 42 | 43 | func main() { 44 | app := baa.New() 45 | app.Get("/foo", func(c *baa.Context) { 46 | c.String(200, c.URL(true)) 47 | }) 48 | app.Get("/bar", func(c *baa.Context) { 49 | c.String(200, c.URL(true)) 50 | }) 51 | app.Run("1323") 52 | } 53 | ``` 54 | 55 | 测试: 56 | 57 | ``` 58 | curl http://127.0.0.1:1323/foo 59 | curl http://127.0.0.1:1323/bar 60 | ``` 61 | 62 | ### 参数路由 63 | 64 | 静态路由是最基础的,但显然满足不了需求的,我们在程序中通常相同的资源访问规则相同,不同的只是资源的编号,这时就该参数路由出场了。 65 | 66 | > 参数路由以 `/` 为拆分单位,故每两个斜线区间中只能有一个参数存在,更复杂的规则需要 正则路由。 67 | 68 | 参数路由以冒号 `:` 后面跟一个字符串作为参数名称,可以通过 `Context`的 `Param` 系列方法获取路由参数的值。 69 | 70 | 使用示例: 71 | 72 | ``` 73 | package main 74 | 75 | import ( 76 | "fmt" 77 | "github.com/go-baa/baa" 78 | ) 79 | 80 | func main() { 81 | app := baa.New() 82 | app.Get("/user/:id", func(c *baa.Context) { 83 | c.String(200, "My user id is: " + c.Param("id")) 84 | }) 85 | app.Get("/user/:id/project/:pid", func(c *baa.Context) { 86 | id := c.ParamInt("id") 87 | pid := c.ParamInt("pid") 88 | c.String(200, fmt.Sprintf("user id: %d, project id: %d", id, pid)) 89 | }) 90 | app.Run("1323") 91 | } 92 | ``` 93 | 94 | 测试: 95 | 96 | ``` 97 | curl http://127.0.0.1:1323/user/101 98 | curl http://127.0.0.1:1323/user/101/project/201 99 | ``` 100 | 101 | ### 正则路由 102 | 103 | `正则路由,默认的baa中不支持正则表达式路由,需要一个增强组件来支持。` 104 | 105 | 语法和参数路由接近,并兼容参数路由,可以直接使用 正则路由替换默认路由。 106 | 107 | 参数路由以冒号 `:` 后面跟一个字符串作为参数名称,再加一对括号中间可以写正则;如果省略括号默认为 `.*` 的正则匹配。 108 | 109 | > 使用正则路由要先引入新的路由器,并通过DI替换掉内置路由。 110 | 111 | 使用示例: 112 | 113 | ``` 114 | package main 115 | 116 | import ( 117 | "fmt" 118 | "github.com/go-baa/baa" 119 | "github.com/go-baa/router/regtree" 120 | ) 121 | 122 | func main() { 123 | app := baa.New() 124 | app.SetDI("router", regtree.New(app)) 125 | 126 | app.Get("/user/:id", func(c *baa.Context) { 127 | c.String(200, "My user id is: " + c.Param("id")) 128 | }) 129 | app.Get("/user/:id/project/:pid", func(c *baa.Context) { 130 | id := c.ParamInt("id") 131 | pid := c.ParamInt("pid") 132 | c.String(200, fmt.Sprintf("user id: %d, project id: %d", id, pid)) 133 | }) 134 | 135 | app.Get("/user-:id(\\d+)", func(c *baa.Context) { 136 | c.String(200, "My user id is: "+c.Param("id")) 137 | }) 138 | app.Get("/user-:id(\\d+)-project-:pid(\\d+)", func(c *baa.Context) { 139 | id := c.ParamInt("id") 140 | pid := c.ParamInt("pid") 141 | c.String(200, fmt.Sprintf("user id: %d, project id: %d", id, pid)) 142 | }) 143 | 144 | app.Run("1323") 145 | } 146 | ``` 147 | 148 | 测试: 149 | 150 | ``` 151 | curl http://127.0.0.1:1323/user/101 152 | curl http://127.0.0.1:1323/user-101 153 | curl http://127.0.0.1:1323/user/101/project/201 154 | curl http://127.0.0.1:1323/user-101-project-201 155 | ``` 156 | 157 | ## 路由选项 158 | 159 | ``` 160 | func (b *Baa) SetAutoHead(v bool) 161 | ``` 162 | 163 | 我记得搜索引擎很喜欢用HEAD方法来检查一个网页是否能正常访问。但我们一般又不会单独写一个HEAD的处理方法,一般行为是GET返回的数据不要内容。 164 | 165 | 使用 `app.SetAutoHead(true)` 将在设置 `GET` 方法时,自动添加 `HEAD` 路由,绑定和GET一样的处理。 166 | 167 | ``` 168 | func (b *Baa) SetAutoTrailingSlash(v bool) 169 | ``` 170 | 171 | 在URL访问中,一个目录要带不带最后的斜线也有很多争议,google站长工具明确表示,带不带斜线将表示不同的URL资源,但是浏览习惯问题,很多时候带不带都能访问到相同的资源目录。 172 | 173 | 使用 `app.SetAutoTrailingSlash(true)` 将处理最后的斜线,将带和不带都统一行为,自动补全最后一个斜线。 174 | 175 | ## 组路由 176 | 177 | ``` 178 | func (b *Baa) Group(pattern string, f func(), h ...HandlerFunc) 179 | ``` 180 | 181 | 组路由,常常被同事问道,这个功能太好用了,你是怎么想到这样的设计,我说,我抄的,我抄的 [macaron](https://github.com/go-macaron/macaron),就是这么`直白`。 182 | 183 | 组路由,顾名思义,用来处理一组路由的需求,可以设定统一的前缀,统一的前置方法。 184 | 185 | 使用示例: 186 | 187 | ``` 188 | package main 189 | 190 | import ( 191 | "fmt" 192 | "github.com/go-baa/baa" 193 | ) 194 | 195 | func main() { 196 | app := baa.New() 197 | 198 | app.Group("/group", func() { 199 | app.Get("/", func(c *baa.Context) { 200 | c.String(200, "我是组的首页") 201 | }) 202 | app.Group("/user", func() { 203 | app.Get("/", func(c *baa.Context) { 204 | c.String(200, "我是组的用户") 205 | }) 206 | app.Get("/:id", func(c *baa.Context) { 207 | c.String(200, "in group, user id: "+c.Param("id")) 208 | }) 209 | }) 210 | app.Get("/:gid", func(c *baa.Context) { 211 | c.String(200, "in group, group id: "+c.Param("gid")) 212 | }) 213 | }, func(c *baa.Context) { 214 | // 我是组内的前置检测,过不了我这关休想访问组内的资源 215 | }) 216 | 217 | app.Run("1323") 218 | } 219 | ``` 220 | 221 | 测试: 222 | 223 | ``` 224 | curl http://127.0.0.1:1323/group/ 225 | curl http://127.0.0.1:1323/group/user/ 226 | curl http://127.0.0.1:1323/group/user/101 227 | curl http://127.0.0.1:1323/group/111 228 | ``` 229 | 230 | ### 链式处理 231 | 232 | 一个URL请求可以先处理A,根据A的结果再执行B。 233 | 234 | **举个例子:** 235 | 236 | 一个URL要先判断你登录过才可以访问,就可以设定两个Handler,第一个 判断是否登录,如果没登录就调到登录界面,否则继续执行第二个真正的内容。 237 | 238 | 使用示例: 239 | 240 | ``` 241 | package main 242 | 243 | import ( 244 | "github.com/go-baa/baa" 245 | ) 246 | 247 | func main() { 248 | app := baa.Default() 249 | app.Get("/", func(c *baa.Context) { 250 | c.String(200, "Hello, 世界") 251 | }) 252 | app.Post("/", func(c *baa.Context) { 253 | c.String(200, c.Req.Method) 254 | }) 255 | app.Get("/admin", func(c *baa.Context) { 256 | if c.GetCookie("login_id") != "admin" { 257 | c.Redirect(302, "/login") 258 | c.Break() 259 | } 260 | }, func(c *baa.Context) { 261 | c.String(200, "恭喜你,看到后台了") 262 | }) 263 | app.Get("/login", func(c *baa.Context) { 264 | c.Resp.Header().Set("Content-Type", "text/html; charset=utf-8") 265 | c.SetCookie("login_id", "admin", 3600, "/") 266 | c.Resp.Write([]byte("登录成功,点击进入后台")) 267 | }) 268 | app.Run(":1323") 269 | } 270 | ``` 271 | 272 | ## 命名路由 273 | 274 | ``` 275 | func (n *Node) Name(name string) 276 | func (b *Baa) URLFor(name string, args ...interface{}) string 277 | ``` 278 | 279 | 前面可以看到添加路由后,返回了一个 `RouteNode` 说可以做命名路由,有什么用呢? 280 | 281 | 就是给一个URL起个名字,然后在程序中可以通过 `URLFor`方法来生成这个符合这个路由的URL路径。 282 | 283 | 举个栗子: 284 | 285 | ``` 286 | app := baa.New() 287 | app.Get("/user/:id/project", func(c *baa.Context) { 288 | c.String(200, c.Baa().URLFor("user_project", c.Param("id"))) 289 | }).Name("user_project") 290 | ``` 291 | 292 | 执行上面的方法,会输出你当前访问的URL,就是这个姿势。 293 | 294 | ## 文件路由 295 | 296 | ``` 297 | func (b *Baa) Static(prefix string, dir string, index bool, h HandlerFunc) 298 | func (b *Baa) StaticFile(pattern string, path string) RouteNode 299 | ``` 300 | 301 | 在一个完整的应用中,我们除了业务逻辑,还有访问图片/CSS/JS等需求,通过文件路由,可以直接访问文件或文件夹。 302 | 303 | `app.StaticFile` 可以让你直接访问一个具体的文件,比如: robots.txt 304 | 305 | `app.Static` 可以访问一个目录下所有的资源,甚至列出目录结构,类似文件服务器。 306 | 307 | 举个例子: 308 | 309 | ``` 310 | app := baa.New() 311 | app.Static("/assets", "/data/www/public/asssets", true, func(c *baa.Context) { 312 | // 你可以对输出的结果干点啥的 313 | }) 314 | app.Static("/robots.txt", "/data/www/public/robots.txt") 315 | ``` 316 | 317 | 就是酱样子,第一条路由就可以列出目录和访问下面的资源了。第二条路由可以直接返回一个静态文件。 318 | 319 | ## 自定义错误 320 | 321 | ### 500错误 322 | 323 | ``` 324 | func (b *Baa) SetError(h ErrorHandleFunc) 325 | ``` 326 | 327 | 要是运行过程中程序出错了,怎么办,会不会泄露你的隐私,能不能提供点错误日志? 328 | 329 | baa 默认在 `debug` 模式下向浏览器发送具体的错误信息,线上运行只显示 `Internal Server Error` 并返回 `500` 错误头。 330 | 331 | 可以通过 `app.SetError` 来设置错误处理方法,该方法接受一个 [ErrorHandleFunc](https://godoc.org/github.com/go-baa/baa#ErrorHandleFunc) 类型。 332 | 333 | ### 404错误 334 | 335 | ``` 336 | func (b *Baa) SetNotFound(h HandlerFunc) 337 | ``` 338 | 339 | baa默认返回 `Not Found` 和 `404` 错误头,你也可以通过 `app.SetNotFound`来自定义错误处理,该方法接受一个 [HandlerFunc](https://godoc.org/github.com/go-baa/baa#HandlerFunc) 类型。 340 | 341 | 342 | 举个栗子: 343 | 344 | ``` 345 | app := baa.New() 346 | app.SetError(func(err error, c *baa.Context) { 347 | c.Baa().Logger().Println("记录日志", err) 348 | c.String(500, "出错了") 349 | }) 350 | app.SetNotFound(func(c *baa.Context) { 351 | c.String(404, "页面放假了,请稍后再来。") 352 | }) 353 | app.Run(":1323") 354 | ``` 355 | 356 | ## Websocket 357 | 358 | ``` 359 | func (b *Baa) Websocket(pattern string, h func(*websocket.Conn)) RouteNode 360 | ``` 361 | 362 | Websocket 用于和浏览器进行保持通话。 363 | 364 | 在这里我们尝试了 官方的 `golang.org/x/net/websocket` 不好封装,放弃了。 365 | 366 | 官方推荐了 `github.com/gorilla/websocket` 我们试了下,不错哦,就用他了。 367 | 368 | baa 的websocket路由,用于快速开始一个 websocket 服务,混合现有应用编程。 369 | 370 | 该方法有两个参数,一个 `pattern` 路径,一个 [*websocket.Conn]() 类型的链接。 371 | 372 | 举个例子: 373 | 374 | ``` 375 | package main 376 | 377 | import ( 378 | "fmt" 379 | "time" 380 | 381 | "github.com/go-baa/baa" 382 | "github.com/gorilla/websocket" 383 | ) 384 | 385 | func main() { 386 | app := baa.Default() 387 | 388 | app.Get("/", func(c *baa.Context) { 389 | c.String(200, "index") 390 | }) 391 | app.Websocket("/socket", func(ws *websocket.Conn) { 392 | for { 393 | fmt.Println("websocket retry read...") 394 | messageType, data, err := ws.ReadMessage() 395 | if err != nil { 396 | if websocket.IsCloseError(err) { 397 | app.Logger().Println("websocket ReadMessage error: connection is closed") 398 | } else { 399 | app.Logger().Println("websocket ReadMessage error:", err) 400 | } 401 | ws.Close() 402 | return 403 | } 404 | fmt.Println("websocket receive: ", messageType, string(data)) 405 | err = ws.WriteMessage(messageType, data) 406 | if err != nil { 407 | app.Logger().Println("websocket WriteMessage error:", err) 408 | ws.Close() 409 | return 410 | } 411 | } 412 | }) 413 | 414 | app.Run(":1234") 415 | 416 | fmt.Println("end") 417 | } 418 | ``` 419 | 420 | 含js和go代码的完整示例:[example/websocket](https://github.com/go-baa/example/tree/master/websocket) 421 | 422 | websocket的具体使用请参考 [gorilla/websocket](http://godoc.org/github.com/gorilla/websocket) 423 | 424 | 425 | --------------------------------------------------------------------------------