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

Inside Rails: ข้อผิดพลาดที่สวยงาม (Exception Page) ทำงานยังไง

สิ่งดีของ Rails ตอน development something goes wrong ได้ error detail page สวยงาม Backtrace สวย ส่วน relevant ไป app highlighted ดู params ที่ posted, inspect environment และ session variables

วันนี้ดูว่ fancy error pages นี้ทำงานยังไง

Inside Rails: ข้อผิดพลาดที่สวยงาม (Exception Page) ทำงานยังไง

Cracking Open Actionpack

File สำคัญวันนี้: actionpack/lib/action_dispatch/middleware/debug_exceptions.rb ทำ heavy lifting สำหรับ development mode error pages Production mode error screens ดูที่ public_exceptions.rb

Rack Middleware

ไม่คุ้น rack middleware concept 簡單: intercept HTTP requests ก่อนถึง app และ intercept app output ก่อนกลับ user

Middleware 简单 ไม่ทำอะไรน่าสนใจ:

class MyMiddleware def initialize(app) @app = app end def call(env) @app.call(env) end end 

Rescuing All Exceptions via Rack Middleware

Exception ใน app เกิดจากการเรียก @app.call() Rescue all exceptions ใน rack app ง่ายแค่เพิ่ม rescue clause ใน middleware

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 

อะไรก็ตามที่คืนจาก call method จะถูก treat เหมือน normal web page Content จาก render_exception แทนที่ original response

Rendering the Exception

Excerpt render_exception method จาก ActionDispatch::DebugExceptions ดูว่าแค่ pull relevant data จาก exception feed เข้า ERB template

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 

Other Uses

Rack middleware trick ใช้ทำ interesting things กับ exceptions ได้ ที่ Honeybadger ใช้ intercept errors record ไป 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