这篇博客是我们 《Rails 5 系列文章》 中的一篇。
Rails 的「请求 - 响应」周期很好理解。请求击中应用,传递到路由 route.rb
,路由匹配相应的控制器动作,最终由控制器动作处理请求,并基于请求类型渲染 HTML 或 JSON。然而有时候我们想要在这个「请求 - 响应」周期之外去渲染我们的 HTML 或 JSON。比如用户要下载一个网页报告的 PDF 版本,通过「请求 - 相应」周期是能做到的。但要是我们还需发送一个周报给经理并将其作为邮件的附件来发送时,我们就需要生成相同的 PDF,却由于发送邮件是后台任务因此就落到了「请求 - 相应」周期之外了。
Rails 5 提供了该特性。
比如我们有一个 OrderController
且需要在其外部渲染独立的订单。
启动 rails console
执行如下命令:
OrdersController.render :show, assigns: { order: Order.last }
这将渲染 app/views/orders/show.html.erb
并带上值为 Order.last
的 @order
变量。可以通过 assigns
来设置实例变量,就跟在控制器动作中设置它们一样。那些实例变量将被传入到即将被渲染的视图中去。
渲染局部视图也可以:
OrdersController.render :_form, locals: { order: Order.last }
这将渲染 app/views/orders/_form.html.erb
且将传入本地变量 order
。
再如我要渲染所有订单为 JSON 格式:
OrdersController.render json: Order.all
# => "[{"id":1, "name":"The Well-Grounded Rubyist", "author":"David A. Black"},
{"id":2, "name":"Remote: Office not required", "author":"David & Jason"}]
甚至渲染普通文本也行:
>> BooksController.render plain: 'this is awesome!'
Rendered text template (0.0ms)
# => "this is awesome!"
类似 text
,我们还可以使用 render file
和 render template
。
典型的 web 请求会带上它自己的环境,通常我们在控制器中使用 request.env
来处理该环境。某些像 devise
的包依靠 env
哈希来获取诸如 warden token 的信息。
Rails 为此提供了一个默认的 rack 环境,通过 renderer.defaults
可访问它的默认参数。
>> OrdersController.renderer.defaults
=> {:http_host=>"example.org", :https=>false, :method=>"get", :script_name=>"", :input=>""}
从内部说,Rails 将基于以上参数创建一个新 Rack 环境。
我们可以使用 renderer
的方法来自定义环境。比如我们需要「POST 方式」和 要求「HTTPS」作为后台任务处理。
renderer = ApplicationController.renderer.new(method: 'post', https: true)
# => #<ActionController::Renderer:0x007fdf34453f10 @controller=ApplicationController, @defaults={:http_host=>"example.org", :https=>false, :method=>"get", :script_name=>"", :input=>""}, @env={"HTTP_HOST"=>"example.org", "HTTPS"=>"on", "REQUEST_METHOD"=>"POST", "SCRIPT_NAME"=>"", "rack.input"=>""}>
现在我们拥有了自定义的 renderer
用来生成视图:
renderer.render template: 'show', locals: { order: Order.last }
总的来说这个是不错的特性,尤其可复用于现有代码。
作者 Prathamesh Sonpatki 于 2016.1.8