创建响应
从控制器的角度,创建HTTP响应有三种方法:
调用
render
方法调用
redirect_to
方法调用
head
方法,向浏览器发送只含HTTP首部的响应
一个控制器:
class BooksController < ApplicationController
def index
@books = Book.all
end
end
基于“多约定,少配置”原则,在 index
动作末尾并没有指定要渲染的视图,Rails会自动在控制器的视图文件夹中寻找 action_name.html.erb
模板,然后渲染。这里渲染的就是 app/views/books/index.html.erb
使用render方法
render
方法的行为有多种定制方式,可以渲染Rails模板的默认视图、指定的模板、文件、行间代码或者什么也不渲染。渲染的内容可以是 文本 、JSON 或者 XML,而且可以设置响应的内容类型和HTTP状态码。
渲染同个控制器的其他模板
def update
@book = Book.find(params[:id])
if @book.update(book_params)
redirect_to @book
else
render "edit"
end
end
不想用字符串,也可以使用符号:
render :edit
渲染其他控制器的动作
使用 render
方法,指定模板的完整路径(相对于 app/views
)即可。
render "products/show"
为了代码意图更加明显,还可以使用 :template
选项:
render template: "products/show"
渲染任意文件
render file: "/u/apps/warehouse_app/current/app/views/products/show"
想要渲染 views/books
下的 edit.html.erb 模板,以下方法都行:
render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"
渲染纯文本
使用 :plain
选项,可以把没有标记语言的纯文本发给浏览器,这主要用于响应Ajax或无需使用HTML的网络服务。
render plain: "OK"
渲染HTML
使用 :html
选项可以把HTML字符串发送给浏览器:
render html: "<p>hello, world</p>".html_safe
如果没调用 html_safe
方法,HTML实体会转义
渲染JSON
render json: @product
在需要渲染的对象上无需调用 to_json
方法,使用了 :json
选项,render
方法会自动调用 to_json
。
渲染XML
render xml: @product
在需要渲染的对象上无需调用 to_xml
方法,使用了 :xml
选项,render
方法会自动调用 to_xml
。
渲染javascript
render js: "alert('hello, rails')"
此时发送给浏览器的字符串,其MIME类型就是 text/javascript
渲染原始的主体
render body: "raw"
这时候返回的类型是 text/html
,只有在不在意内容类型的时候才应该使用这个选项。大多数时候,使用 :plain
或 :html
选项更加合适。
render 方法的其它选项
render
方法一般还可接受其他5个选项:
:content_type
:layout
:location
:status
:formats
:content_type选项
默认情况下,Rails渲染得到的结果内容类型为 text/html
,如果使用 :json
选项,内容类型为 application/json
,如果使用 :xml
选项,则内容类型为 application/xml
,如果需要修改内容类型,可使用 :content_type
选项:
render file: filename, content_type: "application/rss"
:layout 选项
render
方法大部分渲染得到的结果都会作为当前布局的一部分显示,:layout
选项指定使用特定的文件作为布局:
render layout: "special_layout"
当设置为 false
时,则说明不使用布局:
render layout: false
:location选项
用于设置HTTP的location首部:
render xml: photo, location: photo_url(photo)
:status选项
设定HTTP状态码,(在大多数情况下都是200),可以使用HTTP状态码,也可以使用状态码含义设定。
render status: 500
render status: :forbidden
:formats选项
改变格式,值可以是一个符号或者一个数组,默认使用 :html
:
render formats: :xml
render formats: [:json, :xml]
查找布局
查找布局时,首先在文件夹 app/views/layouts
文件夹中是否有和控制器同名的文件。例如,渲染 PhotosController
中的动作会使用 app/views/layouts/photo.html.erb
或者 app/views/layouts/photos.builder
。如果没有针对控制器的布局,Rails会使用 app/views/layouts/application.html.erb
或 app/views/layouts/application.builder
。如果没有 .erb
布局,Rails会使用 .builder
布局。
指定控制器的布局
在控制器中使用 layout 声明,可以覆盖默认使用的布局约定:
class ProductsController < ApplicationController
layout "inventory"
end
若要指定整个应用使用的布局,可以在ApplicationController类中使用layout声明:
class ApplicationController < ActionController::Base
layout "main"
end
在运行时选择布局
使用符号把布局延后到处理请求时再选择:
class ProductsController < ApplicationController
layout :products_layout
def show
@product = Product.find(params[:id])
end
private
def products_layout
@current_user.special? ? "special" : "products"
end
现在,如果用户是特殊用户,会使用一个特殊的布局渲染。
根据条件设定布局
使用 :only
和 :except
选项,可以设定条件
class ProductsController < ApplicationController
layout "product", except: [:index, :rss]
end
使用 redirect_to 方法
redirect_to
方法告诉浏览器向另一个URL发起新请求:
redirect_to photos_url
可以使用 redirect_back
把用户带回他们之前所在的页面,页面地址从 http_referer
中获取,不过浏览器不一定会设定,所以需要设定 fallback_location
redirect_back(fallback_location: root_path)
默认 redirect_to
方法把HTTP状态码设为302,如果想要设定其他状态码,可以使用 :status
选项:
redirect_to photos_path, status: 301
使用head方法
head
方法只把首部发送给浏览器,参数是HTTP状态码数字,或者符号形式,选项是一个散列,指定首部的名称和对应的值
head :bad_request
head :created, location: photo_path(@photo)
布局的结构
静态资源标签辅助方法
aotu_discovery_link_tag
javascript_include_tag
stylesheet_link_tag
image_tag
video_tag
audio_tag
aotu_discovery_link_tag 链接到订阅源
<%= auto_discovery_link_tag(:rss, {action: "feed"}, {title: "RSS Feed"}) %>
javascript_include_tag
Rails应用的javascript文件可以存放在三个位置: app/assets
、lib/assets
、vendor/assets
。文件的地址可使用相对文档根目录的完整路径或URL。例如,如果想链接到 app/assets、lib/assets 或 vendor/assets 文件夹中名为 javascripts 的子文件夹中的文件,可以这么做:
<%= javascript_include_tag "main" %>
Rails生成的script标签如下:
<script src="/assets/main.js"></script>
同时引入多个文件:
<%= javascript_include_tag "main", "columns" %>
引入外部文件:
<%= javascript_include_tag "http://example.com/main.js" %>
stylesheet_link_tag
类似于 javascript_include_tag
<%= stylesheet_link_tag "main" %>
<%= stylesheet_link_tag "main", "column" %>
默认情况下, stylesheet_link_tag
创建的链接属性为 media="screen" rel="stylesheet",指定相应的选项可以覆盖默认值:
<%= stylesheet_link_tag "main_print", media: "print" %>
image_tag
生成img标签,默认从 public/images
文件夹中加载文件:
<%= image_tag "header.png" %>
文件名必须指定图像的拓展名
同样可以通过散列指定HTML属性,另外如果没有 alt
属性,
Rails会使用图片的首字母大写的文件名(去掉拓展名)。
<%= image_tag "home.gif" %>
<%= image_tag "home.gif", alt: "Home" %>
video_tag
生成 <video>
标签,默认从 public/vedios
文件夹中加载文件。
<%= video_tag "movie.ogg" %>
生成
<video src="/videos/movie.ogg" />
同样也支持散列指定HTML属性。
把数组传递给 video_tag
方法可以指定多个视频
<%= video ["trailer.ogg", "movie.ogg"] %>
生成
<video>
<source src="trailer.ogg" />
<source src="movie.ogg" />
</video>
audio_tag
生成 <audio>
标签,默认从 public/audio
文件夹中加载
<%= audio_tag "music.mp3" %>
yield
在布局中,yield
标明一个区域,渲染的视图会插在这里,最简单的情况是只有一个 yield
,此时渲染的整个视图都会插入在这个区域:
<html>
<head></head>
<body>
<%= yield %>
</body>
</html>
表明多个区域:
<html>
<head>
<%= yield %>
</head>
<body>
<%= yield %>
</body>
</html>
视图的主体会插入未命名的yield区域,若想在具名yield中插入内容,可以使用 content_for
方法。
<% content_for :head do %>
<title>A simple page</title>
<% end %>
<p>Hello, World!</p>
套入布局后生成:
<html>
<head>
<title>A simple page</title>
</head>
<body>
<p>Hello, World!</p>
</body>
</html>
如果不同区域需要不同的内容(sidebar、footer等),就可以使用 content_for
方法。
使用局部视图
<%= render "menu" %>
这会渲染名为 _menu.html.erb
的文件,局部视图的文件名都是以下划线开头的,以便和普通视图区分开,引用时无需加入下划线。
局部布局
与视图使用布局一样,局部视图也可以使用布局
<%= render partial: "link_area", layout: "graybar" %>
这里会使用 _graybar.html.erb
布局渲染局部视图 _link_area.html.erb
,此时局部布局与局部视图保存在同一个文件夹中。
传递局部变量
局部变量可以传入局部视图,这样可以使得局部视图更加强大、更加灵活。
new.html.erb
<h1>New zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>
edit.html.erb
<h1>Editing zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>
_form.html.erb
<%= form_for(zone) do |f| %>
<p>
<b>Zone name</b><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
每个局部视图中都有一个和局部视图同名的局部变量,通过object选项可以把这个对象传给这个变量:
<%= render partial: "customer", object: @new_customer %>
如果要在局部视图中渲染模型实例,可以使用简写:
<%= render @customer %>
如果要在局部视图中自定义局部变量的名字,可以使用 :as
选项指定:
<%= render partial: "product", collection: @products, as: :item %>