برنامه نویسی

Exception Handling و Validations in Rails و نحوه نمایش خطاها به کاربران.

در این مقاله به بررسی موارد استثنا و اعتبارسنجی در Rails خواهیم پرداخت. رسیدگی و اعتبارسنجی استثناها برای آن بسیار مهم است هر برنامه های وب، از جمله آنهایی که با Rails ساخته شده اند. به این ترتیب می‌توانیم پیام‌های خطایی را نمایش دهیم که نه تنها برای ما به‌عنوان توسعه‌دهنده، بلکه برای کاربران برنامه‌هایمان نیز مفید است. چگونه متوجه می شوید که نام کاربری یا رمز عبور اشتباهی را در یک برنامه بدون هیچ گونه اعتبارسنجی یا رسیدگی به خطا وارد کرده اید؟

ابتدا، در صورت نیاز، یک تجدید (یا مقدمه) در مورد استثناها. در غیر این صورت، به بخش اعتبار سنجی بروید!


قبلاً استثناهایی را در عمل دیده اید. به عنوان مثال، اگر تا به حال در کد خود اشتباه تایپی داشته باشید که باعث از کار افتادن برنامه شما با «NoMethodError» یا «SyntaxError» شود، یک استثنا را مشاهده می کنید:

putss 'Hello World'
# => NoMethodError: undefined method 'putss' for main:Object
# => Did you mean? puts
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

یک استثنا نشان دهنده یک شرط خطا در یک برنامه است. استثناها نحوه برخورد روبی با رویدادهای غیرمنتظره است و مکانیسمی برای توقف اجرای یک برنامه ارائه می کنند. هنگامی که یک استثنا مطرح می شود، برنامه شما شروع به خاموش شدن می کند. اگر هیچ چیز این فرآیند را متوقف نکند، برنامه شما در نهایت متوقف می شود و یک پیام خطا چاپ می کند.

رسیدگی به استثنا

برنامه‌های کرش‌کننده بدون مشکل هستند. به طور معمول می خواهیم از خراب شدن برنامه جلوگیری کنیم و به خطا واکنش نشان دهیم. به این کار “دست زدن” می گویند (همچنین به عنوان “نجات” یا “گرفتن” یک استثنا شناخته می شود.

نحو اصلی چیزی شبیه به این است:

#Imagine we write a function that would be called when an exception is raised.
def handle_exception
  puts "Got an exception, but I'm handling it!"
end

begin
  # Any exceptions in here...
rescue
  # ...will trigger this block of code
  handle_exception
end

# => "Got an exception, but I'm handling it!"

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این مثال، یک استثنا مطرح شده است، اما برنامه به دلیل “رستگاری” از کار نمی افتد. به جای خراب شدن، روبی کد را در قسمت اجرا می کند rescue بلوک، که یک پیام را چاپ می کند. این خیلی خوب است، اما تمام این کارها این است که بدون اینکه به ما بگوید مشکلی پیش آمده است چی اشتباه رفت

هر گونه اطلاعات در مورد آنچه اشتباه رخ داده است در داخل یک قرار می گیرد شی استثنا.

اشیاء استثنایی

اشیاء استثنا (به عنوان مثال SyntaxError یا ArgumentError) اشیاء Ruby معمولی هستند (زیر کلاس های کلاس Exception). آنها حاوی اطلاعاتی در مورد “چه اشتباهی رخ داد” برای استثنایی هستند که نجات یافتند.

برای به دست آوردن یک شی استثنا، rescue نحو کمی متفاوت است. در این مثال، ما از یک کلاس استثنایی ZeroDivisionError نجات خواهیم داد.

#Rescues all errors, and assigns the exception object to an `error` variable
rescue => error

#Rescues only ZeroDivisionError and assigns the exception object to an `error` variable
rescue ZeroDivisionError => error
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در مثال دوم بالا، ZeroDivisionError کلاس شی در است error متغیر،ZeroDivisionError خود زیر مجموعه ای از StandardError. استانداردترین انواع خطا در روبی مانند ArgumentError و NameError زیر کلاس های هستند StandardError. در حقیقت، StandardError استثنای پیش فرض برای روبی است زمانی که Exception زیر کلاس به صراحت نامگذاری نشده است.

begin
  do_something
rescue => error
  # This is the same as rescuing StandardError
  # and all of its subclasses
end
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

حالا.. بیایید به عقب برگردیم و نگاهی دقیق تر به شی استثنا برای بیاندازیم ZeroDivisionError

begin
  # Any exceptions in here...
  1 / 0
rescue ZeroDivisionError => error
  puts "Exception Class: #{error.class}"
  puts "Exception Message: #{error.message}"
end

# Exception Class: ZeroDivisionError
# Exception Message: divided by 0
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکثر موارد استثنایی Ruby حاوی پیامی هستند که جزئیات اشتباه را نشان می دهد.

ایجاد استثناهای خودتان

شما همچنین می توانید استثناهای خود را فعال کنید! فرآیند افزایش استثناهای خود را … خوب … “افزایش” می گویند. شما این کار را با تماس با raise روش.

وقتی استثناهای خود را مطرح می کنید، شما انتخاب می کنید که از کدام نوع استثنا استفاده کنید. همچنین می توانید پیام خطای خود را تنظیم کنید.

begin
  #raises an ArgumentError with the message "You screwed up!"
  raise ArgumentError.new("You screwed up!")
rescue ArgumentError => error
  puts error.message
end

# => "You screwed up!"
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ایجاد استثناهای سفارشی

استثناهای داخلی روبی عالی هستند، اما هنوز از نظر دامنه محدود هستند و نمی‌توانند هر مورد استفاده ممکن را پوشش دهند.

فرض کنید در حال ساختن یک برنامه وب هستید و می خواهید زمانی که یک کاربر سعی می کند به بخشی از سایت دسترسی پیدا کند که نباید به آن دسترسی داشته باشد، استثنایی ایجاد کنید؟ هیچ یک از استثناهای داخلی Ruby واقعاً مناسب نیستند، بنابراین بهترین شرط شما ایجاد یک استثنا سفارشی است.

برای ایجاد یک استثنا سفارشی، به سادگی یک کلاس جدید ایجاد کنید که از آن ارث می برد StandardError.

class PermissionDeniedError < StandardError

  def initialize(message)
    # Call the parent's(StandardError) constructor to set the message
    super(message)
  end

end

# Then, when the user tries to do something they don't
# have permission to do, you might do something like this:
raise PermissionDeniedError.new("Permission Denied!!!")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

سلسله مراتب طبقات استثنایی

ما با ایجاد یک زیر کلاس از یک استثنا سفارشی ایجاد کردیم StandardError، که خود زیر مجموعه ای از Exception.
اگر به سلسله مراتب کلاس ها نگاه کنید، خواهید دید که همه چیز زیر کلاس است Exception.

Exception Handling و Validations in Rails و نحوه نمایش خطاها

نگران نباشید، لازم نیست همه اینها را به خاطر بسپارید. اما این مهم است که به دلیل بسیار خوبی با سلسله مراتب طبقاتی به طور کلی آشنا هستید.

نجات خطاهای یک کلاس خاص، خطاهای زیر کلاس های آن را نیز نجات می دهد.

وقتی StandardError را نجات می دهید، نه تنها استثنائات را نجات می دهید StandardError بلکه فرزندانش نیز. اگر به نمودار نگاه کنید، خواهید دید که مقدار زیادی وجود دارد: ArgumentError، NameError، TypeError و غیره.

بنابراین، اگر قرار بود نجات دهید Exception، شما می توانید تک تک استثناها را نجات دهید، که یک بد اندیشه. این کار را نکن!

روبی برای چیزهایی غیر از خطا از استثناها استفاده می کند، بنابراین نجات همه خطاها باعث می شود که برنامه شما به روش های عجیبی خراب شود. همیشه نجات خاص استثناها. در صورت شک، استفاده کنید StandardError.


اکنون که درک درستی از استثناها داریم، بیایید نگاه کنیم اعتبارسنجی ها و چگونه همه چیز به هم مرتبط است

بسیاری از کارهایی که ما در زندگی روزمره خود انجام می دهیم اکنون می توانند به صورت آنلاین انجام شوند. چه خرید، بانکداری، رزرو شام و غیره. اما در قلب همه این فرآیندها چیست؟ داده ها، این همه داده است.

بنابراین، برای اطمینان از اینکه همه چیز به خوبی اجرا می شود، یکپارچگی داده ها باید تضمین شود. این توسط انجام می شود اعتبار سنجی داده هایی که قبل از ذخیره در پایگاه داده ارائه شده است.

همانطور که می دانید Rails بر اساس معماری MVC ساخته شده است. را م، که هست مترلایه odel، به مدیریت نحوه ایجاد و ذخیره اشیاء در پایگاه داده می پردازد. در Rails، این لایه به طور پیش‌فرض توسط Active Record اجرا می‌شود، بنابراین، Active Record با انجام وظیفه حیاتی اعتبارسنجی داده‌ها سروکار دارد.

اعتبارسنجی ها صرفاً مجموعه ای از قوانین هستند که بر اساس برخی معیارها معتبر بودن داده ها را تعیین می کنند. هر شی مدل شامل یک خطاها مجموعه. در مدل های معتبر، این مجموعه فاقد خطاست و خالی است. وقتی قوانین اعتبارسنجی را روی یک مدل خاص اعلام می‌کنید و آن مدل نمی‌تواند تصویب شود، یک استثنا مطرح خواهد شد و مجموعه خطاها حاوی خطاهایی مطابق با قوانینی است که شما تنظیم کرده اید، و این مدل معتبر نخواهد بود. یک مثال ساده بررسی این است که آیا یک فرم آنلاین تمام داده های لازم را قبل از ارسال دارد یا خیر. اگر قسمتی خالی بماند، پیغام خطا به کاربر نمایش داده می شود تا بتواند آن را پر کند.

اعتبار سنجی های ساده

بیایید به دو روش اعتبارسنجی اساسی نگاه کنیم.

اعتبارسنجی وجود و/یا عدم وجود صفات

این اعتبارسنجی وجود (یا عدم وجود) یک ویژگی را بررسی می کند. در مثال زیر، ما در حال بررسی هستیم که ببینیم آیا username ویژگی نه تنها موجود است، بلکه منحصر به فرد است. این به شما کمک می کند تا از نام کاربری تکراری در برنامه خود جلوگیری کنید. شما اینها را با presence و uniqueness کمک کنندگان اعتبارسنجی به ترتیب با استفاده از validates کلمه کلیدی و به دنبال آن نام ویژگی:

class User < ApplicationRecord
   #validates presence and uniqueness of a username upon 
   #creation
   validates :username, presence: true, uniqueness: true
end
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اعتبارسنجی طول کاراکتر در یک ویژگی

برای یک password ویژگی، ما می‌توانیم بررسی کنیم که آیا رمز عبوری که می‌خواهیم تنظیم کنیم، الزامات مشخص شده ما را برآورده می‌کند یا خیر. در این مورد، ما مطمئن می شویم که حداقل طول آن 8 کاراکتر با کمک کننده طول باشد:

class User < ApplicationRecord
  validates :username, presence: true, uniqueness: true
  #validates minimum length of 8 characters
  validates :password, length: { minimum: 8 }
end
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کار با خطاهای اعتبارسنجی

پس چگونه با خطای اعتبارسنجی برخورد می کنید؟ همانطور که قبلا ذکر شد، زمانی که اعتبار سنجی ناموفق باشد، یک استثنا مطرح می شود و یک errors مجموعه تولید می شود.

بیایید ببینیم وقتی می خواهیم یک حساب کاربری با رمز عبور کمتر از 8 کاراکتر ایجاد کنیم چه اتفاقی می افتد:

class User < ApplicationRecord
  validates :username, presence: true, uniqueness: true
  validates :password, length: { minimum: 8 }
end

user = User.create(username: admin, password: "Welcome")

pp user

# => #<User:0x00007fd0cca748e8
# id: nil,
# username: "admin",
# password: "Welcome",
# created_at: nil,
# updated_at: nil> 
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

چیزی را که به نظر می رسد یک برمی گردیم User به عنوان مثال، اما با بررسی دقیق تر می بینیم که id ویژگی است nil، به معنی User نمونه در پایگاه داده ذخیره نشد. دانستن آن خوب است، اما باید ببینیم چرا دقیقاً این رکورد نامعتبر است. برای ایجاد یک استثنا و دیدن جزئیات بیشتر در مورد خطا، باید یک bang(!) را به ما اضافه کنیم create روش:

class User < ApplicationRecord
  validates :username, presence: true, uniqueness: true
  validates :password, length: { minimum: 8 }
end

user = User.create!(username: admin, password: "Welcome")

# => ActiveRecord::RecordInvalid (Validation failed: Password is # too short (minimum is 8 characters)) 
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در اینجا ما یک را دریافت می کنیم RecordInvalid استثنا، یکی از بسیاری از زیر کلاس های استثنا ActiveRecordError، که خود زیر مجموعه ای از StandardError. لیست استثناهای Active Record را اینجا ببینید.

RecordInvalid زمانی افزایش می یابد که یک رکورد به دلیل اعتبار سنجی ناموفق در پایگاه داده ذخیره نشود. در این مورد رمز عبور ما خیلی کوتاه بود.

یادتان هست اشاره کردم که مجموعه ای از خطاها نیز ایجاد می شود؟ در اینجا نحوه دسترسی به آن آمده است:

class User < ApplicationRecord
  validates :username, presence: true, uniqueness: true
  validates :password, length: { minimum: 8 }
end

user = User.create!(username: nil, password: "Welcome")
# => ActiveRecord::RecordInvalid (Validation failed: Username can't be blank, Password is too short (minimum is 8 characters))

#Accesses an array of error messages from the `errors` object.
user.errors.full_messages
# => ["Username can't be blank", "Password is too short (minimum is 8 characters)"] 
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

داشتن آرایه‌ای از خطاهای اعتبارسنجی به راحتی قابل دسترسی است زیرا می‌توانیم آن‌ها را به frontend ارتباط دهیم.


برمی گردیم به خودمان User مدل، بیایید a اضافه کنیم create روش ایجاد یک حساب کاربری جدید

def create
    #remember to append a "!" to your create method
    user = User.create!(user_params)
    session[:user_id] = user.id
    render json: user, status: :created
end

private

def user_params
    params.permit(:username, :password, 
    :password_confirmation)
end
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اکنون در صورت تأیید ناموفق، در این مورد یک نام کاربری خالی و/یا یک رمز عبور کوتاه، a RecordInvalid استثنا مطرح خواهد شد. اکنون می‌توانید منطق شرطی را برای نمایش خطاها به صفحه اصلی اضافه کنید، اجازه دهید ما را به‌روزرسانی کنیم create روش:

def create
  begin
    user = User.create!(user_params)
    session[:user_id] = user.id
    render json: user, status: :created
  rescue ActiveRecord::RecordInvalid => exception
    render json: {errors: 
    exception.record.errors.full_messages}, status: 
    :unprocessable_entity
  end
end
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در بلوک نجات خود، استثنا را به عنوان آرگومان عبور می دهیم و به مجموعه خطاهای خود که در exception.record.errors.full_messages. سپس آن را با فرمت JSON زیبا می بندیم تا بتوان با کد وضعیت HTTP به فرانت اند ارسال شود. 422 موجودیت غیر قابل پردازش با استفاده از status: روش. شما همچنین می توانید فقط تایپ کنید status: 422، اما این قابل خواندن تر است.

آیا راه آسان تری وجود ندارد؟

می توانید ببینید که چگونه این می تواند سریع تکرار شود، به خصوص اگر با چندین مدل و کنترلر کار می کنید. شما باید همین را داشته باشید begin/rescue مسدود کردن در همه create اقدامات در سراسر کنترلرهای شما فقط برای رسیدگی به این یک استثنا. خوشبختانه راهی برای انتزاع بخشی از این منطق و آسان کردن زندگی شما وجود دارد.

در Rails متوجه خواهید شد که هر کنترل کننده از آن ارث می برد ApplicationController، و بنابراین تمام روش های خود را به ارث می برد. ما می توانیم با حرکت منطقی برای مدیریت a از این مزیت استفاده کنیم RecordInvalid استثنا تا ApplicationController، اما این بار این کار را کمی متفاوت انجام خواهیم داد:

class ApplicationController < ActionController::API
   #rescue_from takes an exception class, and an exception 
   #handler method
   rescue_from ActiveRecord::RecordInvalid, with: 
   :unprocessable_entity_response

   private

   def unprocessable_entity_response(exception)
     render json: {errors: 
     exception.record.errors.full_messages}, status: 
     :unprocessable_entity
   end
end
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به جای یک بلوک start/rescue، از متد rescue_from استفاده می کنیم که یک کلاس استثنا و یک کنترل کننده استثنا مشخص شده توسط یک دنباله را می گیرد. with: گزینه ای حاوی نام روش کنترل کننده استثنا. در این مورد ما یک متد کلاس خصوصی به نام ایجاد کردیم unprocessable_entity_response. بنابراین، هنگامی که استثنای مشخص شده مطرح می شود، rescue_from شی استثنا را به کنترل کننده استثنا که در with: گزینه.

در قسمت جلویی …

نمایش خطاهای اعتبارسنجی در فرانت اند به سادگی دریافت آنها از طریق یک درخواست واکشی و استفاده از رندر شرطی برای نمایش آنها به کاربر در صورت بروز خطای اعتبارسنجی است.

این نمونه ای از فرم ثبت نام حساب است که قبلاً با استفاده از React ساخته بودم:

function SignupForm() {
    const [username, setUsername] = useState("")
    const [password, setPassword] = useState("")
    const [passwordConfirmation, setPasswordConfirmation] = 
    useState("")

   //Any validation errors that are returned will be stored 
   in state...
    const [errors, setErrors] = useState([])

    async function handleSubmit(e) {
        e.preventDefault();

        const res = await fetch("/api/signup", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({
                username,
                password,
                password_confirmation: passwordConfirmation
            })
        })

        if (res.ok) {
            const user = await res.json()
            setUser(user)
        } else {
            //if the fetch request fails and returns an error 
            object, the errors will be stored in state using 
            setErrors
            const err = await res.json()
            setErrors(err.errors)
        }

    }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در دستور بازگشت، می‌توانید از رندر شرطی برای نمایش خطاها در صورت وجود آنها استفاده کنید errors آرایه در حالت:

return (
   {errors.map((err) => <Alert key={err} severity="error">{err}</Alert>)}
)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

نتیجه:

Exception Handling و Validations in Rails و نحوه نمایش خطاها


https://guides.rubyonrails.org/active_record_validations.html
http://www.railsstatuscodes.com/
https://www.rubydoc.info/docs/rails/4.1.7/ActiveRecord/ActiveRecordError
https://ruby-doc.org/core-2.5.1/Exception.html

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا