У статті розглянемо способи створення користувацьких винятків, що відповідають потребам вашого застосунку.
Припустимо, існує метод, який обробляє завантаження зображень. Він контролює, щоб розмір завантажуваних JPEG- зображень був у проміжку від 100 Кб до 10 Мб. З цією метою створюємо виняток кожен раз, коли розмір зображення порушує встановлені обмеження.
class ImageHandler
def self.handle_upload(image)
raise "Зображення завелике" if image.size > 10.megabytes
raise "Зображення занадто мале" if image.size < 100.kilobytes
raise "Зображення не відповідає формату JPEG" unless %w[JPG JPEG].include?(image.extension)
#… інший код
end
end
Кожного разу, коли користувач завантажує зображення, що не відповідає умові, наш Rails веб-застосунок переадресовує користувача на стандартну сторінку невідомої помилки — Rails 502 error page.
class ImageUploadController < ApplicationController
def upload
@image = params[:image]
ImageHandler.handle_upload(@image)
redirect_to :index, :notice => "Зображення успішно завантажено!"
end
end
Стандартне сповіщення про помилку в Rails не надає користувачеві бажаної допомоги, тому спробуємо виправити цю ситуацію. Наша основна мета: проінформувати користувача, якщо розмір зображення виходить за межі допустимого та заборонити хакерам завантажувати потенційно небезпечні (не JPEG) файли, повертаючи статус 403 forbidden
.
Типи користувацьких помилок
Ruby майже повністю складається з об'єктів, і помилки — не виняток. Таким чином, ми можемо визначити підклас для будь-якого типу помилок та створити власний. Використаємо згадані типи користувацьких помилок в нашому handle_upload
методі для різних валідацій.
class ImageHandler
# Конкретна область помилок
class ImageExtensionError < StandardError; end
class ImageTooBigError < StandardError
def message
"Зображення надто велике"
end
end
class ImageTooSmallError < StandardError
def message
"Зображення замале"
end
end
def self.handle_upload(image)
raise ImageTooBigError if image.size > 10.megabytes
raise ImageTooSmallError if image.size < 100.kilobytes
raise ImageExtensionError unless %w[JPG JPEG].include?(image.extension)
#інший код
end
end
Спершу ми додали до обробника три нових класи, що розширюються від StandardError
. Для помилок, пов'язаних з розміром зображень, ми перевизначили метод message
класу StandardError
з повідомленням про помилку для користувача. Спосіб виклику raise
в методі handle_upload
було також модифіковано. Таким чином, замінивши користувацьке повідомлення StandardError
на різні типи винятків, отримали можливість відстежувати більш конкретні помилки.
Нарешті, можемо використовувати вказані типи користувацьких помилок в нашому контролері для повернення різних відповідей. Наприклад, повертаємо конкретне повідомлення про помилку або певний код відповіді.
class ImageUploadController < ApplicationController
def upload
@image = params[:image]
ImageHandler.handle_upload(@image)
redirect_to :index, :notice => "Зображення успішно завантажено!!"
rescue ImageHandler::ImageTooBigError, ImageHandler::ImageTooSmallError => e
render "edit", :alert => "Error: #{e.message}"
rescue ImageHandler::ImageExtensionError
head :forbidden
end
end
Такий спосіб набагато кращий, ніж застосування стандартного виклику raise
. Додавши ще трохи підкласів, можна значно спростити обробку помилок, об'єднуючи їх у групи, замість відстеження кожного типу помилки окремо.
class ImageHandler
class ImageExtensionError < StandardError; end
class ImageDimensionError < StandardError; end
class ImageTooBigError < ImageDimensionError
def message
"Зображення занадто велике"
end
end
class ImageTooSmallError < ImageDimensionError
def message
"Зображення занадто мале"
end
end
def self.handle_upload(image)
raise ImageTooBigError if image.size > 10.megabytes
raise ImageTooSmallError if image.size < 100.kilobytes
raise ImageExtensionError unless %w(JPG JPEG).include?(image.extension)
#… інший код
end
end
Замість відстеження кожного окремого винятку, пов'язаного з розміром зображення, використовуємо батьківський клас ImageDimensionError
. Таким чином, ми відстежуємо як ImageTooBigError
, так і ImageTooSmallError
.
class ImageUploadController < ApplicationController
def upload
@image = params[:image]
ImageHandler.handle_upload(@image)
redirect_to :index, :notice => "Зображення успішно завантажено!"
rescue ImageHandler::ImageDimensionError => e
render "edit", :alert => "Error: #{e.message}"
rescue ImageHandler::ImageExtensionError
head :forbidden
end
end
Розподілення користувацьких помилок на класи найчастіше застосовується для написання сторонніх бібліотек. Яскравим прикладом є Mongo-ruby-driver gem. Кожна операція, яка може призвести до винятку, має свій клас. Такий підхід полегшує процес обробки певних типів винятків та дозволяє створювати чіткі повідомлення про помилки.
Ще немає коментарів