Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

หน้าข้อยกเว้นแฟนซีของ Rails ทำงานอย่างไร

ข้อดีอย่างหนึ่งของการทำงานกับรางคือเมื่อมีบางอย่างผิดพลาดในการพัฒนา คุณจะได้รับหน้ารายละเอียดข้อผิดพลาดที่ดีมาก คุณได้รับ backtrace ที่ดีโดยเน้นส่วนที่เกี่ยวข้องกับแอพของคุณ คุณสามารถดูพารามิเตอร์ที่โพสต์ รวมถึงตรวจสอบตัวแปรสภาพแวดล้อมและเซสชันได้

วันนี้เราจะมาดูกันว่าหน้าข้อผิดพลาดแฟนซีเหล่านี้ทำงานอย่างไร

หน้าข้อยกเว้นแฟนซีของ Rails ทำงานอย่างไร

การแคร็กเปิด Actionpack

ไฟล์ที่เราจะกล่าวถึงในวันนี้เป็นส่วนใหญ่คือ actionpack/lib/action_dispatch/middleware/debug_exceptions.rb การแสดงหน้าข้อผิดพลาดของโหมดการพัฒนานั้นทำได้ยากที่สุด หากคุณสงสัยว่าหน้าจอข้อผิดพลาดของโหมดใช้งานจริงมาจากไหน โปรดดูที่ public_exceptions.rb

แร็คมิดเดิลแวร์

หากคุณไม่คุ้นเคยกับมิดเดิลแวร์ของแร็ค แนวคิดนี้ก็เรียบง่าย ช่วยให้คุณสามารถสกัดกั้นคำขอ HTTP ก่อนที่พวกเขาจะไปถึงแอปของคุณ และสกัดกั้นเอาต์พุตของแอปก่อนที่จะส่งกลับไปยังผู้ใช้

นี่คือมิดเดิลแวร์ง่ายๆ ที่ไม่ทำอะไรที่น่าสนใจ

class MyMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    @app.call(env)
  end
end

การช่วยเหลือข้อยกเว้นทั้งหมดผ่านแร็คมิดเดิลแวร์

ข้อยกเว้นใด ๆ ที่เกิดขึ้นในแอปของคุณเกิดจากการเรียก @app.call() การช่วยเหลือข้อยกเว้นทั้งหมดในแอพแร็คนั้นง่ายพอๆ กับการเพิ่มส่วนคำสั่งช่วยเหลือให้กับมิดเดิลแวร์

def call(env)
  @app.call(env)
rescue StandardError => exception
  # this is a method we have to provide to generate the exception page
  render_exception(env, exception)
end

สิ่งที่ส่งคืนจากวิธีการโทรจะถือว่าเหมือนกับเป็นหน้าเว็บปกติ ดังนั้นเนื้อหาที่ส่งคืนโดย render_exception จะแทนที่การตอบกลับเดิม

การแสดงข้อยกเว้น

ฉันได้ตัดตอนเมธอด render_exception จาก ActionDispatch::DebugExceptions อย่างที่คุณเห็น มันเพียงดึงข้อมูลที่เกี่ยวข้องจากข้อยกเว้นและป้อนลงในเทมเพลต ERB

def render_exception(env, exception)
  wrapper = ExceptionWrapper.new(env, exception)
  log_error(env, wrapper)

  if env['action_dispatch.show_detailed_exceptions']
    request = Request.new(env)
    template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
      request: request,
      exception: wrapper.exception,
      application_trace: wrapper.application_trace,
      framework_trace: wrapper.framework_trace,
      full_trace: wrapper.full_trace,
      routes_inspector: routes_inspector(exception),
      source_extract: wrapper.source_extract,
      line_number: wrapper.line_number,
      file: wrapper.file
    )
    file = "rescues/#{wrapper.rescue_template}"

    if request.xhr?
      body = template.render(template: file, layout: false, formats: [:text])
      format = "text/plain"
    else
      body = template.render(template: file, layout: 'rescues/layout')
      format = "text/html"
    end
    render(wrapper.status_code, body, format)
  else
    raise exception
  end
end

def render(status, body, format)
  [status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
end

การใช้งานอื่นๆ

คุณสามารถใช้เคล็ดลับมิดเดิลแวร์ชั้นวางนี้เพื่อทำสิ่งที่น่าสนใจมากมายโดยมีข้อยกเว้น ที่ Honeybadger เราใช้เพื่อสกัดกั้นข้อผิดพลาดและบันทึกลงใน API ของเรา นี่คือรหัสที่เราใช้ทำ:

def call(env)
  config.with_request(::Rack::Request.new(env)) do
    begin
      env['honeybadger.config'] = config
      response = @app.call(env)
    rescue Exception => raised
      env['honeybadger.error_id'] = notify_honeybadger(raised, env)
      raise
    end

    framework_exception = framework_exception(env)
    if framework_exception
      env['honeybadger.error_id'] = notify_honeybadger(framework_exception, env)
    end

    response
  end
ensure
  Honeybadger.context.clear!
end