HTTP通讯协议是1种Request-Response (要求-回应)的流程,客户端(通常是阅读器)向伺服器送出1个HTTP request封包,然后伺服器就回应1个response封包。在上1章中,我们介绍了Rails如何使用路由来分派request到Controller的其中1个Action。而每一个Action的任务就是根据客户端传来的资料与Model互动,然后回应结果给客户端。这1章中我们将仔细介绍负责回应要求的Controller。
透过rails g controller
指令产生出来的controller都会继承自ApplicationController
。因此定义在这里的方法可以被所有Controller取用,你可以在这边定义1些共用的方法。预设的application_controller.rb长的以下:
class ApplicationController < ActionController::Base
protect_from_forgery
end
其中的protect_from_forgery
方法启动了CSRF安全性功能,所有非GET的HTTP
request都必须带有1个Token参数才能存取,Rails会自动在所有表单中帮你插入Token参数,预设的Layout中也有1行<%
= csrf_meta_tag %>
标签可让JavaScript读取到这个Token。
但是当需要开放API给非阅读器客户端时,例如手机端或第3方利用的回呼(webhook),这时候候我们会需要关闭这个功能,例如:
class ApisController < ApplicationController
skip_before_action :verify_authenticity_token # 全部ApisController 关闭检查
end
我们在Part1示范过,要产生1个Controller档案,请输入
rails g controller events
如此便会产生app/controllers/events_controller.rb,依照RESTful设计的惯例,所有的Controller命名都是复数,而档案名称依照惯例都是{name}_controller.rb。
1个Action就是Controller里的1个Public方法:
class EventsController < ApplicationController
def show
# ...
end
end
在Action方法中我们要处理request,基本上会做3件事情: 1.搜集request的资讯,例如使用者传进来的参数2.操作Model来做资料的处理3.回传response结果,这个动作称作render
在Controller的Action当中,Rails提供了1些方法可让你得知此request各种资讯,包括:
在根据request资讯做好资料处理以后,我们接下来就要回传结果给用户。事实上,就算你甚么都不处理,Action方法里面空空如也,乃至不定义Action,Rails预设也还是会履行render方法。这个render方法会回传预设的Template,依照Rails惯例就是app/views/{controller_name}/{action_name}。如果找不到样板档案的话,会出现Template is missing的毛病。
固然,有时候我们会需要自定render,或许是指定不同的Template,或许是不需要Template。这时候候有以下参数可使用:
render :text => "Hello"
直接回传字串内容,不使用任何样板。render :xml => @event.to_xml
回传XML格式render :json => @event.to_json
回传JSON格式(再加上:callback
就会是JSONP )render :nothing => true
空空如也:template
指定Template,例如render
:template => "index"
或可以省略成render "index"
,如果是不同Controller的Template再加上Controller名称,例如render
"events/index"
。:action
指定同1个Controller中另外一个Action的Template (注意到只是使用它的Template,而不会履行该Action内的程式):status
设定HTTP
status,预设是200,也就是正常。其他经常使用代码包括401权限不足、404找不到页面、500伺服器毛病等。:layout
可以指定这个Action的Layout,设成false即关掉Layout
补充1提,在特定情况你想把render
的结果存成1个字串,例如拿到局部样板Partials成为1个字串,这时候候可以改使用render_to_string
:partial => "foobar"
如果Action不要render任何结果,而是要使用者转向到别页,可使用redirect_to
redirect_to events_url
redirect_to :back
回到上1页。如果需要回传2进位Binary资料,有两个方法可使用:
send_data(data, options={})
回传2进位字串,接受以下参数:
data
参数是2进位的字串::filename
使用者贮存下来的档案名称:type
预设是application/octet-stream:disposition
inline或attachment:status
预设是200
send_file(file_location, options={})
回传1个档案,接受以下参数:
file_location
是档案路径和档名::filename
使用者贮存下来的档案名称:type
预设是application/octet-stream:disposition
inline或attachment:status
预设是200不过实务上我们很少在上线环境上直接用Rails来推送静态档案,由于大档的传输时间会浪费宝贵的Rails运算资源。我们会改用X-Sendfile Header将传档的任务委派给网页伺服器(例如Apache或Nginx )处理,来下降Rails伺服器的负担。或是搭配第3方云贮存服务例如AWS S3将传档的任务外包出去。
我们在第6章RESTful利用程式中曾示范过用法,respond_to
可以用来回应不同的资料格式。Rails内建支援格式包括有:html,
:text, :js, :css, :ics, :csv, :xml, :rss, :atom, :yaml, :json
等。如果需要扩充,可以编辑config/initializers/mime_types.rb这个档案。
如果你想要设定1个else的情况,你可以用:any
:
respond_to do |format|
format.html
format.xml { render :xml => @event.to_xml }
format.any { render :text => "WTF" }
end
另外,Rails也支援单行的简单写法:
respond_to :html, :json, :js
这样其实就是:
respond_to do |format|
format.html
format.json
format.js
end
HTTP是1种无状态的通讯协议,为了能够让阅读器能够在跨request之间记住资讯,Rails提供了Session功能,像是记住登入的状态、记住使用者购物车的内容等等,都是用Session实作出来的。
要操作Session,直接操作session
这个Hash变数便可。例如:
session[:cart_id] = @cart.id
Rails预设采取Cookies
session storage来贮存Session资料,它是将Session资料透过config/secrets.yml的secret_key_base
编码后放到阅读器的Cookie当中,最大的好处是对伺服器的效能负担很低,缺点是大小最多4Kb,和资料还是可以透过反编码后看出来,只是没法进行修改。因此安全性较低,不合适寄存机密资料。
除Cookies session storage,Rails也支援其他方式,你可以修改config/initializers/session_store.rb:
:active_record_store
使用资料库来贮存:mem_cache_store
使用Memcached快取系统来贮存,合适高流量的网站1般来讲使用预设的Cookies session storage便可,如果对安全性较高要求,可使用资料库。如果希望统筹效能,可以斟酌使用Memcached。
采取:active_record_store
的话,必须安装activerecord-session_store
gem,然后产生sessions资料表:
$ rails g active_record:session_migration
$ rake db:migrate
除Session,我们也能够直接操作底层的