Rails源码研究之ActionController:四,session

本文深入探讨Rails框架中Session管理机制,介绍了如何通过配置选择不同存储方式如文件、数据库、DRb服务器、MemCache及内存,并提供了示例代码展示具体实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们知道Rails默认使用file来存储session数据,放在tmp\sessions目录下
其实我们还可以使用数据库、drb_server、mem_cache甚至内存来存储session数据
方法就是更改environment.rb:
[code]
config.action_controller.session_store = :active_record_store || :drb_store || :mem_cache_store || :memory_store
[/code]
当然使用数据库存储session数据时要先创建数据库表
[code]
rake db:sessions:create
[/code]
这在以前的Rails良药系列文章中也提到了

Rails的session管理的源代码文件为session_management.rb:
[code]
require 'action_controller/session/drb_store'
require 'action_controller/session/mem_cache_store'
if Object.const_defined?(:ActiveRecord)
require 'action_controller/session/active_record_store'
end

module ActionController
module SessionManagement

def self.included(base)
base.extend(ClassMethods)
base.send :alias_method_chain, :process, :session_management_support
base.send :alias_method_chain, :process_cleanup, :session_management_support
end

module ClassMethods
def session_store=(store)
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager] =
store.is_a?(Symbol) ? CGI::Session.const_get(store == :drb_store ? "DRbStore" : store.to_s.camelize) : store
end
def session_store
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS[:database_manager]
end
def session_options
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS
end
def session(*args)
options = Hash === args.last ? args.pop : {}
options[:disabled] = true if !args.empty?
options[: only] = [*options[: only]].map { |o| o.to_s } if options[: only]
options[:except] = [*options[:except]].map { |o| o.to_s } if options[:except]
if options[: only] && options[:except]
raise ArgumentError, "only one of either : only or :except are allowed"
end

write_inheritable_array("session_options", [options])
end
def session_options_for(request, action) #:nodoc:
if (session_options = cached_session_options).empty?
{}
else
options = {}

action = action.to_s
session_options.each do |opts|
next if opts[:if] && !opts[:if].call(request)
if opts[: only] && opts[: only].include?(action)
options.merge!(opts)
elsif opts[:except] && !opts[:except].include?(action)
options.merge!(opts)
elsif !opts[: only] && !opts[:except]
options.merge!(opts)
end
end

if options.empty? then options
else
options.delete : only
options.delete :except
options.delete :if
options[:disabled] ? false : options
end
end
end
end

def process_with_session_management_support(request, response, method = :perform_action, *arguments) #:nodoc:
set_session_options(request)
process_without_session_management_support(request, response, method, *arguments)
end

private

def set_session_options(request)
request.session_options = self.class.session_options_for(request, request.parameters["action"] || "index")
end
def process_cleanup_with_session_management_support
clear_persistent_model_associations
process_cleanup_without_session_management_support
end
def clear_persistent_model_associations #:doc:
if defined?(@_session) && @_session.respond_to?(:data)
session_data = @_session.data

if session_data && session_data.respond_to?(:each_value)
session_data.each_value do |obj|
obj.clear_association_cache if obj.respond_to?(:clear_association_cache)
end
end
end
end
end
end
[/code]
alias_method_chain对process和process_cleanup方法加上session_management_support
我们在看action_controller/base.rb文件时知道process方法是action调用的入口,而process_cleanup是action调用结束时调用的
而@_session变量也是在base.rb里定义的:
[code]
@_response = response
@_response.session = request.session
@_session = @_response.session
[/code]

我们可以在controller里调用session这个类方法来指定使用session的范围:
[code]
# turn off session management for all actions.
session : off

# turn off session management for all actions _except_ foo and bar.
session : off, :except => %w(foo bar)

# turn off session management for only the foo and bar actions.
session : off, : only => %w(foo bar)

# the session will only work over HTTPS, but only for the foo action
session : only => :foo, :session_secure => true

# the session will only be disabled for 'foo', and only if it is requested as a web service
session : off, : only => :foo,
:if => Proc.new { |req| req.parameters[:ws] }
[/code]

看看active_record_store.rb中的一段代码:
[code]
class CGI
class Session
class ActiveRecordStore

class Session < ActiveRecord::Base
cattr_accessor :data_column_name
self.data_column_name = 'data'

class << self
def create_table!
connection.execute <<-end_sql
CREATE TABLE #{table_name} (
id INTEGER PRIMARY KEY,
#{connection.quote_column_name('session_id')} TEXT UNIQUE,
#{connection.quote_column_name(@@data_column_name)} TEXT(255)
)
end_sql
end
def drop_table!
connection.execute "DROP TABLE #{table_name}"
end
end
end

cattr_accessor :session_class
self.session_class = Session

def initialize(session, option = nil)
session_id = session.session_id
unless @session = ActiveRecord::Base.silence { @@session_class.find_by_session_id(session_id) }
unless session.new_session
raise CGI::Session::NoSession, 'uninitialized session'
end
@session = @@session_class.new(:session_id => session_id, :data => {})
end
end

def update
if @session
ActiveRecord::Base.silence { @session.save }
end
end

def close
if @session
update
@session = nil
end
end

def delete
if @session
ActiveRecord::Base.silence { @session.destroy }
@session = nil
end
end

end
end
end
[/code]
active_record_store是使用数据库保存session,里面定义了create_table!方法以及update/delete/close等与数据库@session相关的操作
而mem_cache_store的update/delete/close等方法的实现则是对@cache的操作,drb_store则是对DRbObject的操作,不同的方式有不同的实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值