常见问题

最后更新于:2022-04-01 04:22:41

> 如何将已有的http.Handlers整合到Revel中? 在[概念图](http://gorevel.cn/docs/manual/concepts.html)中, 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) } ~~~ > 拦截器、过滤器和模块之间是什么关系? 1. 模块是可以插入到程序中的包。他们可以在多个Revel程序(或第三方源)中共享控制器、视图、资源和其他代码。 2. 过滤器是可挂接到请求处理管道的函数。他们一般作为一个整体处理技术在应用程序中使用,来垂直分隔应用程序逻辑。 3. 拦截器是封装数据和行为一种方便的方式,因为嵌入类型导入它的拦截器和字段。这使得拦截器可以很好的处理一些事情,比如验证登录cookie并保存这些信息到一个字段。拦截器可以应用到一个或多个控制器。
';

命令行工具

最后更新于:2022-04-01 04:22:39

## Build and Run 为了使用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-line Use "revel help [command]" for more information. ~~~ 关于每条命令的含义,请参阅该工具的内置帮助功能。
';

app.conf

最后更新于:2022-04-01 04:22:37

## 概述 `app.conf` 是Revel程序的配置文件,它使用 [goconfig](https://github.com/revel/config) 语法,类似微软的 INI 文件。 下面是个例子: ~~~ app.name=chat app.secret=pJLzyoiDe17L36mytqC912j81PfTiolHm1veQK6Grn1En3YFdB5lvEHVTwFEaWvj http.addr= http.port=9000 [dev] results.pretty=true watch=true log.trace.output = off log.info.output = stderr log.warn.output = stderr log.error.output = stderr [prod] results.pretty=false watch=false log.trace.output = off log.info.output = off log.warn.output = %(app.name)s.log log.error.output = %(app.name)s.log ~~~ 每个段是一种 **运行模式**。最上面的 key (不在任何段内)对所有的运行模式有效。这使得默认值在所有模式中适用,并且根据需要被重写。`[prod]` 段仅用于 `生产` 模式。 新建的Revel程序中默认定义了 **dev** 和 **prod** 模式, 你也可以自定义你需要的段。 程序启动时,根据 ([命令行工具](http://gorevel.cn/docs/manual/tool.html))“revel run” 提供的参数来选择运行模式。 ## 自定义属性 开发者可以自定义key,并通过 [`revel.Config` 变量](http://gorevel.cn/docs/docs/godoc/revel.html#variables) 访问它们。这里公开了一些简单的 [api](http://gorevel.cn/docs/docs/godoc/config.html)。 ## 内建属性 ### 应用程序设置 #### app.name 应用程序名称,用于控制台输出和开发web页。 例如: ~~~ app.name = Booking example application ~~~ 默认值: 无值 * * * #### app.secret 密钥用于密码操作 ([`revel.Sign`](http://gorevel.cn/docs/docs/godoc/util.html#Sign))。Revel 在内部使用它签署session cookies。设置为空将禁用签名。 使用 `revel new`新建项目时,它被设置为一个随机的字符串。 例如: ~~~ app.secret = pJLzyoiDe17L36mytqC912j81PfTiolHm1veQK6Grn1En3YFdB5lvEHVTwFEaWvj ~~~ 默认值: 无值 ### HTTP settings #### http.port 监听端口 例如: ~~~ http.port = 9000 ~~~ * * * #### http.addr 监听ip地址 Linux中, 空字符串代表通配符 – Windows中, 空字符串被转换为 `"localhost"` 默认值: ”” * * * #### harness.port 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 * * * #### http.ssl 如果为真, Revel Web服务器将自行配置为接受SSL连接。这需要一个 X509 证书和一个 key 文件。 默认值: false #### http.sslcert 指定 X509 证书文件的路径 默认值: ”” #### http.sslkey 指定 X509 证书 key的路径 默认值: ”” ### 响应结果 #### results.chunked 确定模板渲染时是否使用 [分块编码](http://gorevel.cn/docs/manual/en.wikipedia.org/wiki/Chunked_transfer_encoding)。分块编码可以减少发送到客户端的第一个字节的时间(在整个模板已经完全呈现数据之前)。 默认值: false * * * #### results.pretty 配置 [`RenderXml`](http://gorevel.cn/docs/docs/godoc/controller.html#RenderXml) 和 [`RenderJson`](http://gorevel.cn/docs/docs/godoc/controller.html#RenderJson) 生成缩进格式的 XML/JSON. 例如: ~~~ results.pretty = true ~~~ 默认值: false ### 国际化 (i18n) #### i18n.default_language 为消息翻译指定默认​​的语言,如果客户端请求的语言环境未确认。如果不指定,则返回一个虚拟的信息。 例如: ~~~ i18n.default_language = en ~~~ 默认值: ”” * * * #### i18n.cookie 指定存储用户语言环境的cookie名称 默认值: “%(cookie.prefix)_LANG” (参考 cookie.prefix) ### 监视 Revel 监视项目改动,并支持几种类型文件的热重载。启用监视: ~~~ watch = true ~~~ 如果为假, 禁用监视, 并忽略其他相关的监视配置 `watch.*` (适用于生产环境) 默认值: true * * * #### watch.templates 如果为真, Revel 监视模板变化,必要时重新加载他们。 默认值: true * * * #### watch.routes 如果为真, Revel 监视 `routes` 文件的变化,必要时重新加载。 默认值: true * * * #### watch.code 如果为真, Revel 监视Go代码改动,必要时重新编译代码(作为反向代理运行)。 `app/` 目录(包括子目录)下的代码都被监视。 默认值: true ### Cookies Revel 组件默认使用下面的 cookies: * REVEL_SESSION * REVEL_LANG * REVEL_FLASH * REVEL_ERRORS #### cookie.prefix Revel 使用这个属性作为 Revel-produced cookies前缀。这样可以在同一台主机上运行多个REVEL应用程序。 例如, ~~~ cookie.prefix = MY ~~~ 则对应的 cookie 名称如下: * MY_SESSION * MY_LANG * MY_FLASH * MY_ERRORS 默认值: “REVEL” ### Session #### session.expires Revel 使用这个属性设置session cookie的有效期。 Revel 使用 [ParseDuration](http://golang.org/pkg/time/#ParseDuration) 解析字符串,默认值是 30 天。也可以设置为会话结束时过期。 请注意,客户端的行为依赖于浏览器的设置,所以结果并不总是保证。 ### 模板 #### template.delimiters 指定模板左右分隔符 必须这样指定分隔符 “左分隔符 右分隔符” 默认值: “{{ }}” ### 格式化 #### format.date 指定默认的日期格式,Revel在两个地方使用它: * 绑定日期参数到 `time.Time` (参考 [binding](http://gorevel.cn/docs/manual/binding.html)) * 在模板中使用 `date` 模板函数输出日期 (参考 [模板函数](http://gorevel.cn/docs/manual/templates.html)) 默认值: “2006-01-02” * * * #### format.datetime 指定默认的日期时间格式,Revel在两个地方使用它: * 绑定日期参数到 `time.Time` (参考 [binding](http://gorevel.cn/docs/manual/binding.html)) * 在模板中使用 `datetime` 模板函数输出日期 (参考 [模板函数](http://gorevel.cn/docs/manual/templates.html)) 默认值: “2006-01-02 15:04” ### 数据库 #### db.import 指定DB模块的 database/sql 驱动程序导入路径。 默认值: ”” * * * #### db.driver 指定 database/sql 驱动程序名称 (在[`sql.Open`](http://golang.org/pkg/database/sql/#Open)中使用). 默认值: ”” * * * #### db.spec 指定 database/sql 数据源名称 (在 [`sql.Open`](http://golang.org/pkg/database/sql/#Open)中使用). 默认值: ”” ### 构建 #### build.tags [Build tags](http://golang.org/cmd/go/#Compile_packages_and_dependencies) 构建程序的时候使用。 默认值: ”” ### 日志 TODO ### 缓存 [cache](http://gorevel.cn/docs/manual/cache.html) 模块是一个简单的堆或分布式缓存接口 #### cache.expires 设置缓存过期时间。在程序中调用者使用常量`cache.DEFAULT`获取。 它是接受一个[`time.ParseDuration`](http://golang.org/pkg/time/#ParseDuration) 字符串。 (目前还不能指定默认值为 `FOREVER`) 默认值: “1h” (1 小时) * * * #### cache.memcached 如果为真, 缓存模块使用 [memcached](http://memcached.org/) 来代替内存缓存。 默认值: false * * * #### cache.hosts 一个逗号分隔的 memcached 主机列表。缓存条目使用确定的主机名缓存key自动分片到可用的主机中。主机可能会多次列出,以增加共享的缓存空间。 默认值: ”” ### 计划任务 [计划任务](http://gorevel.cn/docs/manual/jobs.html) 模块允许你运行计划任务或者临时任务 #### 时间表 时间表可以通过key来配置。 ~~~ cron.schedulename = @hourly ~~~ 时间表的计划时间可以在执行器中提交任务时使用。例如: ~~~ jobs.Schedule("cron.schedulename", job) ~~~ * * * #### jobs.pool 允许同时允许的任务数量。例如: ~~~ jobs.pool = 4 ~~~ 如果为 0, 则没有数量限制 默认值: 10 * * * #### jobs.selfconcurrent 如果为真, 允许一个任务运行,即使是该任务的实例仍在进行中。 默认值: false ### 模块 [模块](http://gorevel.cn/docs/manual/modules.html) 通过指定导入路径将模块添加到应用程序中。例如: ~~~ module.testrunner = github.com/revel/revel/modules/testrunner ~~~ ## 开发计划 * 允许使用命令行参数配置值或以其他方式在命令行中指定值。
';

参考

最后更新于:2022-04-01 04:22:34

';

部署

最后更新于:2022-04-01 04:22:32

## 概要 几种常见的部署方法如下: 1. 本地编译代码,然后复制到服务器上运行 2. 在服务器上拉取代码,然后编译、运行 3. 使用 Heroku 进行部署 使用命令行演示互动部署 - 一般将web服务器作为守护程序运行。常用工具有: * [Ubuntu Upstart](http://upstart.ubuntu.com/) * [systemd](http://www.freedesktop.org/wiki/Software/systemd) ## 本地编译 目标机器上不需要安装Go语言环境。[Revel命令行工具](http://gorevel.cn/docs/manual/tool.html) 提供了`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 ~~~ ## Heroku Revel 维护了一个 [Heroku Buildpack](https://github.com/revel/heroku-buildpack-go-revel), 允许一条命令即可部署代码,请参考 [自述文件](https://github.com/revel/heroku-buildpack-go-revel/blob/master/README.md) 使用说明 ## 交叉编译 为了创建一个交叉编译环境,我们需要从源代码构建。参考 [从源代码安装Go](http://golang.org/doc/install/source) 获取更多信息。你必须正确设置 $PATH 和 $GOPATH 环境变量, 否则,如果已经有一个二进制的Go语言包存在,你会陷入严重的错误。 当Go编译器安装成功后,通过指定 GOOS 和 GOARCH 目标环境来建立交叉编译环境。参考 [可用的环境变量](http://golang.org/doc/install/source#environment) 获取更多信息。 ~~~ $ 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 ~~~ 然后,将压缩包复制到目标平台。
';

日志

最后更新于:2022-04-01 04:22:30

Revel 支持四类日志信息: * TRACE - 调试信息 * INFO - 一般信息 * WARN - 警告信息 * ERROR - 错误信息 下面是在Revel中使用日志的例子: ~~~ now := time.Now() revel.TRACE.Printf("%s", now.String()) ~~~ 日志记录器默认使用 [go 日志](http://golang.org/pkg/log/). 日志记录器在 [app.conf](http://gorevel.cn/docs/manual/appconf.html)中配置。例如: ~~~ app.name = sampleapp [dev] log.trace.output = stdout log.info.output = stdout log.warn.output = stderr log.error.output = stderr log.trace.prefix = "TRACE " log.info.prefix = "INFO " log.trace.flags = 10 log.info.flags = 10 [prod] log.trace.output = off log.info.output = off log.warn.output = log/%(app.name)s.log log.error.output = log/%(app.name)s.log ~~~ 在**开发**环境中: * 显示详细日志 * **info** 或 **trace**信息以app.conf中定义的前缀显示信息 在**生产**环境中: * **info** 和 **trace** 日志将被忽略 * 警告 和 错误信息被写入 **log/sampleapp.log** 文件 根据 [标记常量](http://www.golang.org/pkg/log/#constants)修改日志格式,。例如, `01:23:23 /a/b/c/d.go:23 Message` 格式,使用标记 `Ltime | Llongfile = 2 | 8 = 10` 开发状态: * 如果日志目录log不存在,Revel 会自动创建日志目录。
';

版本

最后更新于:2022-04-01 04:22:28

# 版本控制 nathany上已经给出了Go包版本控制的许多信息。然而, 那时还没有一个包版本管理的社区标准。因此, 只能由开发者确保软件安全与可重复构建。 如果你使用Revel构建应用程序, 开发者应避免由于不兼容造成的问题。你的构建过程不应当使用go get获取Revel的主分支。 处理这种情况最简单的方法是签出Revel和所有依赖包到你的代码库中。如果你使用git, 可以把这些库作为你项目的子代码库。 或者, 试试下面链接中的软件包管理器。 * [Go包版本控制](http://nathany.com/go-packages/)
';

操作

最后更新于:2022-04-01 04:22:25

';

计划任务

最后更新于:2022-04-01 04:22:23

Revel 支持计划任务(异步执行), 运行在请求流程的外部。比如,更新缓存数据的周期性任务,或发送电子邮件的临时任务。 ## 激活 该框架是一个可选模块,默认是禁用的。要将它激活,需要在配置文件中添加该模块: ~~~ module.jobs = github.com/revel/revel/modules/jobs ~~~ 此外,为了访问计划任务的监控页面,需要将下面的内容添加到路由文件中: ~~~ module:jobs ~~~ 这条语句将插入 `/@jobs` 路由 ## 选项 有两个选项来限制计划任务。 这个例子显示了它们的默认值。 ~~~ jobs.pool = 10 # 允许同时运行的任务数 jobs.selfconcurrent = false # 一个任务只允许一个实例 ~~~ ## 启动计划任务 应用程序启动时, 使用[`revel.OnAppStart`](http://gorevel.cn/docs/docs/godoc/init.html#OnAppStart) 注册一个函数来运行一个任务。Revel 在服务启动之前,会连续启动这些任务。 请注意,此功能实际上并未使用计划任务模块,它被用来提交任务,但并不阻止服务器的启动。 ~~~ func init() { revel.OnAppStart(func() { jobs.Now(populateCache{}) }) } ~~~ ## 周期性任务 任务可以被指定在任意时间运行。使用的时间表有两个选项: 1. 一个cron时间格式 2. 固定时间间隔 Revel 使用 [cron library](https://github.com/revel/cron) 来解析时间表和任务。[cron库的说明](https://github.com/revel/cron/blob/master/README.md) 提供了时间格式的详细描述。 计划任务通常使用 [`revel.OnAppStart`](http://gorevel.cn/docs/docs/godoc/init.html#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**), 以及之前和下次运行时间。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-20_5625fc00ebef6.png) 为了安全起见,状态页面仅接受127.0.0.1发起的请求。 ## 限制池的大小 任务模块配置,用来限制同一时间运行的任务数量。这允许开发人员限制可能会潜在地使用异步任务的资源 - 通常是交互式的响应高于异步处理。当池中正在运行的任务已满,新的任务将阻塞,直到正在运行的任务完成。 注意事项:计划任务在一个channel上阻塞,为等待的goroutines实现了一个FIFO (但没有指定/需要这样). [参考这里的讨论](https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/CPwv8WlqKag). ## 开发状态 * 允许使用HTTP基本身份验证凭据访问任务状态页面 * 允许管理员从状态页面以交互方式执行预定任务 * 支持更多的页面属性,例如:池的大小,任务队列长度等。
';

测试

最后更新于:2022-04-01 04:22:21

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`的一个struct * `Before()` 、 `After()` 在每个测试方法之前和之后被调用,如果有的话。 * `revel.TestSuite` 帮助发出请求到你的应用程序,和对响应的断言。 * 如果一个断言失败,产生了恐慌,将被测试工具捕获。 你可以用两种方式运行这个测试: * 交互方式,Web浏览器,开发过程中非常有用。 * 非交互方式,命令行,对于持续构建整合有用。 ## 开发一个测试套件 要创建自己的测试套件,需要定义一个嵌入了`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{}) ~~~ [参考godoc](http://gorevel.cn/docs/docs/godoc/tests.html) 所有的请求方法类似: 1. 接受一个路径 (比如 `/users/`) 2. 向应用程序服务器发出一个请求 3. 存储 `Response` 中的成员 4. 读取完整的响应到`ResponseBody` 成员中 如果开发人员希望使用一个定制的HTTP客户端,而不是默认的[http.DefaultClient](http://golang.org/pkg/net/http/#pkg-variables),应当在`Before()` 方法之前替换它。 断言失败后,会抛出恐慌并被测试工具捕获,并将错误列出。 ## 运行测试套件 为了运行测试,`testrunner` 模块必须被激活。需要在 `app.conf`文件中配置: ~~~ module.testrunner = github.com/revel/revel/modules/testrunner ~~~ 您还必须导入测试模块的路由,在你的 `routes` 文件中加入下面的内容: ~~~ module:testrunner ~~~ 配置完后,测试就可以交互或非交互方式运行。 ### 运行交互式测试 要利用 Revel 的热编译功能,交互式测试运行提供了快速编辑刷新周期。 例如,开发人员从浏览器中访问 `/@tests`: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-20_5625fc0008afb.png) 然后,增加一个测试方法: ~~~ func (t AppTest) TestSomethingImportant() { t.Get("/") t.AssertOk() t.AssertContentType("text/xml") } ~~~ 然后,刷新浏览器,看看新的测试: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-20_5625fc0022c0a.png) 运行测试: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-20_5625fc0035a1c.png) 嗯哼,,,行不通哦,,,修改代码使用“text/html” 替换 “text/xml”类型。 ~~~ t.AssertContentType("text/html") ~~~ 然后,重新运行测试: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-10-20_5625fc005745a.png) 成功啦! ### 运行非交互式测试 Revel [命令行工具](http://gorevel.cn/docs/manual/tool.html) 提供了一个 `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 testrunner Open DB Listening on port 9000... INFO 2012/11/09 19:21:06 test.go:95: Testing Booking example (github.com/revel/revel/samples/booking) in dev mode Go to /@tests to run the tests. 1 test suite to run. AppTest PASSED 0s All 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-results test-results test-results/app.log test-results/AppTest.passed.html test-results/result.passed ~~~ 它写三点不同: 1. 应用程序的标准输出和标准错误重定向到 `app.log` 2. 每个测试套件有一个HTML文件被写入,说明测试通过或失败。 3. 无论 `result.passed` 或 `result.failed` 被写入, 这取决于整体的成功。 对于整合持续构建测试,有两点建议: 1. 检查返回码,0代表测试成功,否则为非0值。 2. 测试运行后要求存在 `result.success`, 或禁止 `result.failed`存在。 ## 注意事项 Revel 做了什么: * 扫描嵌入TestSuite类型 (transitively) 的源代码 * 在生成的 main.go 文件中,为 `revel.TestSuites` 类型的变量设置一个列表 * 按要求,使用反射来查找所有以“Test”开头的TestSuite类型的方法,并调用它们来运行测试。 * 从错误或失败的断言捕获恐慌,显示错误。 当 `testrunner` 模块激活后,测试代码才会被构建。 ## 开发计划 改进测试框架: * 固定存储测试数据。 * 日志写入到一个文件中(而不是 stderr / stdout)也应该被重定向到 `test-results/app.log`
';

概要

最后更新于:2022-04-01 04:22:19

# 模块 模块是一些包,可以集成到Revel程序中。Revel允许多个Revel程序(或第三方代码)共享控制器、模板、资源和其他代码。 模块中文件的布局应当与Revel应用程序文件结构一致。“托管”应用程序会按以下方式将它们合并: 1. module/app/views 的所有模板,会被添加到模板加载器的搜索路径中 2. module/app/controllers 的所有控制器, 将被视为你的应用程序中的控制器。 3. 资源文件通过 `Static.ServeModule("modulename","public")` 提供 4. 路由通过 `module:modulename` 被添加到你的程序中 ### 启用一个模块 为了将模块添加到您的应用程序,需要在`app.conf` 中添加一行配置: ~~~ module.mymodulename = go/import/path/to/module ~~~ 如果导入路径为空,将禁用模块: ~~~ module.mymodulename = ~~~ 举个栗子, 启用测试运行模块: ~~~ module.testrunner = github.com/revel/revel/modules/testrunner ~~~
';

模块

最后更新于:2022-04-01 04:22:16

';

Cache

最后更新于:2022-04-01 04:22:14

Revel 提供了一个服务器端、临时的、低延迟存储的缓存库。对于频繁访问数据库中缓慢变化的数据,使用缓存一个很好的方法,并且它也可以用于实现用户session (如果基于cookie的session不足). 参考 [缓存接口](http://godoc.org/github.com/revel/revel/cache#Cache) ## 过期时间 缓存有三种过期时间: * time.Duration,指定一个过期时间。 * `cache.DEFAULT`, 默认过期时间(1小时)。 * `cache.FOREVER`, 永不过期。 重要提示:调用者不能依赖于存在于缓存中内容,因为数据是不持久保存,重新启动后,会清除所有缓存数据。 ## 序列化 缓存读写接口自动为调用者序列化任何类型的的值。有以下几种方式: * 如果是 `[]byte`类型, 数据保持不变。 * 如果是 整数类型, 数据被保存为 ASCII。 * 其他类型,使用 [`encoding/gob`](http://golang.org/pkg/encoding/gob/)进行编码。 ## 实现 缓存通过以下几种方式进行配置: * 分布式[memcached](http://memcached.org/) 主机缓存 * 单独 [redis](http://redis.io/) 主机缓存 * 本地内存缓存 ## 配置 在`app.conf`中配置缓存: * `cache.expires` - 一个字符串,[`time.ParseDuration`](http://golang.org/pkg/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") } ~~~ ## Session 用法 缓存有一个全球性的key空间 - 使用它作为一个session存储,调用方应采用session UUID的优点,如下图所示: ~~~ cache.Set(c.Session.Id(), products) // 然后在随后的请求中使用它 err := cache.Get(c.Session.Id(), &products) ~~~
';

Messages

最后更新于:2022-04-01 04:22:12

Revel使用文本文件提供国际化翻译支持。Revel 支持语言翻译文件化, 自动区域查询, cookie重写、嵌套的消息与参数。 #### 词汇表 * Locale(语言环境): 包含 *语言* 和 *区域*两个部分,指示用户的语言偏好,例如 `en-US`。 * Language(语言): locale 的语言部分, 例如 `en`。 语言标识符请参考 [ISO 639-1 codes](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)。 * Region(区域): locale 的区域部分, 例如 `US`。 区域标识符请参考 [ISO 3166-1 alpha-2 codes](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)。 ## 示例程序 Revel 处理消息文件和国际化的方法与其他框架类似。如果你想立马上手, 可以参考示例程序 `revel/samples/i18n`,里面包含所有的基础知识。 ## 消息文件 国际化消息在消息文件中定义。这些消息在渲染视图模板(或程序的其他地方)时使用。当创建新的消息文件时,需要遵循几个规则: * 所有的消息文件应存放在应用程序根目录的`messages`目录中。 * 文件扩展名与 *language* 一致,并且应为 [ISO 639-1 code](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)。 * 消息文件应该是 UTF-8 编码。虽然不是强制规定, 最好还是遵循这个约定。 * 消息文件必须是 [goconfig 文件](https://github.com/revel/config) 支持多有的 goconfig 特性 ### 组织消息文件 消息文件的文件名​​没有任何限制; 只要扩展名合法就行。每种语言的文件数量也没有限制。当应用程序启动时,Revel会解析`messages`语言目录中所有的扩展名合法的消息文件,并根据语言将它们合并。这意味着你可以自由组织你的消息文件。 例如,您可能希望采取传统的方法,在一个文件中定义所有的: ~~~ /app /messages messages.en messages.fr ... ~~~ 或者是为每种语言创建多个文件,并根据分类组织他们: ~~~ /app /messages labels.en warnings.en labels.fr warnings.fr ... ~~~ **重要注意事项:** 在同一语言的多个文件中,虽然技术上可以定义相同的消息key,但是这将导致不可预知的行为。当每种语言定义了多个文件时,请注意保持您的key唯一,这样文件合并后,key将不会被覆盖! ### 消息 key 和 值 消息文件是一个 [goconfig 文件](https://github.com/revel/goconfig)。这意味着,消息应按照key-value格式定义: ~~~ key=value ~~~ 举个栗子: ~~~ greeting=Hello greeting.name=Rob greeting.suffix=, welcome to Revel! ~~~ ### 段 goconfig 文件被分成多个 *sections*. *默认的段* 总是存在, 并且包含未指定段时定义的消息。例如: ~~~ key=value [SECTION] key2=value2 ~~~ `key=value` 消息消息被隐式放置在默认段中,因为它没有定义在一个指定的段中。 消息文件的所有消息应在定义默认的段中,除非它们是用于某个特定区域(见[Regions](http://gorevel.cn/docs/manual/i18n-messages.html#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=Rob greeting.suffix=, welcome to Revel! greeting.full=%(greeting)s %(greeting.name)s%(greeting.suffix)s ~~~ **注意:** * 引用是 *goconfig* 的功能. * 因为消息文件可以合并,多以只要它们被定义为同一种语言,就可以引用其他文件中的消息。 #### 参数 消息支持一个或多个参数。在消息中参数使用与 `fmt` 包相同的规则去解析。 例如: ~~~ greeting.name_arg=Hello %s! ~~~ 参数按照给定的顺序进行解析,参考 [解析消息](http://gorevel.cn/docs/manual/i18n-messages.html#resolving_messages). ## 解析客户端语言环境(locale) 为了找出用户喜欢的语言环境,Revel会在以下地方查找一个可用的语言环境: 1. 语言 cookie 对于每次请求,框架会查找应用程序配置 (`i18n.cookie`)。如果找到了,它的值被假定为当前的语言环境。当一个cookie已经发现,所有其他解析方法将被跳过。 2. `Accept-Language` HTTP 头 对于每次请求,Revel 会自动解析 HTTP 头信息的 `Accept-Language`。其中的每个语言环境根据 [HTTP 规范](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4)在当前`Request`实例中获取和存储。此信息用来为稍后的消息解析函数确定当前语言环境。 更多信息请参考 [Parsed Accept-Language HTTP header](http://gorevel.cn/docs/manual/i18n-messages.html#parsed_acceptlanguage_http_header). 3. 默认语言 当以上所有方法都没有找到可用的客户端语言环境时,框架将使用配置文件中定义的默认语言`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 头信息 如果程序需要访问 `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 规范](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4). ## 解析消息 消息可以在 *控制器* 或 *模板*中解析。 ### 控制器 控制器中使用 `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` | 默认的语言环境,在没有首选区域的情况下使用。 |
';

Websockets

最后更新于:2022-04-01 04:22:09

Revel 提供[Websockets](http://en.wikipedia.org/wiki/WebSocket)支持。 处理一个 Websocket 连接: 1. 添加一个 `WS` 类型的路由。 2. 添加一个接受 `*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 { ... } ~~~
';

过滤器

最后更新于:2022-04-01 04:22:07

过滤器是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, // 调用控制器。 } } ~~~ 每个请求沿着过滤器链从上到下依次执行。 ### Per-Action configuration 尽管所有的请求都被发往过滤器链 `revel.Filters`, Revel 也提供了 [`过滤器配置`](http://gorevel.cn/docs/docs/godoc/filterconfig.html#FilterConfigurator), 允许开发者根据操作或控制器添加、插入、删除过滤器。 此功能通过 `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:]) // 执行下一个过滤器 } ~~~ 注意:这种模式往往说明[拦截器](http://gorevel.cn/docs/manual/interceptors.html)可能是实现所需功能的好的机制的一个指标。
';

拦截器

最后更新于:2022-04-01 04:22:05

“拦截器”是框架执行一个方法之前或之后被调用的函数。它允许 [面向方面编程](http://en.wikipedia.org/wiki/Aspect-oriented_programming), 作用如下: * 请求记录 * 错误处理 * 状态保持 在 Revel 中, 有两种形式的拦截器: 1. 函数拦截器:请参考 [`InterceptorFunc`](http://gorevel.cn/docs/docs/godoc/intercept.html#InterceptorFunc) 接口. * 不能挂接到某个特定的控制器方法 * 可以应用到所有的、任意的控制器上 2. 方法拦截器:一个不带参数、并返回一个 `revel.Result`的控制器方法 * 只能拦截控制器方法 * 可以修改被调用的控制器 拦截器的执行顺序与添加位置相关 ## 拦截时间 在一个请求生命周期内,可以注册四种拦截时间: 1. BEFORE: 在请求被路由到以后,并且session, flash, 参数解析之后、控制器方法被调用之前执行拦截。 2. AFTER: 在请求返回了一个结果, 但是结果被应用之前执行拦截。如果出现了panic,拦截不会被调用。 3. PANIC: 在控制器方法中或应用结果时出现panic退出后被拦截。 4. FINALLY: 在控制器方法执行完毕并且结果被应用之后被拦截。 ## 结果 拦截器通常返回 `nil`, 在这种情况下,需要继续处理请求,不能中断。 返回一个非 `nil` `revel.Result`的效果, 取决于拦截器被调用的时间: 1. BEFORE: 没有进一步的拦截器被调用, 也不是一个控制器方法。 2. AFTER: 所有拦截器仍然可以运行。 3. PANIC: 所有拦截器仍然可以运行。 4. FINALLY: 所有拦截器仍然可以运行。 在任何情况下,返回的结果都将附加到任何现有的结果上: 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.Result func (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) } ~~~
';

模板

最后更新于:2022-04-01 04:22:02

Revel 使用 [Go 模板](http://www.golang.org/pkg/text/template/), 在下面两个目录中查找模板: * 应用程序的 `views` 目录 (包括所有的子目录) * Revel的 `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 也提供一些有用的数据: * “errors” - 验证错误(map,请参考文档 [`Validation.ErrorMap`](http://gorevel.cn/docs/docs/godoc/validation.html#Validation.ErrorMap) * “flash” - 上个请求flash的数据 ## 模板功能 Go 提供了一些 [模板函数](http://www.golang.org/pkg/text/template/#Functions)。Revel 也增加了一些模板函数。请阅读下面的文档 或 [查看源代码](http://gorevel.cn/docs/docs/godoc/template.html#variables). ### eq 一个简单的 “a == b” 测试. 例如: ~~~ <div class="message {{if eq .User "you"}}you{{end}}"> ~~~ ### set 在当前模板上下文中设置一个变量 例如: ~~~ {{set . "title" "Basic Chat room"}} <h1>{{.title}}</h1> ~~~ ### append 添加变量到一个数组中, 或者在模板上下文中创建一个数组 例如: ~~~ {{append . "moreScripts" "js/jquery-ui-1.7.2.custom.min.js"}} {{range .moreStyles}} <link rel="stylesheet" type="text/css" href="/public/{{.}}"> {{end}} ~~~ ### field input 字段辅助函数. 给出一个字段名, 函数会生成包含下面成员的结构: * Id: 字段Id, 转换为一个HTML字段的ID。 * Name: 字段名称 * Value: 当前上下文中字段的值 * Flash: 带回的字段值 * Error: 字段错误消息(如果有错误) * ErrorClass: 原始的字符串 “hasError”, 如果没有错误就是一个 ””. [浏览 godoc.](http://gorevel.cn/docs/docs/godoc/field.html) 例如: ~~~ {{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}} ~~~ ### option 使用辅助函数生成 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}} ~~~ ### radio 使用辅助函数生成 HTML radio `input` 字段 例如: ~~~ {{with $field := field "booking.Smoking" .}} {{radio $field "true"}} Smoking {{radio $field "false"}} Non smoking {{end}} ~~~ ### nl2br 将换行符转换成 HTML 的 break. 例如: ~~~ You said: <div class="comment">{{nl2br .commentText}}</div> ~~~ ### pluralize 一个辅助的复数函数 例如: ~~~ There are {{.numComments}} comment{{pluralize (len comments) "" "s"}} ~~~ ### raw 输出原生的、未转义的文本 例如: ~~~ <div class="body">{{raw .blogBody}}</div> ~~~ ## Including 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="/public/js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script> <script src="/public/js/sessvars.js" type="text/javascript" charset="utf-8"></script> {{range .moreScripts}} <script src="/public/{{.}}" 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 } } ~~~
';

Results

最后更新于:2022-04-01 04:22:00

控制器方法必须返回一个[`revel.Result`](http://gorevel.cn/docs/docs/godoc/results.html#Result), 用来处理响应结果,其接口定义如下: ~~~ type Result interface { Apply(req *Request, resp *Response) } ~~~ [`revel.Controller`](http://gorevel.cn/docs/docs/godoc/controller.html#Controller) 使用以下方法来处理响应结果: * Render, RenderTemplate - 渲染模板, 传送参数. * RenderJson, RenderXml - 序列化结构体到 json 或 xml. * RenderText - 返回一个明文响应. * Redirect - 重定向到一个控制器方法 或 URL * RenderFile - 返回一个文件, 一般用于响应文件下载. * RenderError - 渲染 errors/500.html 模板来返回一个 500 错误. * NotFound - 渲染 errors/404.html 模板来返回一个 404 错误. * Todo - 返回一个存根响应(500) 此外,开发者可以自定义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`](http://gorevel.cn/docs/docs/godoc/controller.html#Controller.Render) 做了两件事: 1. 添加参数到控制器的 RenderArgs 中, 使用变量的名字作为字典的key. 2. 使用传送的参数渲染模板 “views/Controller/Action.html”。 如果不成功 (比如,没有找到模板), 将返回 ErrorResult. 这允许开发人员编写: ~~~ func (c MyApp) Action() revel.Result { myValue := calculateValue() return c.Render(myValue) } ~~~ 在模板中引用变量 “myValue”。这通常比构建一个明确的map更方便,因为在许多情况下,数据将被作为局部变量处理。 **注意:** Revel 通过调用的控制器方法名来确定使用哪个模板,查找参数名。因此, c.Render() 只可称为由操作。 ## RenderJson / RenderXml 应用程序可以调用 [`RenderJson`](http://gorevel.cn/docs/docs/godoc/controller.html#Controller.RenderJson) 或 [`RenderXml`](http://gorevel.cn/docs/docs/godoc/controller.html#Controller.RenderXml) 并传送任意类型的变量 (通常是一个 struct). Revel 会使用 [`json.Marshal`](http://www.golang.org/pkg/encoding/json/#Marshal) or[`xml.Marshal`](http://www.golang.org/pkg/encoding/xml/#Marshal)对变量进行序列化操作. 如果 `app.conf` 配置文件中 `results.pretty=true`, 将使用 `MarshalIndent` 进行序列化, 生成漂亮的缩进,方便使用。 ## 重定向 一个辅助函数是用于生成重定向。它有两种方式使用: 1. 重定向到一个控制器方法(不带参数): ~~~ return c.Redirect(Hotels.Settings) ~~~ 这种形式是非常有用的,因为它提供了一定程度的路由类型安全性和独立性(自动生成URL)。 1. 重定向到一个格式化字符串: ~~~ return c.Redirect("/hotels/%d/settings", hotelId) ~~~ 通常用来传送参数. 它返回 302 (临时重定向) 状态码. ## 添加你自己的 Result 下面是一个添加简单结果的例子。 创建此类型: ~~~ type Html string func (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() } ~~~
';

Session / Flash

最后更新于:2022-04-01 04:21:58

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/Get type Flash struct { Data, Out map[string]string } ~~~ ## Session Revel的 “session” 是一个加密签名存储的字符串 map。 影响如下: * 大小不超过 4kb。 * 所有数据被保存为一个序列化的字符串。 * 用户可以查看、修改所有数据 (未加密)。 session cookie 的默认过期时间是浏览器关闭。 可以在app.config修改session.expires配置来指定一个有效期时间。格式是[time.ParseDuration](http://golang.org/pkg/time/#ParseDuration). ## Flash Flash 支持单独使用字符串存储。这对于实现 [Post/Redirect/Get 模式](http://en.wikipedia.org/wiki/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) } ~~~ 例子主要功能如下: 1. 用户获取设置页面。 2. 用户提交一个表单 (POST)。 3. 应用程序处理用户请求,保存错误或成功的消息到flash中,然后重定向设置页面 (REDIRECT)。 4. 用户获取设置页面, 显示flash中的消息。 (GET) 主要使用了两个函数: 1. `Flash.Success(message string)` 是 `Flash.Out["success"] = message`简写方式 2. `Flash.Error(message string)` 是 `Flash.Out["error"] = message`简写方式 在模板中使用key来获取Flash 消息。例如, 通过简写函数获取成功和错误消息: ~~~ {{.flash.success}} {{.flash.error}} ~~~
';