[RUBY] Read the Rails Guide (Overview of Action Controller) again

5. Session

A session object is created for each user in the Rails application, and the previous request information can be used continuously. Sessions are only available in Controller and View, and a small amount of data is stored in the session.
You can select the session storage destination from the following storages.

--ʻActionDispatch :: Session :: CookieStore: Save all sessions in the cookie of the client-side browser --ʻActionDispatch :: Session :: CacheStore: Save data to Rails cache --ʻActionDispatch :: Session :: ActiveRecordStore: Save to database using Active Record (requires activerecord-session_store gem) --ʻActionDispatch :: Session :: MemCacheStore: Store data in memcached cluster (this implementation is old so you should consider CacheStore)

The session has a unique Id and stores it in a cookie. Passing the session ID as ʻURLis very dangerous for security, so it is not allowed by default inRails. Therefore, the session ID must be passed as cookie`.

Many SessionStores (session IDs) are used to retrieve session data on the server. However, only CookieStore stores all session data in cookie itself (session ID can be used if necessary).
CookieStore is used by default in Rails and is the recommended session store in Rails.
The advantage of CookieStore is

--Very lightweight --No preparation is required for new use

The cookie data is given an encrypted signature to prevent tampering, and the cookie itself is encrypted, so the content is read by others. (The tampered cookie will be rejected by Rails)

About 4KB of data can be stored in the CookieStore, which is enough capacity. Regardless of the type of session store you use, it is not desirable to store large amounts of data in a session.

Consider ʻActionDispatch :: Session :: CacheStore` if the user session does not contain important data or if you do not need to store the session for a long time.
The merit is that the existing cache infrastructure can be used as it is because it is saved using the cache implementation set in the Web application. Therefore, no special preparation is required.
The disadvantage is that the session is short-lived and can disappear at any time.

5-1. Access the session

Access to the session is made possible using the session instance method in the controller. Session values are stored using hash-like key / value pairs.

class ApplicationController < ActionController::Base

  private

  #Search for users by id stored in a keyed session
  # :current_user_id is a standard way to handle user logins in Rails applications.
  #When you log in, the session value is set and
  #The session value is deleted when you log out.

<Method ①>

  def current_user
    @current_user ||= session[:current_user_id] &&
      User.find_by(id: session[:current_user_id])
  end

<Method ②>

  def current_user
    if session[:current_user_id]
      @current_user ||= User.find_by(id: session[:current_user_id])
    end
  end

end

If you want to save something in the session, assign a key like a hash.

class LoginsController < ApplicationController
  # "Create" a login, aka "log the user in"
  def create
    if user = User.authenticate(params[:username], params[:password])
      #Save the session user id and
      #Make it available for future requests
      session[:current_user_id] = user.id
      redirect_to root_url
    end
  end
end

If you want to remove some of the data from the session, delete that key-value pair.

class LoginsController < ApplicationController
  #Delete login(=Logout)
  def destroy
    #Remove user id from session id
    session.delete(:current_user_id)
    #Clear the current user memoized
    @_current_user = nil
    redirect_to root_url
  end
end

Use reset_session to reset the entire session.

5-2. Flash flash is a special part of the session that is cleared on every request. It has a feature that it can be referenced only immediately after the request, and an error message is passed to view.
The flash is accessed as a hash and is called a FlashHash instance. As an example, let's deal with the operation of logging out. By using flash in the controller, you can send the message to be displayed in the next request.

class LoginsController < ApplicationController
  def destroy
    session.delete(:current_user_id)
    flash[:notice] = "You have successfully logged out."
    redirect_to root_url
  end
end

The flash message can also be assigned as part of a redirect. In addition to : notice and: alert as options, general : flash can also be used.

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 }
  1. Cookie Web applications can store a small amount of data called cookie on the client side (browser). Since HTTP is a" stateless "protocol, there is basically no association between requests, but with cookie you can use it between requests (or even between sessions). ) This data will be retained. In Rails, you can easily access cookies using the cookies method. The access method is very similar to that of a session, acting like a hash.
class CommentsController < ApplicationController
  def new
    #If the comment author name remains in the cookie, it will be automatically entered in the field
    @comment = Comment.new(author: cookies[:commenter_name])
  end

  def create
    @comment = Comment.new(params[:comment])
    if @comment.save
      flash[:notice] = "Thanks for your comment!"
      if params[:remember_name]
        #Comment Save the author name
        cookies[:commenter_name] = @comment.author
      else
        #Comment Delete the author name if it remains in the cookie
        cookies.delete(:commenter_name)
      end
      redirect_to @comment.article
    else
      render action: "new"
    end
  end
end

When deleting a session, it was deleted by specifying nil as the key, but when deleting cookies, you must use cookies.delete (: key) instead of this method.

Rails can also use a signed cookie jar and an encrypted cookie jar to store sensitive data.

--Signed cookie jar prevents tampering with cookie by adding an encrypted signature to the cookie value. --Encryption cookie jar adds a signature and encrypts the value itself so that it cannot be read by the end user.

These special cookies use a serializer to convert the value to a string, save it, and do a deverse conversion (deserialize) on read to return it to a Ruby object.

Rails.application.config.action_dispatch.cookies_serializer = :json

The default serializer for new applications is : json.

It is recommended to store only "simple data" such as strings and numbers in cookie. If you have to store a complex object in cookie, you have to take care of the conversion when reading the value from cookie in a subsequent request.

The same is true for session and flash hashes when using the cookie session store.

7. Output XML and JSON data

Thanks to ʻActionController, it is very easy to output (render) XMLdata andJSONdata. The controller generated usingscaffold` is as follows.

class UsersController < ApplicationController
  def index
    @users = User.all
    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render xml: @users }
      format.json { render json: @users }
    end
  end
end

Note that in the code above, it's render xml: @users instead of render xml: @ users.to_xml. Rails will automatically call to_xml if the object is not of type String.

.to_xml A method that converts an array or hash to XML format.

array.to_xml([option])
hassh.to_xml([option])

8. Filter

A filter is a method that is executed "before", "after", or "both immediately before" and "around" an action in the controller.

The filter is inherited, so if you set the filter in ApplicationController, the filter will be enabled in all controllers of the application.

A common use of "before" filters is to require a user to log in before performing an action. This filter method looks like this:

class ApplicationController < ActionController::Base
  before_action :require_login

  private

  def require_login
    unless logged_in?
      flash[:error] = "You must be logged in to access this section"
      redirect_to new_login_url # halts request cycle
    end
  end
end

This method is as simple as saving the error message in flash and redirecting to the login form if the user is not logged in.

In this example, we added a filter to ʻApplicationController, so all controllers that inherit it will be affected. This means that you will be required to log in for every feature of your application. Of course, if you request authentication on every screen of the application, you will not be able to display the login screen required for authentication, so set the login request on all controllers and actions like this. Should not be. The skip_before_action` method allows you to skip a filter for a particular action.

class LoginsController < ApplicationController
  skip_before_action :require_login, only: [:new, :create]
end

By doing the above, the new and create actions of LoginsController can still be authenticated-free. You can use the : only option if you want to skip the filter only for a particular action. Conversely, if you don't want to skip the filter only for a particular action, use the : except option. These options are also available when adding filters, so you can add filters that only run for the action you select in the first place.

8-1. ʻafter filter and ʻaround filter

In addition to the "before" filter, you can also use a filter that is executed after the action is executed, or a filter that is executed both before and after the action is executed.

The "after" filter is similar to the "before" filter, but in the case of the "after" filter, the action has already been executed and the response data that is about to be sent to the client can be accessed. Different from filters. Of course, no matter how you write the "after" filter, the execution of the action will not be interrupted. However, please note that the "after" filter is executed only after the action is successful, and is not executed if an exception occurs in the middle of the request cycle.

If you use an "around" filter, you are obliged to do a yield somewhere in the filter to perform the associated action. This is similar to how Rack middleware works.

For example, consider a website that has an approval workflow for any changes. Administrators can easily preview these changes and approve them within a transaction.

class ChangesController < ApplicationController
  around_action :wrap_in_transaction, only: :show

  private

  def wrap_in_transaction
    ActiveRecord::Base.transaction do
      begin
        yield
      ensure
        raise ActiveRecord::Rollback
      end
    end
  end
end

Note that for "around" filters, screen output (rendering) is also included in the work. Especially in the above example, if the view itself reads from the database (using scope etc.), the read will be done within a transaction and the data will be displayed in the preview.

You can also build your own response without running yield. In this case, no action is taken.

9. Protection from request forgery

Recommended Posts

Read the Rails Guide (Overview of Action Controller) again
[Rails] Check the contents of the object
Explanation of the order of rails routes
I read the source of ArrayList I read
I read the source of Integer
Check the migration status of rails
I read the source of Long
[Rails] Read the RSS of the site and return the contents to the front
Read the implementation of Rails 6 multi-DB automatic switching (ActiveRecord :: Middleware :: DatabaseSelector)
I read the source of Short
I read the source of Byte
I read the source of String
Rails: 7 basic actions defined on the controller
The identity of params [: id] in rails
part of the syntax of ruby ​​on rails
[rails] List of actions defined in Controller
[Rails] Change the label name of f.label
The process of introducing Vuetify to Rails
Read the Perelman treatise of Poincare conjecture
Rails6: Extract the image in Action Text
Read the Rails Guide (Overview of Action Controller) again
[Rails] Delete the migration file
What is Rails Active Record?
Check the migration status of rails
Rails migration
[Ruby on Rails] About Active Record callbacks
[Rails] Active Record method that even beginners often use
I read the "Object-Oriented Practical Guide", so a memorandum
[Rails 6 + Action Mailbox] Those who extracted the plain text of Gmail received by Action Mailbox
[Order method] Set the order of data in Rails
[Rails 6] Implementation of inquiry function using Action Mailer
About the official start guide of Spring Framework
[Ruby on Rails] Until the introduction of RSpec
[SPA development with Rails x Vue] Learn the basics of Vue.js (Overview of vue.js, template syntax)