Controlling complexity is the essence of computer programming. — Brian Kernighan
HTTP通讯协议是1种Request-Response (要求-回应)的流程,客户端(通常是阅读器)向伺服器送出1个HTTP
request封包,然后伺服器就回应1个response封包。在上1章中,我们介绍了Rails如何使用路由来分派request到Controller的其中1个Action。而每一个Action的任务就是根据客户端传来的资料与Model互动,然后回应结果给客户端。这1章中我们将仔细介绍负责回应要求的Controller。
ApplicationController
透过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
CSRF 网路攻击http://en.wikipedia.org/wiki/Cross-site_request_forgery
注意,请将方法放在protected或private之下,如果是public方法,就会变成1个公然的Action可以给阅读器呼唤到。
产生Controller与Action
我们在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
除 ??继承自ApplicationController ,我们也能够继承更底层的ActionController::Metal ,请参考Rails3:新的Metal机制。
在Action方法中我们要处理request,基本上会做3件事情:
1.搜集request的资讯,例如使用者传进来的参数2.操作Model来做资料的处理3.回传response结果,这个动作称作render
Request资讯搜集
Controller的Action当中,Rails提供了1些方法可让你得知此request各种资讯,包括:
-
action_name目前的Action名称
-
cookies Cookie下述
-
headers HTTP标头
-
params包括用户所有传进来的参数Hash,这是最常使用的资讯
-
request各种关于此request的详细资讯,较经常使用的例如:
-
xml_http_request? 或xhr?,这个方法可以知道是否是Ajax 要求
-
host_with_port
-
remote_ip
-
headers
-
response代表要回传的内容,会由Rails设定好。通常你会用到的时机是你想加特别的Response
Header。
-
session Session下述
正确的说,params这个Hash是ActiveSupport::HashWithIndifferentAccess 物件,而不是普通的Hash而已。Ruby内建的Hash,用Symbol的hash[:foo] 和用字串的hash["foo"] 是不1样的,这在混用的时候常常弄错而取不到值,算是常见的臭虫来源。Rails在这里使用的ActiveSupport::HashWithIndifferentAccess 物件,不管键是Symbol或字串,都指涉相同的值,减少麻烦。
Render结果
在根据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指定Template,例如render
:template => "index" 或可以省略成render "index" ,如果是不同Template再加上Controller名称,例如render
"events/index" 。-
:action 指定同1个Controller中另外一个Action的Template (注意到只是使用它的Template,而不会履行该Action内的程式)
其他参数
:status设定HTTP
status,预设是200,也就是正常。其他经常使用代码包括401权限不足、404找不到页面、500伺服器毛病等。-
:layout 可以指定这个Layout,设成false即关掉Layout
补充1提,在特定情况你想把render 的结果存成1个字串,例如拿到局部样板Partials成为1个字串,这时候候可以改使用render_to_string
:partial => "foobar"
Redirect
如果Action不要render任何结果,而是要使用者转向到别页,可使用redirect_to
redirect_to events_url-
redirect_to :back 回到上1页。
注意,1个Action中只能有1个render 或1个redirect_to 。不然你会得到1个DoubleRenderError 例外毛病。
串流Sending data
如果需要回传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是档案路径和档名:-
不过实务上我们很少在上线环境上直接用Rails来推送静态档案,由于大档的传输时间会浪费宝贵的Rails运算资源。我们会改用X-Sendfile
Header将传档的任务委派给网页伺服器(例如Apache或Nginx )处理,来下降Rails伺服器的负担。或是搭配第3方云贮存服务例如AWS
S3将传档的任务外包出去。
respond_to
我们在第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
Sessions
HTTP是1种无状态的通讯协议,为了能够让阅读器能够在跨request之间记住资讯,Rails提供了Session功能,像是记住登入的状态、记住使用者购物车的内容等等,都是用Session实作出来的。
要操作Session,直接操作session 这个Hash变数便可。例如:
session[:cart_id] = @cart.id
Session原理可以参考Session_ID,基本上也是利用阅读器的cookie来追踪requests要求。
Session storage
Rails预设采取Cookies
session storage来贮存Session资料,它是将Session资料透过config/secrets.yml的secret_key_base 编码后放到阅读器的Cookie当中,最大的好处是对伺服器的效能负担很低,缺点是大小最多4Kb,和资料还是可以透过反编码后看出来,只是没法进行修改。因此安全性较低,不合适寄存机密资料。
除??Cookies session storage,Rails也支援其他方式,你可以修改config/initializers/session_store.rb:
使用资料库来贮存 -
: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
Cookies
??Session,我们也能够直接操作底层的 (编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|