مدیریت خطای متمرکز (Centralized Error Handling)
بیایید برنامه خود را با انتقال بخشی از کد مدیریت خطا به متدهای کمکی (Helper Methods) مرتب کنیم. این کار به جداسازی نگرانیها (Separation of Concerns) کمک میکند و از تکرار کد در طول ساخت جلوگیری میکند.
یک فایل جدید به نام helpers.go در دایرکتوری cmd/web اضافه کنید:
$ touch cmd/web/helpers.go
و کد زیر را اضافه کنید:
package main import ( "net/http" ) // The serverError helper writes a log entry at Error level (including the request // method and URI as attributes), then sends a generic 500 Internal Server Error // response to the user. func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) { var ( method = r.Method uri = r.URL.RequestURI() ) app.logger.Error(err.Error(), "method", method, "uri", uri) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } // The clientError helper sends a specific status code and corresponding description // to the user. We'll use this later in the book to send responses like 400 "Bad // Request" when there's a problem with the request that the user sent. func (app *application) clientError(w http.ResponseWriter, status int) { http.Error(w, http.StatusText(status), status) }
در این کد، یک چیز جدید دیگر نیز معرفی کردهایم: تابع http.StatusText(). این تابع یک نمایش متنی دوستانه از یک کد وضعیت HTTP مشخص برمیگرداند — برای مثال http.StatusText(400) رشته "Bad Request" را برمیگرداند و http.StatusText(500) رشته "Internal Server Error" را برمیگرداند.
حالا که این کار انجام شد، به فایل handlers.go خود برگردید و آن را بهروزرسانی کنید تا از تابع کمکی serverError() استفاده کند:
package main import ( "fmt" "html/template" "net/http" "strconv" ) func (app *application) home(w http.ResponseWriter, r *http.Request) { w.Header().Add("Server", "Go") files := []string{ "./ui/html/base.tmpl", "./ui/html/partials/nav.tmpl", "./ui/html/pages/home.tmpl", } ts, err := template.ParseFiles(files...) if err != nil { app.serverError(w, r, err) // Use the serverError() helper. return } err = ts.ExecuteTemplate(w, "base", nil) if err != nil { app.serverError(w, r, err) // Use the serverError() helper. } } ...
وقتی که بهروزرسانی شد، برنامه خود را مجدداً راهاندازی کنید و یک درخواست به http://localhost:4000 در مرورگر خود ارسال کنید.
دوباره، این باید منجر به بروز خطای (عمدی) ما شود و باید ورودی لاگ مربوطه را در ترمینال خود ببینید، شامل روش درخواست و URI به عنوان ویژگیها.
$ go run ./cmd/web time=2024-03-18T11:29:23.000+00:00 level=INFO msg="starting server" addr=:4000 time=2024-03-18T11:29:23.000+00:00 level=ERROR msg="open ./ui/html/pages/home.tmpl: no such file or directory" method=GET uri=/
بازگرداندن خطای عمدی (Reverting the Deliberate Error)
در این مرحله دیگر به خطای عمدی نیاز نداریم، بنابراین آن را به این صورت برطرف کنید:
$ mv ui/html/pages/home.bak ui/html/pages/home.tmpl
اطلاعات اضافی (Additional Information)
ردیابی پشته (Stack Traces)
میتوانید از تابع debug.Stack() برای دریافت یک ردیابی پشته که مسیر اجرای برنامه را برای گوروتین فعلی مشخص میکند، استفاده کنید. افزودن این به عنوان یک ویژگی در ورودیهای لاگ میتواند برای اشکالزدایی خطاها مفید باشد.
اگر میخواهید، میتوانید متد serverError() را بهروزرسانی کنید تا شامل یک ردیابی پشته در ورودیهای لاگ باشد به این صورت:
package main import ( "net/http" "runtime/debug" ) func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) { var ( method = r.Method uri = r.URL.RequestURI() // Use debug.Stack() to get the stack trace. This returns a byte slice, which // we need to convert to a string so that it's readable in the log entry. trace = string(debug.Stack()) ) // Include the trace in the log entry. app.logger.Error(err.Error(), "method", method, "uri", uri, "trace", trace) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) }
خروجی ورودی لاگ سپس به این صورت خواهد بود (شکست خطوط برای خوانایی اضافه شده است):
time=2024-03-18T11:29:23.000+00:00 level=ERROR msg="open ./ui/html/pages/home.tmpl:
no such file or directory" method=GET uri=/ trace="goroutine 6 [running]:\nruntime/
debug.Stack()\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x5e\nmain.(*applicat
ion).serverError(0xc00006c048, {0x8221b0, 0xc0000f40e0}, 0x3?, {0x820600, 0xc0000ab
5c0})\n\t/home/letsgofa/code/snippetbox/cmd/web/helpers.go:14 +0x74\nmain.(*application
).home(0x10?, {0x8221b0?, 0xc0000f40e0}, 0xc0000fe000)\n\t/home/letsgofa/code/snippetbo
x/cmd/web/handlers.go:24 +0x16a\nnet/http.HandlerFunc.ServeHTTP(0x4459e0?, {0x8221b
0?, 0xc0000f40e0?}, 0x6cc57a?)\n\t/usr/local/go/src/net/http/server.go:2136 +0x29\n
net/http.(*ServeMux).ServeHTTP(0xa7fde0?, {0x8221b0, 0xc0000f40e0}, 0xc0000fe000)\n
\t/usr/local/go/src/net/http/server.go:2514 +0x142\nnet/http.serverHandler.ServeHTT
P({0xc0000aaf00?}, {0x8221b0?, 0xc0000f40e0?}, 0x6?)\n\t/usr/local/go/src/net/http/
server.go:2938 +0x8e\nnet/http.(*conn).serve(0xc0000c0120, {0x8229e0, 0xc0000aae10})
\n\t/usr/local/go/src/net/http/server.go:2009 +0x5f4\ncreated by net/http.(*Server).
Serve in goroutine 1\n\t/usr/local/go/src/net/http/server.go:3086 +0x5cb\n"
واژهنامه اصطلاحات فنی
| اصطلاح فارسی | معادل انگلیسی | توضیح |
|---|---|---|
| مدیریت خطای متمرکز | Centralized Error Handling | روشی برای مدیریت خطاها در یک مکان مشخص و یکپارچه |
| متدهای کمکی | Helper Methods | توابع کمکی که برای انجام وظایف تکراری استفاده میشوند |
| جداسازی نگرانیها | Separation of Concerns | اصل طراحی که هر بخش از کد باید یک مسئولیت مشخص داشته باشد |
| کد وضعیت HTTP | HTTP Status Code | کد عددی که نتیجه یک درخواست HTTP را نشان میدهد |
| ردیابی پشته | Stack Trace | نمایش مسیر اجرای برنامه تا نقطه بروز خطا |
| گوروتین | Goroutine | واحد اجرای همزمان در Go |
| اشکالزدایی | Debugging | فرآیند شناسایی و رفع خطاها در برنامه |
| خطای عمدی | Deliberate Error | خطایی که به صورت عمدی برای آزمایش سیستم ایجاد میشود |
| کد وضعیت داخلی سرور | Internal Server Error | خطای 500 که نشاندهنده مشکلی در سمت سرور است |
| کد وضعیت درخواست بد | Bad Request | خطای 400 که نشاندهنده مشکلی در درخواست کاربر است |