##5. Session
Trong trong ứng dụng của bạn có một session cho mỗi use mà ở đó bạn có thể lưu trữ một lượng dữ liệu nhỏ mà nó được lưu lại giữa các request
. Session cũng được dùng trong controller
và view
và có thể sử dụng một số cơ chế lưu trữ khác:
* ActionDispatch::Session::CookieStore - lưu trữ mọi thứ trên `client`.
* ActionDispatch::Session::CacheStore - Lưu trữ dữ liệu trên Rails cache.
* ActionDispatch::Session::ActiveRecordStore - Lưu trữ dữ liệu trên db sử dụng Active Record. (require activerecord-session_store gem).
* ActionDispatch::Session::MemCacheStore - Lưu trữ dữ liệu trên `memcached cluster `(Đây là một cách thực thi kế thừa ; hãy xem các sử dụng `CacheStore`).
Tất cả session
lưu trữ sử dụng cookie
để lưu trữ một ID
duy nhất cho mỗi session
(bạn phải sử dụng một cookie
, Rails sẽ không cho phép bạn truyền một session ID
tới URL , điều này là kém an toàn).
Hầu hết các lưu trữ, ID
được sử dụng để tìm kiếm dữ liệu session
trên server
, ví dụ như trong bảng của cơ sở dữ liệu. có một ngoại lệ, và nó là mặc định và khuyến cáo lưu trữ session
-CookieStore
- lưu trữ toàn bộ dữ liệu session
trong bản thân cookie
(ID
vẫn được sử dụng nếu bạn cần). Ưu điểm của nó là rất nhẹ và nó không cần cài đặt ở trong ứng dụng mới để sử dụng trong session
. Dữ liệu cookie
được mã hóa để chống trộm, như nó sẽ không được encrypted
, vì vậy mọi người có thể truy cập tới nó và có thể đọc nội dung của nó nhưng không chỉnh sửa được (Rails sẽ không chấp nhận nó nếu nó đã được chỉnh sửa).
CookieStore
có thể lưu trữ khoảng 4KB dữ liệu - ít hơn nhiều so với những kiểu lưu trữ khác- nhưng thường như vậy là đủ. Lưu trữ một lượng lớn dữ liệu trong session
không được khuyến khích không vấn đề gì với session
lưu trữ ứng dụng của bạn. Đặc biệt bạn nên tránh lưu trữ những đối tượng phức tạp ( bất cứ gì khác với các đối tượng của Ruby, một ví dụ phổ biến là thể hiện của model
) trong session
, cũng như server
không thể tập hợp lại chúng giữa các request
, kết quả sẽ là lỗi.
Nếu user session
của bạn không lưu trữ giới hạn dữ liệu hoặc là không cần thời gian dài( ví dụ nếu bạn chỉ sử dụng flash
để lưu trữ thông điệp), bạn có thể sử dụng ActionDispatch::Session::CacheStore
.
Nó sẽ lưu sessions
của bạn sử dụng bộ đệm để thực thi cái mà bạn vừa thiết lập trong ứng dụng. Ưu điểm của phương pháp này là bạn có thể sử dụng kết cấu bộ nhớ đệm đã tồn tại cho việc lưu trữ session
mà không cần thêm bất cứ một thiết lập nào hoặc là quyền admin
. Khuyết điểm, sớm bị mất và có thể không xuất hiện bất cứ lúc nào.
Đọc thêm về lưu trữ session
tại: http://guides.rubyonrails.org/security.html
Nếu bạn muộn lưu trữ session
theo cơ chế khác, bạn có thể thay đổi nó trong file config/initializers/session_store.rb
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails g active_record:session_migration")
# YourApp::Application.config.session_store :active_record_store
Rails thiết lập một session key
(tên của cookie
) khi bạn đăng ký dữ liệu session
. Chúng có thể thay đổi trong file config/initializers/session_store.rb
# Be sure to restart your server when you modify this file.
YourApp::Application.config.session_store :cookie_store, key: '_your_app_session'
Bạn cũng có thể truyền qua một khóa :domain
và chỉ ra tên miền của cookie
:
# Be sure to restart your server when you modify this file.
YourApp::Application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
Rails thiết lập (cho CookieStore
) một key
bí mật được sử dụng cho việc truy cập dữ liệu session
. Nó có thể thay đổi trong file config/initializers/secret_token.rb
# Be sure to restart your server when you modify this file.
# Your secret key for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
YourApp::Application.config.secret_key_base = '49d3f3de9ed86c74b94ad6bd0...'
Thay đổi khóa bí mật này khi bạn sử dụng CookieStore
nó sẽ không hợp lễ trong tất cả các session
đã tồn tại.
###5.1 Truy cập vào Session
Trong controller
bạn có thể truy vào session
thông qua instance method
.
Những `Session` là `lazily loaded`. Nếu bạn không truy cập vào `session` trong `action's code`, nó sẽ không được load. Do đó bạn không cần phải hủy `session`, Không chỉ là truy cập chúng sẽ làm việc.
Giá trị của session
được lưu trữ sử dụng cặp key/value
giống như hash
class ApplicationController < ActionController::Base
private
# Finds the User with the ID stored in the session with the key
# :current_user_id This is a common way to handle user login in
# a Rails application; logging in sets the session value and
# logging out removes it.
def current_user
@_current_user ||= session[:current_user_id] &&
User.find_by_id(session[:current_user_id])
end
end
Để lưu một cái gì đó trong session
chỉ cần gán nó vào key
giống như hash
:
class LoginsController < ApplicationController
# "Create" a login, aka "log the user in"
def create
if user = User.authenticate(params[:username], params[:password])
# Save the user ID in the session so it can be used in
# subsequent requests
session[:current_user_id] = user.id
redirect_to root_url
end
end
end
Để xóa một cái gì đó trong session
, chỉ việc gán key
của nó thành nil
.
class LoginsController < ApplicationController
# "Delete" a login, aka "log the user out"
def destroy
# Remove the user id from the session
@_current_user = session[:current_user_id] = nil
redirect_to root_url
end
end
Để reset lại toàn bộ session
sử dụng reset_session
##5.2 Flash
Flash
là một phần đặc biệt của session
mà nó sẽ được xóa sau mỗi request
. Điều đó có nghĩa là giá trị đó sẽ chỉ được dùng ở request
tiếp theo, nó rất hữu ích để truyền các message
lỗi...
Nó truy cập khá giống với cách truy cập của sess ion
, như một hash
(nó là một FlashHash instance
).
Ví dụ sử dụng cho việc đăng xuất chẳng hạn. controller
có thể gửi
message
mà nó sẽ được hiển thị cho người dùng ở request
tiếp theo:
class LoginsController < ApplicationController
def destroy
session[:current_user_id] = nil
flash[:notice] = "You have successfully logged out."
redirect_to root_url
end
end
Chú ý rằng nó cũng có gán flash message
trong một phần của việc chuyển hướng. Bạn có thể gán :notice, :alert, hoặc là theo mục đích
redirect_to root_url, notice: "You have successfully logged out."
redirect_to root_url, alert: "You're stuck here!"
redirect_to root_url, flash: { referral_code: 1234 }
Action
destroy
đi tới root_url
của ứng dụng, wor đó message
sẽ được hiển thị. Chú ý rằng nó phụ thuộc hoàn toàn vào action
tiếp theo, nếu bất cứ thứ gì, nó sẽ làm việc với action
trước put
vào flash
. Nó rất thuận tiện để hiển thị lỗi alert
hoặc notice
từ flash
trong application's layout
:
<html>
<!-- <head/> -->
<body>
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
<!-- more content -->
</body>
</html>
Bằng cách này nếu một action
gán một notice
hoặc một alert
message, layout
sẽ tự động hiển thị nó.
Bạn có thể truyền bất cứ gì mà session
có thể lưu trữ; không chỉ có notice
hoặc alert
mà bạn có thể dùng cái mà mình muốn
<% if flash[:just_signed_up] %>
<p class="welcome">Welcome to our site!</p>
<% end %>
Nếu như bạn muốn giá trị của flash
được chuyển qua một request
khác bạn phải sử dụng phương thức keep
:
class MainController < ApplicationController
# Let's say this action corresponds to root_url, but you want
# all requests here to be redirected to UsersController#index.
# If an action sets the flash and redirects here, the values
# would normally be lost when another redirect happens, but you
# can use 'keep' to make it persist for another request.
def index
# Will persist all flash values.
flash.keep
# You can also use a key to keep only some kind of value.
# flash.keep(:notice)
redirect_to users_url
end
end
##5.2.1 flash.now
Mặc định, thêm giá trị vào flash
sẽ làm chúng có thể sử dụng trong request
tiếp theo, nếu đôi khi bạn muốn truy cập vào những giá chị của chúng trong cùng một request
. Ví dụ, nếu action create
là fails
khi bạn lưu một resoure
và bạn render
trực tiếp một template
mới, nó sẽ không đi tới kết quả của một request mới, nó có thể vẫn hiện thị message
sử dụng flash
. Để làm được điều này bạn có thể sử dụng flash.now
cũng giống như bạn sử dụng flash
:
class ClientsController < ApplicationController
def create
@client = Client.new(params[:client])
if @client.save
# ...
else
flash.now[:error] = "Could not save client"
render action: "new"
end
end
end