Revel 框架手册介绍。
Revel 从Rails 和 Play! 中吸收了许多成熟的设计思想, 许多相同的思想被用到了框架的设计和接口中。
Revel 通过简单的约定来支持 MVC 设计模式,轻量、开发效率高。
一些不错的MVC结构概述,像 Play! 框架 与Revel框架完全匹配。
下面是一个请求处理的基本流程:
概要:
Revel 构建自顶级 Go HTTP server, 他为每一个到来的请求创建一个 go-routine (轻量线程),用于处理并发。
Revel什么也不做,只是把请求交给过滤器链处理,完成之后,将结果写到响应中。
默认情况下, Revel 处理程序注册到 "/"
接受所有的请求连接。然而, 应用程序可以自由的重写此行为 – 例如, 可以使用现有的 http.Handlers 而不是Revel来重新实现此功能。具体请参考 FAQ。
过滤器 实现了Revel的大部分请求处理功能,过滤器有一个简单的易于嵌套的接口。
“过滤器链” 是一个函数数组, 每一个都会去执行下一个,直到最后一个过滤器执行了控制器方法。例如, 过滤器链中的第一个过滤器是 RouterFilter
, 它决定哪个操作接受请求并保存到控制器。
总之, 过滤器和过滤器链就像机架一样。
每一个 HTTP 请求,执行一个 action, 处理请求并写入响应。 相关的 actions 被分组到 controllers中. Controller 类型包含相关字段和方法,作为每个请求的上下文。
作为 HTTP 请求处理的一部分,Revel 实例化你的控制器,并设置嵌入revel.Controller
的所有的属性。 Revel 不在请求之间共享实例。
Controller 是直接 或 间接嵌入 *revel.Controller
的一个struct。
type AppController struct { *revel.Controller}
Action 是 Controller 的方法。符合下面的条件:
例如:
func (c AppController) ShowLogin(username string) revel.Result { .. return c.Render(username)}
程序调用 revel.Controller.Render
渲染一个模板, 传递给模板一个username参数。revel.Controller 有许多方法处理revel.Result, 程序也可以自己创建处理方法。
Result 符合下面的接口:
type Result interface { Apply(req *Request, resp *Response)}
通常, 什么也不响应,直到 action 和所有的过滤器返回。此时,Revel写入响应的headers和cookies。(例如设置会话cookie), 然后调用 Result.Apply
写入实际响应内容。
按照go 命令行工具的要求将Revel和Revel应用程序安装到 GOPATH。 (参考 “GOPATH 环境变量” go 命令 文档)
gocode GOPATH 目录 src GOPATH src 目录 revel Revel 安装目录 ... sample Revel应用程序根目录 app MVC目录 controllers 控制器 init.go models 模型 routes views 模板 tests 测试 conf app.conf 默认配置 routes 路由定义 messages 国际化 public 静态文件 css CSS files js Javascript files images Image files
app
存放源代码和模板。
app/controllers
app/models
app/views
Revel 约定:
app/views
目录app/controllers
目录此外,Revel监视 app/
目录,当发现文件变动时,会自动重新编译。app/
目录以外的依赖关系不会被监视,在必要的时候由开发人员重新执行编译。
Revel在app目录的init()
函数开始的时候会导入 app/
中的所有依赖包或者 ( 模块)。
controllers/init.go
用于注册拦截器 interceptor。同一个包的源文件中init()
函数的执行无序的, 所以收集所有的拦截器定义到同一个文件中,便于开发者指定拦截器的执行顺序(也可以用于顺序敏感的初始化)。
conf
目录包含了Revel应用程序的配置文件,有两个主要的配置:
app.conf
, 主配置文件,包含了标准配置参数。routes
, 路由定义文件。messages
目录包含了本地化消息文件。
静态资源文件存放到 public
目录,由Web server 直接提供静态文件支持。 通常包含三个标准的子目录 images, CSS 和 JavaScript。
目录的名字可以随意,使用的时候只需要与路由对应起来就好。
Revel 使用 Go 模板, 在下面两个目录中查找模板:
views
目录 (包括所有的子目录)templates
目录.比如有一个控制器 Hello
,方法名为 World
, Revel 会查找名字为 views/Hello/World.html
的模板。模板名字不区分大小写,所以 views/hello/world.html
与 views/HeLlO/wOrLd.HtMl
都是匹配的模板.
Revel 提供了错误页面模板 (在开发模式中友好的显示编译错误), 开发者也可以重写这些模板,比如app/views/errors/500.html
.
Revel 使用 RenderArgs map 渲染模板。除了开发者传送的数据, Revel 也提供一些有用的数据:
Validation.ErrorMap
Go 提供了一些 模板函数。Revel 也增加了一些模板函数。请阅读下面的文档 或 查看源代码.
一个简单的 “a == b” 测试.
例如:
<div class="message {{if eq .User "you"}}you{{end}}">
在当前模板上下文中设置一个变量
例如:
{{set . "title" "Basic Chat room"}}<h1>{{.title}}</h1>
添加变量到一个数组中, 或者在模板上下文中创建一个数组
例如:
{{append . "moreScripts" "js/jquery-ui-1.7.2.custom.min.js"}}{{range .moreStyles}} <link rel="stylesheet" type="text/css" href="/public/{{.}}">{{end}}
input 字段辅助函数.
给出一个字段名, 函数会生成包含下面成员的结构:
例如:
{{with $field := field "booking.CheckInDate" .}} <p class="{{$field.ErrorClass}}"> <strong>Check In Date:</strong> <input type="text" size="10" name="{{$field.Name}}" class="datepicker" value="{{$field.Flash}}"> * <span class="error">{{$field.Error}}</span> </p>{{end}}
使用辅助函数生成 HTML option
字段。
例如:
{{with $field := field "booking.Beds" .}}<select name="{{$field.Name}}"> {{option $field "1" "One king-size bed"}} {{option $field "2" "Two double beds"}} {{option $field "3" "Three beds"}}</select>{{end}}
使用辅助函数生成 HTML radio input
字段
例如:
{{with $field := field "booking.Smoking" .}} {{radio $field "true"}} Smoking {{radio $field "false"}} Non smoking{{end}}
将换行符转换成 HTML 的 break.
例如:
You said:<div class="comment">{{nl2br .commentText}}</div>
一个辅助的复数函数
例如:
There are {{.numComments}} comment{{pluralize (len comments) "" "s"}}
输出原生的、未转义的文本
例如:
<div class="body">{{raw .blogBody}}</div>
Go 模板允许你在模板中包含其他模板,比如:
{{template "header.html" .}}
注意: * 相对路径是 app/views
Revel 应用程序有效利用 Go 模板,请看看下面的例子:
revel/samples/booking/app/views/header.html
revel/samples/booking/app/views/Hotels/Book.html
使用辅助函数,为模板设置标题和额外的样式。
例如:
<html> <head> <title>{{.title}}</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" media="screen" href="/public/css/main.css"> <link rel="shortcut icon" type="image/png" href="/public/img/favicon.png"> {{range .moreStyles}} <link rel="stylesheet" type="text/css" href="/public/{{.}}"> {{end}} <script src="/attachments/image/cimg/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script> <script src="/attachments/image/cimg/script> {{range .moreScripts}} <script src="/attachments/image/cimg/{{.}}" type="text/javascript" charset="utf-8"></script> {{end}} </head>
在模板中这样使用:
{{set . title "Hotels"}}{{append . "moreStyles" "ui-lightness/jquery-ui-1.7.2.custom.css"}}{{append . "moreScripts" "js/jquery-ui-1.7.2.custom.min.js"}}{{template "header.html" .}}
应用程序可以注册自定义模板函数
例如:
func init() { revel.TemplateFuncs["eq"] = func(a, b interface{}) bool { return a == b }}
“拦截器”是框架执行一个方法之前或之后被调用的函数。它允许 面向方面编程, 作用如下:
在 Revel 中, 有两种形式的拦截器:
函数拦截器:请参考 InterceptorFunc
接口.
方法拦截器:一个不带参数、并返回一个 revel.Result
的控制器方法
拦截器的执行顺序与添加位置相关
在一个请求生命周期内,可以注册四种拦截时间:
拦截器通常返回 nil
, 在这种情况下,需要继续处理请求,不能中断。
返回一个非 nil
revel.Result
的效果, 取决于拦截器被调用的时间:
在任何情况下,返回的结果都将附加到任何现有的结果上:
BEFORE:返回的结果是保证是最终的。
AFTER:它可能是一个进一步的拦截器,可以返回自己的结果。
下面是定义和注册函数拦截器的一个简单例子。
func checkUser(c *revel.Controller) revel.Result { if user := connected(c); user == nil { c.Flash.Error("请先登录") return c.Redirect(App.Index) } return nil}func init() { revel.InterceptFunc(checkUser, revel.BEFORE, &Hotels{})}
方法拦截器有两种方式的签名:
func (c AppController) example() revel.Resultfunc (c *AppController) example() revel.Result
下面是个同样的例子,只能拦截一个控制器。
func (c Hotels) checkUser() revel.Result { if user := connected(c); user == nil { c.Flash.Error("请先登录") return c.Redirect(App.Index) } return nil}func init() { revel.InterceptMethod(Hotels.checkUser, revel.BEFORE)}
过滤器是Revel框架的中间件 – 是组成请求处理管道的独立的功能。他们执行框架的所有功能。
过滤器类型是一个简单的函数:
type Filter func(c *Controller, filterChain []Filter)
每个过滤器负责调用过滤器链中的下一个过滤器。下面是个默认的过滤器栈:
// Filters 是默认的全局过滤器集。// 可以在程序初始化时设置它。var Filters = []Filter{ PanicFilter, // 从恐慌中恢复,并显示一个错误页面。 RouterFilter, // 负责解析路由,并选择正确的控制器方法。 FilterConfiguringFilter, // 用于添加/删除每个动作过滤的钩子。 ParamsFilter, // 解析参数到 Controller.Params 中。 SessionFilter, // 恢复和写入会话 cookie。 FlashFilter, // 恢复和写入 flash cookie。 ValidationFilter, // 恢复保存验证错误并保存新的Cookie中。 I18nFilter, // 解析请求语言。 InterceptorFilter, // 执行拦截器。 ActionInvoker, // 调用控制器。}
程序可以在 init()
中重写 revel.Filters
变量,来配置过滤器链 (默认在 app/init.go
)。
func init() { // Filters 是默认的全局过滤器集。 revel.Filters = []Filter{ PanicFilter, // 从恐慌中恢复,并显示一个错误页面。 RouterFilter, // 负责解析路由,并选择正确的控制器方法。 FilterConfiguringFilter, // 用于添加/删除每个动作过滤的钩子。 ParamsFilter, // 解析参数到 Controller.Params 中。 SessionFilter, // 恢复和写入会话 cookie。 FlashFilter, // 恢复和写入 flash cookie。 ValidationFilter, // 恢复保存验证错误并保存新的Cookie中。 I18nFilter, // 解析请求语言。 InterceptorFilter, // 执行拦截器。 ActionInvoker, // 调用控制器。 }}
每个请求沿着过滤器链从上到下依次执行。
尽管所有的请求都被发往过滤器链 revel.Filters
, Revel 也提供了 过滤器配置
, 允许开发者根据操作或控制器添加、插入、删除过滤器。
此功能通过 FilterConfiguringFilter
实现, 它本身就是一个过滤器.
Filters 负责依次调用下一个过滤器来依次处理请求。这通常需要完成下面的表达式:
var MyFilter = func(c *revel.Controller, fc []revel.Filter) { // .. 做一些预处理 .. fc[0](c, fc[1:]) // 执行下一个过滤器 // .. 做一些后期处理 ..}
Filters 接受一个 *Controller
类型的参数, 而不是被调用的实际的控制器类型。如果过滤器需要访问实际的控制器类型,可以这样实现:
var MyFilter = func(c *revel.Controller, fc []revel.Filter) { if ac, err := c.AppController.(*MyController); err == nil { // 判定存在一个 *MyController 实例... } fc[0](c, fc[1:]) // 执行下一个过滤器}
注意:这种模式往往说明拦截器可能是实现所需功能的好的机制的一个指标。
Revel 提供Websockets支持。
处理一个 Websocket 连接:
WS
类型的路由。*websocket.Conn
参数的控制器方法.举个栗子, 在 routes
文件中添加路由:
WS /app/feed Application.Feed
添加一个控制器方法,接受*websocket.Conn
参数:
import "code.google.com/p/go.net/websocket"func (c App) Feed(user string, ws *websocket.Conn) revel.Result { ...}
自定义Controller 是一个直接或间接嵌入了 *revel.Controller
的struct。
典型用法:
type AppController struct { *revel.Controller}
*revel.Controller
在你自定义的struct中必须是第一个嵌入的类型
revel.Controller
用于请求的上下文,包含了请求与响应数据,请到 the godoc 查看完整内容, 下面是一个定义 (以及辅助类型的定义):
type Controller struct { Name string // 控制器名称, 比如: "Application" Type *ControllerType // 控制器类型描述 MethodType *MethodType // 控制器方法描述 AppController interface{} // 控制器实例 Request *Request Response *Response Result Result Flash Flash // 用户 cookie, 在请求之后清空 Session Session // Session, 保存在cookie中,签名。 Params *Params // URL和表单中的参数(包扩 multipart). Args map[string]interface{} // 每个请求的暂存空间 RenderArgs map[string]interface{} // 传递给模板的参数 Validation *Validation // 数据验证帮助器}// 统一的请求参数包装// 包括:// - URL 查询字符串// - Form 表单字段// - File 文件上传type Params struct { url.Values Files map[string][]*multipart.FileHeader}type Request struct { *http.Request ContentType string}type Response struct { Status int ContentType string Headers http.Header Cookies []*http.Cookie Out http.ResponseWriter}
作为HTTP请求处理的一部分,Revel实例化一个控制器,设置所有revel.Controller
嵌入的属性, 因此, Revel 不在请求之间共享实例,对于每个请求的处理,控制器都是独立的。
Revel使用文本文件提供国际化翻译支持。Revel 支持语言翻译文件化, 自动区域查询, cookie重写、嵌套的消息与参数。
en-US
。en
。 语言标识符请参考 ISO 639-1 codes。US
。 区域标识符请参考 ISO 3166-1 alpha-2 codes。Revel 处理消息文件和国际化的方法与其他框架类似。如果你想立马上手, 可以参考示例程序 revel/samples/i18n
,里面包含所有的基础知识。
国际化消息在消息文件中定义。这些消息在渲染视图模板(或程序的其他地方)时使用。当创建新的消息文件时,需要遵循几个规则:
messages
目录中。消息文件的文件名没有任何限制; 只要扩展名合法就行。每种语言的文件数量也没有限制。当应用程序启动时,Revel会解析messages
语言目录中所有的扩展名合法的消息文件,并根据语言将它们合并。这意味着你可以自由组织你的消息文件。
例如,您可能希望采取传统的方法,在一个文件中定义所有的:
/app /messages messages.en messages.fr ...
或者是为每种语言创建多个文件,并根据分类组织他们:
/app /messages labels.en warnings.en labels.fr warnings.fr ...
重要注意事项: 在同一语言的多个文件中,虽然技术上可以定义相同的消息key,但是这将导致不可预知的行为。当每种语言定义了多个文件时,请注意保持您的key唯一,这样文件合并后,key将不会被覆盖!
消息文件是一个 goconfig 文件。这意味着,消息应按照key-value格式定义:
key=value
举个栗子:
greeting=Hello greeting.name=Robgreeting.suffix=, welcome to Revel!
goconfig 文件被分成多个 sections. 默认的段 总是存在, 并且包含未指定段时定义的消息。例如:
key=value[SECTION]key2=value2
key=value
消息消息被隐式放置在默认段中,因为它没有定义在一个指定的段中。
消息文件的所有消息应在定义默认的段中,除非它们是用于某个特定区域(见Regions获取更多消息)。
注意: 段是 goconfig 功能.
特定区域的消息应当在相同的段中定义。Region-specific messages should be defined in sections with the same name. 例如,假设我们要对所有讲英语的用户显示 "Hello"
, 对英国用户显示 "Hey"
,对讲美国用户显示 "Howdy"
。 为了做到这一点,我们可以定义下面的消息文件 greeting.en
:
greeting=Hello[GB]greeting=Hey[US]greeting=Howdy
对于定义了英语 (en
) 作为他们的首选语言时,Revel 会将greeting
解析成 Hello
。只有在用户的区域被明确定义的情况下,比如en-GB
或 en-US
,Revel 才会用对应段中的消息去解析 greeting
。
重要提示: 如果定义了一个不合法的区域,技术上是允许的,但是它们永远不会被解析。
文件中的消息也可以引用其他消息。这使得用户可以从一个或多个其它的消息组成一个单一的消息。引用消息的语法是 %(key)s
. 例如:
greeting=Hello greeting.name=Robgreeting.suffix=, welcome to Revel!greeting.full=%(greeting)s %(greeting.name)s%(greeting.suffix)s
注意:
消息支持一个或多个参数。在消息中参数使用与 fmt
包相同的规则去解析。 例如:
greeting.name_arg=Hello %s!
参数按照给定的顺序进行解析,参考 解析消息.
为了找出用户喜欢的语言环境,Revel会在以下地方查找一个可用的语言环境:
语言 cookie
对于每次请求,框架会查找应用程序配置 (i18n.cookie
)。如果找到了,它的值被假定为当前的语言环境。当一个cookie已经发现,所有其他解析方法将被跳过。
Accept-Language
HTTP 头
对于每次请求,Revel 会自动解析 HTTP 头信息的 Accept-Language
。其中的每个语言环境根据 HTTP 规范在当前Request
实例中获取和存储。此信息用来为稍后的消息解析函数确定当前语言环境。
更多信息请参考 Parsed Accept-Language HTTP header.
默认语言
当以上所有方法都没有找到可用的客户端语言环境时,框架将使用配置文件中定义的默认语言i18n.default_language
。
如果有的消息解析失败,则返回一个包含原始消息名的特殊格式的字符串。
注意: Accept-Language
头信息 总是 被解析并保存到当前 Request
中, 即使它已经存在。在这种情况下,头信息中的值即使从未被消息解析功能使用,但是仍可以在需要他们的时候使用。
应用程序可以从Request
内部使用 Request.Locale
属性访问当前语言环境。例如:
func (c App) Index() revel.Result { currentLocale := c.Request.Locale c.Render(currentLocale)}
在模板中, 就可以从 renderArgs
中的 currentLocale
变量中访问当前语言环境。 例如:
<p>Current preferred locale: {{.currentLocale}}</p>
如果程序需要访问 Accept-Language
HTTP 头信息, 可以从 Controller
实例的 Request
中获取。 AcceptLanguages
字段(AcceptLanguage
实例的切片)包含所有从各自的头信息中解析的值, 最合适的值是切片中的第一个。例如:
func (c App) Index() revel.Result { // 获取所有解析到的AcceptLanguage的字符串表示形式 c.RenderArgs["acceptLanguageHeaderParsed"] = c.Request.AcceptLanguages.String() // 返回最合适AcceptLanguage实例 c.RenderArgs["acceptLanguageHeaderMostQualified"] = c.Request.AcceptLanguages[0] c.Render()}
更多信息请参考 HTTP 规范.
消息可以在 控制器 或 模板中解析。
控制器中使用 Message(message string, args ...interface{})
函数使用当前语言环境解析消息:
func (c App) Index() revel.Result { c.RenderArgs["controllerGreeting"] = c.Message("greeting") c.Render()}
在模板中使用 模板函数 msg
解析当前语言环境的消息。例如:
<p>Greetings without arguments: {{msg . "greeting"}}</p> <p>Greetings: {{msg . "greeting.full.name" "Tommy Lee Jones"}}</p>
注意: msg
函数的签名是 msg . "message name" "argument" "argument"
. 如果没有参数, 可以忽略。
文件 | 选项 | 描述 |
---|---|---|
app.conf | i18n.cookie | 语言cookie的名称。应使用Revel cookie的前缀,以避免cookie名称冲突。 |
app.conf | i18n.default_language | 默认的语言环境,在没有首选区域的情况下使用。 |
路由定义在一个单独的 routes
文件中.
路由定义规则是:
(METHOD) (URL Pattern) (Controller.Action)
下面演示路由的定义:
# conf/routes# 这个文件定义了应用程序的所有路由 (优先级按照先后顺序)GET /login App.Login # 一个简单的路由GET /hotels/ Hotels.Index # 一个简单的路由,带不带斜线后缀都一样GET /hotels/:id Hotels.Show # 绑定到一个URI参数idWS /hotels/:id/feed Hotels.Feed # WebSocketsPOST /hotels/:id/:action Hotels.:action # 自动匹配路由到一个控制器的多个方法GET /public/*filepath Static.Serve("public") # 映射到 /public/下的静态文件
接下来我们一个个看。最后在看看如何实现 反转路由 – 生成URL调用一个特定的方法
GET /login App.Login
直接将一个URL精确匹配到一个控制器方法。
GET /hotels/ Hotels.Index
/hotels
和 /hotels/
路由都会调用 Hotels.Index
方法。反转 Hotels.Index
产生的URL则会包含斜线后缀。
斜线后缀无法用于区分两个同名路由,/login
和 /login/
是一样的
GET /hotels/:id Hotels.Show
Revel可以从URL重提取参数并绑定到控制器方法。:id
变量会尝试匹配除了斜线后缀外的任意值。举个栗子,/hotels/123
和 /hotels/abc
都与这个路由匹配。
提取的参数保存到 Controller.Params
map中, 绑定到控制器方法Show中. 举个栗子:
func (c Hotels) Show(id int) revel.Result { ...}
或
func (c Hotels) Show() revel.Result { var id string = c.Params.Get("id") ...}
或
func (c Hotels) Show() revel.Result { var id int c.Params.Bind(&id, "id") ...}
GET /public/*filepath Static.Serve("public")
路由器识别第二种通配符 。通配符必须放到路由的最后一个元素前, 匹配所有以*之前路径开头的URL
这里的路由将匹配 “/public/”和以他开头的所有路径
WS /hotels/:id/feed Hotels.Feed
Websockets 以同样的方式接受请求, 它使用 WS标识符
相应的方法签名如下:
func (c Hotels) Feed(ws *websocket.Conn, id int) revel.Result { ...}
GET /public/*filepath Static.Serve("public")GET /favicon.ico Static.Serve("public","img/favicon.png")
对于使用2个参数的Static.Serve, 在 ” 和 , 之间不允许有空格,due to how encoding/csv works.
对于静态资源服务, Revel 有static 模块支持, 包含一个单一的带两个参数的静态 控制器
(可以参考 代码结构布局图 了解更多信息)
路由可以绑定一个或多个参数到控制器方法中,举个栗子:
GET /products/:id ShowList("PRODUCT")GET /menus/:id ShowList("MENU")
参数通过在路由中的位置绑定到参数名,这里,列表中的字符串将被绑定到方法的第一个参数中。
下面是几种有用的情况:
POST /hotels/:id/:action Hotels.:action
URL 的提取也可以用于确定调用哪个控制器方法。匹配规则不区分大小写
第一个例子匹配下面的路由
/hotels/1/show => Hotels.Show/hotels/2/details => Hotels.Details
第二个例子匹配应用程序中的任意的方法
/app/login => App.Login/users/list => Users.List
由于匹配控制器和方法不区分大小写,下面的路由也匹配
/APP/LOGIN => App.Login/Users/List => Users.List
使用自动路由作为一个捕获所有的方法(例如文件中的最后一个路由),用于快速挂接方法,to non-vanity URLs,尤其是在与反向路由器一起使用是很有用的。
对于以下几种情况,使用反转路由生成URLs是一个很好的方法,
构建程序时,Revel自动生成一个路由包app/routes
,一条语句就可以使用:
routes.Controller.Action(param1, param2)
上面的语句使用Controller.Action和参数返回一个字符串类型的 URL,下面给出一个完整的例子:
import ( "github.com/revel/revel" "project/app/routes")type App struct { *revel.Controller }// 生成一个表单页面func (c App) ViewForm(username string) revel.Result { return c.Render(username)}// 处理提交的表单func (c App) ProcessForm(username, input string) revel.Result { ... if c.Validation.HasErrors() { c.Validation.Keep() c.Flash.Error("Form invalid. Try again.") return c.Redirect(routes.App.ViewForm(username)) // <--- 反转路由 } c.Flash.Success("Form processed!") return c.Redirect(routes.App.ViewConfirmation(username, input)) // <--- 反转路由}
局限性: Only primitive parameters to a route are typed due to the possibility of circular imports. Non-primitive parameters are typed as interface{}.
Revel 提供了一个服务器端、临时的、低延迟存储的缓存库。对于频繁访问数据库中缓慢变化的数据,使用缓存一个很好的方法,并且它也可以用于实现用户session (如果基于cookie的session不足).
参考 缓存接口
缓存有三种过期时间:
cache.DEFAULT
, 默认过期时间(1小时)。cache.FOREVER
, 永不过期。重要提示:调用者不能依赖于存在于缓存中内容,因为数据是不持久保存,重新启动后,会清除所有缓存数据。
缓存读写接口自动为调用者序列化任何类型的的值。有以下几种方式:
[]byte
类型, 数据保持不变。encoding/gob
进行编码。缓存通过以下几种方式进行配置:
在app.conf
中配置缓存:
cache.expires
- 一个字符串,time.ParseDuration
类型,指定一个过期时间 (默认1小时)cache.memcached
-一个布尔值,是否开启memcached缓存 (默认不开启)cache.redis
-一个布尔值,是否开启redis缓存 (默认不开启)cache.hosts
- 缓存的主机列表(多个主机使用逗号分隔),cache.memcached
开启后有效。下面是常见操作的一个例子。请注意,如果不需要调用的结果来处理请求,调用者可以在新的goroutine调用缓存操作。
import ( "github.com/revel/revel" "github.com/revel/revel/cache")func (c App) ShowProduct(id string) revel.Result { var product Product if err := cache.Get("product_"+id, &product); err != nil { product = loadProduct(id) go cache.Set("product_"+id, product, 30*time.Minute) } return c.Render(product)}func (c App) AddProduct(name string, price int) revel.Result { product := NewProduct(name, price) product.Save() return c.Redirect("/products/%d", product.id)}func (c App) EditProduct(id, name string, price int) revel.Result { product := loadProduct(id) product.name = name product.price = price go cache.Set("product_"+id, product, 30*time.Minute) return c.Redirect("/products/%d", id)}func (c App) DeleteProduct(id string) revel.Result { product := loadProduct(id) product.Delete() go cache.Delete("product_"+id) return c.Redirect("/products")}
缓存有一个全球性的key空间 - 使用它作为一个session存储,调用方应采用session UUID的优点,如下图所示:
cache.Set(c.Session.Id(), products)// 然后在随后的请求中使用它err := cache.Get(c.Session.Id(), &products)
Revel 尽可能让客户端传来的参数转换成Go语言的数据类型变得简单。这种从字符串转换成另外一种类型被称为“数据绑定”。
所有的请求参数被收集到一个单独的 Params
对象中. 包括:
Params对象定义在 (godoc)中:
type Params struct { url.Values Files map[string][]*multipart.FileHeader}
嵌入的 url.Values
(godoc) 提供了简单值的查询支持, 但是开发者可以更方便的使用Revel的数据绑定支持,提取参数到任意的数据类型。
参数可以直接绑定到控制器方法,例如:
func (c AppController) Action(name string, ids []int, user User, img []byte) revel.Result { ...}
在控制器方法执行之前, Revel 通过变量名称绑定器解析参数到指定的数据类型,如果解析参数失败, 参数将被解析到目标数据类型的初始值。
使用 Revel 的绑定器绑定一个参数到指定的数据类型 (godoc),它集成了Params对象。例如:
func (c SomeController) Action() revel.Result { var ids []int c.Params.Bind(&ids, "ids") ...}
支持的数据类型有:
数据类型绑定的语法描述如下,详细内容描述也可以参考 源代码。
字符串 “true”, “on”, 和 “1” 被绑定到 true,其他的为 false.
切片绑定有两种语法:有序和无序
有序:
?ids[0]=1&ids[1]=2&ids[3]=4
绑定结果为 []int{1, 2, 0, 4}
无序:
?ids[]=1&ids[]=2&ids[]=3
绑定结果为 []int{1, 2, 3}
注意: 只有有序切片可以绑定到一个 []Struct:
?user[0].Id=1&user[0].Name=rob&user[1].Id=2&user[1].Name=jenny
Struct简单的使用一个 . 进行绑定:
?user.Id=1&user.Name=rob&user.Friends[]=2&user.Friends[]=3&user.Father.Id=5&user.Father.Name=Hermes
绑定到下面的struct类型:
type User struct { Id int Name string Friends []int Father User}
注意: struct中的字段必须是导出的(首字母大写)。
内置的 SQL 标准时间格式 [“2006-01-02”, “2006-01-02 15:04”]
使用 Revel官方模式 简单的添加时间格式到 TimeFormats
变量:
func init() { revel.TimeFormats = append(revel.TimeFormats, "01/02/2006")}
文件上传参数可以绑定到以下几种类型:
它是 Go的 multipart 包 的一个包装器. 文件保存在内存中,如果文件大小超过10MB(默认值), 就会被保存到一个临时文件中。
注意: 绑定 os.File
类型,会保存到临时文件 (如果没有的话),所以效率低。
应用程序可以定义绑定器。
自定义绑定器需要实现 binder 接口并注册自定义类型:
var myBinder = revel.Binder{ Bind: func(params *revel.Params, name string, typ reflect.Type) reflect.Value {...}, Unbind: func(output map[string]string, name string, val interface{}) {...},}func init() { revel.TypeBinders[reflect.TypeOf(MyType{})] = myBinder
Revel 自带参数验证功能:
示例应用程序提供了一些深入理解参数验证的例子。
下面演示使用内联错误消息验证字段
func (c MyApp) SaveUser(username string) revel.Result { // Username (required) 至少 4 - 15 个字符. c.Validation.Required(username) c.Validation.MaxSize(username, 15) c.Validation.MinSize(username, 4) c.Validation.Match(username, regexp.MustCompile("^w*$")) if c.Validation.HasErrors() { // 在flash上下文中保存验证错误并重定向 c.Validation.Keep() c.FlashParams() return c.Redirect(Hotels.Settings) } // All the data checked out! ...}
username
字段验证条件 (Required必填, MinSize最小长度, MaxSize最大长度, Match匹配一个正则表达式).Validation.HasErrors()
如果验证没有通过,返回 trueValidation.Keep()
告诉 Revel 序列化 验证错误消息到 Flash cookie中.Hotels.Settings 方法渲染一个模板:
{{/* app/views/Hotels/Settings.html */}}...{{if .errors}}Please fix errors marked below!{{end}}...<p class="{{if .errors.username}}error{{end}}"> Username: <input name="username" value="{{.flash.username}}"/> <span class="error">{{.errors.username.Message}}</span></p>
它做了三件事:
errors
map 中是否存在key为 username
的错误字段.username
的字段值注意:模板函数 field 使用了验证错误框架,使模板的编写变得更加方便。
如果错误消息都显示在一个地方,模板就变得简单了 (比如,放到页面顶部的一个红色的盒子中.)
下面的例子与上面有两个不同:
Message
,而不是使用验证函数默认的错误信息代码如下:
func (c MyApp) SaveUser(username string) revel.Result { // Username (必填) 至少 4 - 15 字符. c.Validation.Required(username).Message("Please enter a username") c.Validation.MaxSize(username, 15).Message("Username must be at most 15 characters long") c.Validation.MinSize(username, 4).Message("Username must be at least 4 characters long") c.Validation.Match(username, regexp.MustCompile("^w*$")).Message("Username must be all letters") if c.Validation.HasErrors() { // 保存错误信息到 flash 上下文中并重定向 c.Validation.Keep() c.FlashParams() return c.Redirect(Hotels.Settings) } // All the data checked out! ...}
模板如下:
{{/* app/views/Hotels/Settings.html */}}...{{if .errors}}<div class="error"> <ul> {{range .errors}} <li> {{.Message}}</li> {{end}} </ul></div>{{end}}...
Revel 支持两种 基于 cookie 存储机制
// 一个签名 cookie (不超过4kb).// 限制: Keys may not have a colon in them.type Session map[string]string// 在每个请求中,Flash 获取并重写cookie。// 它允许数据每次跨越存储到一个页面。It allows data to be stored across one page at a time.// 它通常用来实现成功或错误消息。// 比如: Post/Redirect/Get : http://en.wikipedia.org/wiki/Post/Redirect/Gettype Flash struct { Data, Out map[string]string}
Revel的 “session” 是一个加密签名存储的字符串 map。
影响如下:
session cookie 的默认过期时间是浏览器关闭。 可以在app.config修改session.expires配置来指定一个有效期时间。格式是time.ParseDuration.
Flash 支持单独使用字符串存储。这对于实现 Post/Redirect/Get 模式是很有用的, 或临时显示 “操作成功!” 或 “操作失败!” 消息。
下面是一个例子:
// 一个设置页面func (c App) ShowSettings() revel.Result { return c.Render()}// 处理页面提交数据func (c App) SaveSettings(setting string) revel.Result { // 验证用户输入 c.Validation.Required(setting) if c.Validation.HasErrors() { // 设置被带回的flash cookie错误信息 c.Flash.Error("Settings invalid!") // 在flash cookie中保存验证错误 c.Validation.Keep() // 复制所有给定的参数(URL, Form, Multipart)到flash cookie c.FlashParams() return c.Redirect(App.ShowSettings) } saveSetting(setting) // 设置flash cookie成功消息 c.Flash.Success("Settings saved!") return c.Redirect(App.ShowSettings)}
例子主要功能如下:
主要使用了两个函数:
Flash.Success(message string)
是 Flash.Out["success"] = message
简写方式Flash.Error(message string)
是 Flash.Out["error"] = message
简写方式在模板中使用key来获取Flash 消息。例如, 通过简写函数获取成功和错误消息:
{{.flash.success}}{{.flash.error}}
控制器方法必须返回一个revel.Result
, 用来处理响应结果,其接口定义如下:
type Result interface { Apply(req *Request, resp *Response)}
revel.Controller
使用以下方法来处理响应结果:
此外,开发者可以自定义revel.Result.
每个内建的 Result 都有一个默认的状态码和内容类型,简单的设置相关属性就可以修改这些默认值:
func (c App) Action() revel.Result { c.Response.Status = http.StatusTeapot c.Response.ContentType = "application/dishware" return c.Render()}
控制器渲染方法 (e.g. “Controller.Action”), mvc.Controller.Render
做了两件事:
如果不成功 (比如,没有找到模板), 将返回 ErrorResult.
这允许开发人员编写:
func (c MyApp) Action() revel.Result { myValue := calculateValue() return c.Render(myValue)}
在模板中引用变量 “myValue”。这通常比构建一个明确的map更方便,因为在许多情况下,数据将被作为局部变量处理。
注意: Revel 通过调用的控制器方法名来确定使用哪个模板,查找参数名。因此, c.Render() 只可称为由操作。
应用程序可以调用 RenderJson
或 RenderXml
并传送任意类型的变量 (通常是一个 struct). Revel 会使用 json.Marshal
orxml.Marshal
对变量进行序列化操作.
如果 app.conf
配置文件中 results.pretty=true
, 将使用 MarshalIndent
进行序列化, 生成漂亮的缩进,方便使用。
一个辅助函数是用于生成重定向。它有两种方式使用:
return c.Redirect(Hotels.Settings)
这种形式是非常有用的,因为它提供了一定程度的路由类型安全性和独立性(自动生成URL)。
return c.Redirect("/hotels/%d/settings", hotelId)
通常用来传送参数.
它返回 302 (临时重定向) 状态码.
下面是一个添加简单结果的例子。
创建此类型:
type Html stringfunc (r Html) Apply(req *Request, resp *Response) { resp.WriteHeader(http.StatusOK, "text/html") resp.Out.Write([]byte(r))}
在一个控制器方法中使用它:
func (c *App) Action() revel.Result { return Html("<html><body>Hello World</body></html>")}
每一个Result 都会设置一个默认的状态码,你也可以重新设置默认的状态代码:
func (c *App) CreateEntity() revel.Result { c.Response.Status = 201 return c.Render()}
模块是一些包,可以集成到Revel程序中。Revel允许多个Revel程序(或第三方代码)共享控制器、模板、资源和其他代码。
模块中文件的布局应当与Revel应用程序文件结构一致。“托管”应用程序会按以下方式将它们合并:
Static.ServeModule("modulename","public")
提供module:modulename
被添加到你的程序中为了将模块添加到您的应用程序,需要在app.conf
中添加一行配置:
module.mymodulename = go/import/path/to/module
如果导入路径为空,将禁用模块:
module.mymodulename =
举个栗子, 启用测试运行模块:
module.testrunner = github.com/revel/revel/modules/testrunner
Revel提供了一个测试框架,可以很容易地编写和运行针对您的应用程序的功能测试。
应用程序带有一个简单的测试骨架以便快速上手测试。
测试代码保存在测试目录中:
corp/myapp app/ conf/ public/ tests/ <----
一个简单的测试如下所示:
type AppTest struct { revel.TestSuite}func (t *AppTest) Before() { println("Set up")}func (t *AppTest) TestThatIndexPageWorks() { t.Get("/") t.AssertOk() t.AssertContentType("text/html")}func (t *AppTest) After() { println("Tear down")}
上面的例子中展示了:
revel.TestSuite
的一个structBefore()
、 After()
在每个测试方法之前和之后被调用,如果有的话。revel.TestSuite
帮助发出请求到你的应用程序,和对响应的断言。你可以用两种方式运行这个测试:
要创建自己的测试套件,需要定义一个嵌入了revel.TestSuite
类型的struct, 它提供了一个HTTP客户端和一些辅助方法发出请求到应用程序。
type TestSuite struct { Client *http.Client Response *http.Response ResponseBody []byte}// 一些请求方法func (t *TestSuite) Get(path string)func (t *TestSuite) Post(path string, contentType string, reader io.Reader)func (t *TestSuite) PostForm(path string, data url.Values)func (t *TestSuite) MakeRequest(req *http.Request)// 一些断言方法func (t *TestSuite) AssertOk()func (t *TestSuite) AssertContentType(contentType string)func (t *TestSuite) Assert(exp bool)func (t *TestSuite) Assertf(exp bool, formatStr string, args ...interface{})
所有的请求方法类似:
/users/
)Response
中的成员ResponseBody
成员中如果开发人员希望使用一个定制的HTTP客户端,而不是默认的http.DefaultClient,应当在Before()
方法之前替换它。
断言失败后,会抛出恐慌并被测试工具捕获,并将错误列出。
为了运行测试,testrunner
模块必须被激活。需要在 app.conf
文件中配置:
module.testrunner = github.com/revel/revel/modules/testrunner
您还必须导入测试模块的路由,在你的 routes
文件中加入下面的内容:
module:testrunner
配置完后,测试就可以交互或非交互方式运行。
要利用 Revel 的热编译功能,交互式测试运行提供了快速编辑刷新周期。
例如,开发人员从浏览器中访问 /@tests
:
然后,增加一个测试方法:
func (t AppTest) TestSomethingImportant() { t.Get("/") t.AssertOk() t.AssertContentType("text/xml")}
然后,刷新浏览器,看看新的测试:
运行测试:
嗯哼,,,行不通哦,,,修改代码使用“text/html” 替换 “text/xml”类型。
t.AssertContentType("text/html")
然后,重新运行测试:
成功啦!
Revel 命令行工具 提供了一个 test
命令,允许在命令行中运行测试。
下面是一个示例会话:
$ revel test github.com/revel/revel/samples/booking dev~~ revel! http://revel.github.com/revel~INFO 2012/11/09 19:21:02 revel.go:237: Loaded module testrunnerOpen DBListening on port 9000...INFO 2012/11/09 19:21:06 test.go:95: Testing Booking example (github.com/revel/revel/samples/booking) in dev modeGo to /@tests to run the tests.1 test suite to run.AppTest PASSED 0sAll Tests Passed.
您还可以运行单个测试套件,或套件内的方法,用句点分隔参数:
$ revel test github.com/revel/revel/samples/booking dev ApplicationTest$ revel test github.com/revel/revel/samples/booking dev ApplicationTest.TestThatIndexPageWorks
在控制台测试套件只有一个简单的合格/不合格显示。更详细的结果写入到文件系统:
$ cd src/github.com/revel/revel/samples/booking$ find test-resultstest-resultstest-results/app.logtest-results/AppTest.passed.htmltest-results/result.passed
它写三点不同:
app.log
result.passed
或 result.failed
被写入, 这取决于整体的成功。对于整合持续构建测试,有两点建议:
result.success
, 或禁止 result.failed
存在。Revel 做了什么:
revel.TestSuites
类型的变量设置一个列表当 testrunner
模块激活后,测试代码才会被构建。
改进测试框架:
test-results/app.log
Revel 支持计划任务(异步执行), 运行在请求流程的外部。比如,更新缓存数据的周期性任务,或发送电子邮件的临时任务。
该框架是一个可选模块,默认是禁用的。要将它激活,需要在配置文件中添加该模块:
module.jobs = github.com/revel/revel/modules/jobs
此外,为了访问计划任务的监控页面,需要将下面的内容添加到路由文件中:
module:jobs
这条语句将插入 /@jobs
路由
有两个选项来限制计划任务。
这个例子显示了它们的默认值。
jobs.pool = 10 # 允许同时运行的任务数jobs.selfconcurrent = false # 一个任务只允许一个实例
应用程序启动时, 使用revel.OnAppStart
注册一个函数来运行一个任务。Revel 在服务启动之前,会连续启动这些任务。 请注意,此功能实际上并未使用计划任务模块,它被用来提交任务,但并不阻止服务器的启动。
func init() { revel.OnAppStart(func() { jobs.Now(populateCache{}) })}
任务可以被指定在任意时间运行。使用的时间表有两个选项:
Revel 使用 cron library 来解析时间表和任务。cron库的说明 提供了时间格式的详细描述。
计划任务通常使用 revel.OnAppStart
钩子进行注册,但也可以在以后的任何时间注册。
下面是一些例子:
import ( "github.com/revel/revel" "github.com/revel/revel/modules/jobs/app/jobs" "time")type ReminderEmails struct { // 过滤}func (e ReminderEmails) Run() { // 查询数据库 // 发送电子邮件}func init() { revel.OnAppStart(func() { jobs.Schedule("0 0 0 * * ?", ReminderEmails{}) jobs.Schedule("@midnight", ReminderEmails{}) jobs.Schedule("@every 24h", ReminderEmails{}) jobs.Every(24 * time.Hour, ReminderEmails{}) })}
您可以在 app.conf
文件中配置时间表,并在任何地方引用。这可以为 crontab 格式提供易于重用和有用的说明。
在 app.conf
定义你的时间表,:
cron.workhours_15m = 0 */15 9-17 ? * MON-FRI
使用一个cron规范指定时间表,就可以在任何地方引用它。
func init() { revel.OnAppStart(func() { jobs.Schedule("cron.workhours_15m", ReminderEmails{}) })}
注意: cron 时间表的名字必须以 “cron.”开头
有时候在响应用户的一个操作时,还要处理一些事情。在这种情况下,模块可以在某个时间运行一个任务。
模块提供的唯一控制是等待多长时间运行任务。
type AppController struct { *revel.Controller }func (c AppController) Action() revel.Result { // 处理请求 ... // 立即发送电子邮件(异步) jobs.Now(SendConfirmationEmail{}) // 或者,一分钟后发送电子邮件(异步)。 jobs.In(time.Minute, SendConfirmationEmail{})}
通过使用jobs.Func
类型包装一个func()
函数,来注册一个任务。例如:
func sendReminderEmails() { // 查询数据库 // 发送电子邮件}func init() { revel.OnAppStart(func() { jobs.Schedule("@midnight", jobs.Func(sendReminderEmails)) })}
模块提供了一个状态页面,用来显示任务的运行状态(IDLE 或 RUNNING), 以及之前和下次运行时间。
Revel 支持四类日志信息:
下面是在Revel中使用日志的例子:
now := time.Now()revel.TRACE.Printf("%s", now.String())
日志记录器默认使用 go 日志.
日志记录器在 app.conf中配置。例如:
app.name = sampleapp[dev]log.trace.output = stdoutlog.info.output = stdoutlog.warn.output = stderrlog.error.output = stderrlog.trace.prefix = "TRACE "log.info.prefix = "INFO "log.trace.flags = 10log.info.flags = 10[prod]log.trace.output = offlog.info.output = offlog.warn.output = log/%(app.name)s.loglog.error.output = log/%(app.name)s.log
在开发环境中:
在生产环境中:
根据 标记常量修改日志格式,。例如, 01:23:23 /a/b/c/d.go:23 Message
格式,使用标记 Ltime | Llongfile = 2 | 8 = 10
开发状态:
nathany上已经给出了Go包版本控制的许多信息。然而, 那时还没有一个包版本管理的社区标准。因此, 只能由开发者确保软件安全与可重复构建。
如果你使用Revel构建应用程序, 开发者应避免由于不兼容造成的问题。你的构建过程不应当使用go get获取Revel的主分支。
处理这种情况最简单的方法是签出Revel和所有依赖包到你的代码库中。如果你使用git, 可以把这些库作为你项目的子代码库。
或者, 试试下面链接中的软件包管理器。
几种常见的部署方法如下:
使用命令行演示互动部署 - 一般将web服务器作为守护程序运行。常用工具有:
目标机器上不需要安装Go语言环境。Revel命令行工具 提供了package
包命令,用来编译和压缩应用程序, 如下所示:
# 本地测试运行$ revel run import/path/to/app.. 测试程序 ..# 打包程序$ revel package import/path/to/app打包的文件准备好了: app.tar.gz# 复制到目标机器$ scp app.tar.gz target:/srv/# 在目标机器上运行$ ssh target$ cd /srv/$ tar xzvf app.tar.gz$ bash run.sh
如果您的本地机器与目标机器的架构相同,那么不会有什么问题。否则,你需要参考交叉编译来构建指定平台架构的程序。
由于静态链接的二进制程序带有完整的资源文件,可能会变得相当大,所以支持增量部署。
# 构建应用程序到一个临时目录$ revel build import/path/to/app /tmp/app# 将临时目录 Rsync 到服务器上的主目录$ rsync -vaz --rsh="ssh" /tmp/app server# 连接到服务器,并重新启动应用程序...
Rsync 支持ssh完整的复制操作。例如, 下面是一个更复杂的操作:
# 一个使用自定义证书、登录名和目标目录的例子$ rsync -vaz --rsh="ssh -i .ssh/go.pem" /tmp/myapp2 ubuntu@ec2-50-16-80-4.compute-1.amazonaws.com:~/rsync
这种方法依赖你的版本控制系统来分发、更新代码。你需要在服务器上安装Go语言环境。好处是,你避免了潜在的交叉编译。
$ ssh server... 安装Go语言环境 ...... 配置存储库 ...# 进入你的应用程序所在的目录 (GOPATH环境变量), 拉取代码, 并运行。$ cd gocode/src/import/path/to/app$ git pull$ revel run import/path/to/app prod
Revel 维护了一个 Heroku Buildpack, 允许一条命令即可部署代码,请参考 自述文件 使用说明
为了创建一个交叉编译环境,我们需要从源代码构建。参考 从源代码安装Go 获取更多信息。你必须正确设置 $PATH 和 $GOPATH 环境变量, 否则,如果已经有一个二进制的Go语言包存在,你会陷入严重的错误。
当Go编译器安装成功后,通过指定 GOOS 和 GOARCH 目标环境来建立交叉编译环境。参考 可用的环境变量 获取更多信息。
$ cd /path/to/goroot/src$ GOOS=linux GOARCH=amd64 ./make.bash --no-clean$ GOOS=windows GOARCH=386 ./make.bash --no-clean
在新的环境中安装Revel,然后设定目标架构,打包应用程序。
$ GOOS=linux GOARCH=amd64 revel package import/path/to/app
然后,将压缩包复制到目标平台。
app.conf
是Revel程序的配置文件,它使用 goconfig 语法,类似微软的 INI 文件。
下面是个例子:
app.name=chatapp.secret=pJLzyoiDe17L36mytqC912j81PfTiolHm1veQK6Grn1En3YFdB5lvEHVTwFEaWvjhttp.addr=http.port=9000[dev]results.pretty=truewatch=truelog.trace.output = offlog.info.output = stderrlog.warn.output = stderrlog.error.output = stderr[prod]results.pretty=falsewatch=falselog.trace.output = offlog.info.output = offlog.warn.output = %(app.name)s.loglog.error.output = %(app.name)s.log
每个段是一种 运行模式。最上面的 key (不在任何段内)对所有的运行模式有效。这使得默认值在所有模式中适用,并且根据需要被重写。[prod]
段仅用于 生产
模式。
新建的Revel程序中默认定义了 dev 和 prod 模式, 你也可以自定义你需要的段。 程序启动时,根据 (命令行工具)“revel run” 提供的参数来选择运行模式。
开发者可以自定义key,并通过 revel.Config
变量 访问它们。这里公开了一些简单的 api。
应用程序名称,用于控制台输出和开发web页。
例如:
app.name = Booking example application
默认值: 无值
密钥用于密码操作 (revel.Sign
)。Revel 在内部使用它签署session cookies。设置为空将禁用签名。
使用 revel new
新建项目时,它被设置为一个随机的字符串。
例如:
app.secret = pJLzyoiDe17L36mytqC912j81PfTiolHm1veQK6Grn1En3YFdB5lvEHVTwFEaWvj
默认值: 无值
监听端口
例如:
http.port = 9000
监听ip地址
Linux中, 空字符串代表通配符 – Windows中, 空字符串被转换为 "localhost"
默认值: ””
Specifies the port for the application to listen on, when run by the harness. For example, when the harness is running, it will listen on http.port
, run the application on harness.port
, and reverse-proxy requests. Without the harness, the application listens on http.port
directly.
默认情况下,会选择一个随机的空闲端口。这仅仅是必要的,由该程序限制插座访问的环境中运行时设置。By default, a random free port will be chosen. This is only necessary to set when running in an environment that restricts socket access by the program.
Default: 0
如果为真, Revel Web服务器将自行配置为接受SSL连接。这需要一个 X509 证书和一个 key 文件。
默认值: false
指定 X509 证书文件的路径
默认值: ””
指定 X509 证书 key的路径
默认值: ””
确定模板渲染时是否使用 分块编码。分块编码可以减少发送到客户端的第一个字节的时间(在整个模板已经完全呈现数据之前)。
默认值: false
配置 RenderXml
和 RenderJson
生成缩进格式的 XML/JSON. 例如:
results.pretty = true
默认值: false
为消息翻译指定默认的语言,如果客户端请求的语言环境未确认。如果不指定,则返回一个虚拟的信息。
例如:
i18n.default_language = en
默认值: ””
指定存储用户语言环境的cookie名称
默认值: “%(cookie.prefix)_LANG” (参考 cookie.prefix)
Revel 监视项目改动,并支持几种类型文件的热重载。启用监视:
watch = true
如果为假, 禁用监视, 并忽略其他相关的监视配置 watch.*
(适用于生产环境)
默认值: true
如果为真, Revel 监视模板变化,必要时重新加载他们。
默认值: true
如果为真, Revel 监视 routes
文件的变化,必要时重新加载。
默认值: true
如果为真, Revel 监视Go代码改动,必要时重新编译代码(作为反向代理运行)。
app/
目录(包括子目录)下的代码都被监视。
默认值: true
Revel 组件默认使用下面的 cookies:
Revel 使用这个属性作为 Revel-produced cookies前缀。这样可以在同一台主机上运行多个REVEL应用程序。
例如,
cookie.prefix = MY
则对应的 cookie 名称如下:
默认值: “REVEL”
Revel 使用这个属性设置session cookie的有效期。 Revel 使用 ParseDuration 解析字符串,默认值是 30 天。也可以设置为会话结束时过期。 请注意,客户端的行为依赖于浏览器的设置,所以结果并不总是保证。
指定模板左右分隔符
必须这样指定分隔符 “左分隔符 右分隔符”
默认值: “{{ }}”
指定默认的日期格式,Revel在两个地方使用它:
默认值: “2006-01-02”
指定默认的日期时间格式,Revel在两个地方使用它:
默认值: “2006-01-02 15:04”
指定DB模块的 database/sql 驱动程序导入路径。
默认值: ””
指定 database/sql 驱动程序名称 (在sql.Open
中使用).
默认值: ””
指定 database/sql 数据源名称 (在 sql.Open
中使用).
默认值: ””
Build tags 构建程序的时候使用。
默认值: ””
TODO
cache 模块是一个简单的堆或分布式缓存接口
设置缓存过期时间。在程序中调用者使用常量cache.DEFAULT
获取。
它是接受一个time.ParseDuration
字符串。
(目前还不能指定默认值为 FOREVER
)
默认值: “1h” (1 小时)
如果为真, 缓存模块使用 memcached 来代替内存缓存。
默认值: false
一个逗号分隔的 memcached 主机列表。缓存条目使用确定的主机名缓存key自动分片到可用的主机中。主机可能会多次列出,以增加共享的缓存空间。
默认值: ””
计划任务 模块允许你运行计划任务或者临时任务
时间表可以通过key来配置。
cron.schedulename = @hourly
时间表的计划时间可以在执行器中提交任务时使用。例如:
jobs.Schedule("cron.schedulename", job)
允许同时允许的任务数量。例如:
jobs.pool = 4
如果为 0, 则没有数量限制
默认值: 10
如果为真, 允许一个任务运行,即使是该任务的实例仍在进行中。
默认值: false
模块 通过指定导入路径将模块添加到应用程序中。例如:
module.testrunner = github.com/revel/revel/modules/testrunner
为了使用Revel,必须构建Revel命令行工具:
$ go get github.com/revel/revel/revel
现在运行它:
$ bin/revel~~ revel! http://revel.github.com/revel~usage: revel command [arguments]The commands are: new create a skeleton Revel application run run a Revel application build build a Revel application (e.g. for deployment) package package a Revel application (e.g. for deployment) clean clean a Revel application's temp files test run all tests from the command-lineUse "revel help [command]" for more information.
关于每条命令的含义,请参阅该工具的内置帮助功能。
如何将已有的http.Handlers整合到Revel中?
在概念图中, http.Handler 用于处理用户的请求。Revel的处理是非常简单的,它只是创建控制器实例,并将请求传递给过滤器链。
应用程序可以通过重写默认的处理程序整合现有http.Handlers:
func installHandlers() { var ( serveMux = http.NewServeMux() revelHandler = revel.Server.Handler ) serveMux.Handle("/", revelHandler) serveMux.Handle("/path", myHandler) revel.Server.Handler = serveMux}func init() { revel.OnAppStart(installHandlers)}
拦截器、过滤器和模块之间是什么关系?
模块是可以插入到程序中的包。他们可以在多个Revel程序(或第三方源)中共享控制器、视图、资源和其他代码。
过滤器是可挂接到请求处理管道的函数。他们一般作为一个整体处理技术在应用程序中使用,来垂直分隔应用程序逻辑。