12/08/2018, 12:00

Custom Errors Page in Rails

Có thể nói đây là bài đầu tiên mình viết về Rails. Dù vấn đề này có thể không mới hay nhiều người đã biết nhưng với một người vẫn còn gà mờ về Rails như mình thì thực sự nó đem lại cho mình rất nhiều cảm xúc lúc tìm hiểu về nó. Vấn đề mình tìm hiểu là custom các trang hiển thị lỗi ở Rails. Các ...

Có thể nói đây là bài đầu tiên mình viết về Rails. Dù vấn đề này có thể không mới hay nhiều người đã biết nhưng với một người vẫn còn gà mờ về Rails như mình thì thực sự nó đem lại cho mình rất nhiều cảm xúc lúc tìm hiểu về nó. Vấn đề mình tìm hiểu là custom các trang hiển thị lỗi ở Rails. Các trang lỗi default mà Rails có đây là các lỗi 404, 422, 500 để hiển thị mấy cái lỗi kiểu như không thấy page, không thấy action, không thấy template, không thấy record... Có lẽ bạn cũng chả lạ gì mấy màn hình

hay là

Vậy làm thế nào để thay vì hiển thị mấy trang default này của Rails, web sẽ hiển thị những trang page báo lỗi do mình thiết kế.

Ruby 2.xx

Quay trở lại với ruby 2.x thì đây là vấn đề được xử lý một cách cực kì đơn giản. Bạn chỉ cần sử dụng rescue_from và được viết như dưới đây ở application_controller.rb

 #application_controller.rb
 class ApplicationController < ActionController::Base
   rescue_from Exception, with: :render_500
   rescue_from UnauthorizedException, with: :render_401
   rescue_from ActionController::RoutingError, with: :render_404

   def render_401
      #render custom 401 page
   end

   def render_404
      #render custom 404 page
   end

   def render_500
      #render custom 500 page
   end
 end

(ok) và đoạn code trên sẽ chỉ chạy ở Rails 2.xx. Lý do là vì ở Rails 2.x thì việc handle cũng như bắt những exception này được xử lý ở ActionController::RoutingError, do đó bạn có thể rescue_from thoải mái ở đây.

Tuy nhiên từ phiên bản 3. thì việc routing request được xử lý hoàn toàn ở middleware (ActionDispatch), tương đương với việc ActionDispatch sẽ bắt lỗi, render đến page lỗi trước khi mà request này đến được với ActionController nên một vài exception được bắt ở những dòng code bên trên sẽ không chạy từ Rails 3, ví dụ như là ActionController::RoutingError, ActionController:InvalidAuthenticyToken... Vậy thì sẽ phải xử lý như thế nào (?)

Rails 3.x, 4.x

Sau một hồi mày mò anh Google, rồi thì tự tìm hiểu thì mình thấy có 4 cách như dưới đây:

Phương án 1

Trong routes.rb sẽ lấy viết routes get tất cả các request, trỏ đến một hàm ở application_controller.rb . Và ở hàm này sẽ raise exception. Như vậy thì vẫn sử dụng được rescue_from. Ví dụ ở dưới đây

 #application_controller.rb
 class ApplicationController < ActionController::Base
   rescue_from Exception, with: :render_500
   rescue_from UnauthorizedException, with: :render_401
   rescue_from ActionController::RoutingError, with: :render_404

   def raise_not_found!
    raise ActionController::RoutingError.new("No route matches #{params[:unmatched_route]}")
    end

   def render_401
      #render custom 401 page
   end

   def render_404
      #render custom 404 page
   end

   def render_500
      #render custom 500 page
   end
 end

và ở trong file routes.rb viết dòng dưới đây vào cuối file

 # -*- coding: utf-8 -*-
Rails.application.routes.draw do
  #...
  match '*path' => 'application#raise_not_found!', via: [:get, :post]
end

Tuy nhiên cách này là cách khá là sida vì lại có một routes nhận tất cả các request. Chưa kể lại chỉ bắt được đúng một exception cho lỗi 404 là ActionController::RoutingError. Thế nên đây là cách không được recommend nhất =))

Trước khi đến cách thứ 2 và cách thứ 3 mình sẽ show các bạn thấy dòng code ở trong ActionDispatch để show exception.

# File railties/lib/rails/application/default_middleware_stack.rb
#...
middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
#...

def show_exceptions_app
  config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
end

Có lẽ đến đây bạn đã hiểu được cách thứ 2 và 3 của mình             </div>
            
            <div class=

0