程序员人生 网站导航

[Ruby On Rails] Action Controller - 控制HTTP 流程

栏目:php教程时间:2016-06-17 08:12:07

Controlling complexity is the essence of computer programming. — Brian Kernighan

HTTP通讯协议是1种Request-Response (要求-回应)的流程,客户端(通常是阅读器)向伺服器送出1个HTTP request封包,然后伺服器就回应1个response封包。在上1章中,我们介绍了Rails如何使用路由来分派requestController的其中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安全性功能,所有非GETHTTP 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

注意,请将方法放在protectedprivate之下,如果是public方法,就会变成1个公然的Action可以给阅读器呼唤到。

产生ControllerAction

我们在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资讯搜集

ControllerAction当中,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这个HashActiveSupport::HashWithIndifferentAccess物件,而不是普通的Hash而已。Ruby内建的Hash,用Symbolhash[:foo]和用字串的hash["foo"]是不1样的,这在混用的时候常常弄错而取不到值,算是常见的臭虫来源。Rails在这里使用的ActiveSupport::HashWithIndifferentAccess物件,不管键是Symbol或字串,都指涉相同的值,减少麻烦。

Render结果

在根据request资讯做好资料处理以后,我们接下来就要回传结果给用户。事实上,就算你甚么都不处理,Action方法里面空空如也,乃至不定义ActionRails预设也还是会履行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",如果是不同ControllerTemplate再加上Controller名称,例如render "events/index"
  • :action指定同1个Controller中另外一个ActionTemplate (注意到只是使用它的Template,而不会履行该Action内的程式)

其他参数

  • :status设定HTTP status,预设是200,也就是正常。其他经常使用代码包括401权限不足、404找不到页面、500伺服器毛病等。
  • :layout可以指定这个ActionLayout,设成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 inlineattachment
  • :status预设是200

send_file(file_location, options={})回传1个档案,接受以下参数:

  • 其中file_location是档案路径和档名:
  • :filename使用者贮存下来的档案名称
  • :type预设是application/octet-stream
  • :disposition inlineattachment
  • :status预设是200

不过实务上我们很少在上线环境上直接用Rails来推送静态档案,由于大档的传输时间会浪费宝贵的Rails运算资源。我们会改用X-Sendfile Header将传档的任务委派给网页伺服器(例如ApacheNginx )处理,来下降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.ymlsecret_key_base编码后放到阅读器的Cookie当中,最大的好处是对伺服器的效能负担很低,缺点是大小最多4Kb,和资料还是可以透过反编码后看出来,只是没法进行修改。因此安全性较低,不合适寄存机密资料。

​​Cookies session storageRails也支援其他方式,你可以修改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

Cookies

​​Session,我们也能够直接操作底层的

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐