Користувацькі винятки в Ruby

5 хв. читання

У статті розглянемо способи створення користувацьких винятків, що відповідають потребам вашого застосунку.

Припустимо, існує метод, який обробляє завантаження зображень. Він контролює, щоб розмір завантажуваних 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. Кожна операція, яка може призвести до винятку, має свій клас. Такий підхід полегшує процес обробки певних типів винятків та дозволяє створювати чіткі повідомлення про помилки.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація