افزودن حالت دیباگ
اگر از frameworkهای وب برای زبانهای دیگر استفاده کردهاید، مثل Django یا Laravel، ممکن است با مفهوم یک حالت دیباگ آشنا باشید که در آن خطاهای دقیق به کاربر در یک پاسخ HTTP نمایش داده میشوند به جای یک پیام عمومی "Internal Server Error".
هدف شما در این تمرین راهاندازی یک حالت دیباگ مشابه برای برنامه ما است، که میتواند با استفاده از flag -debug به این شکل فعال شود:
$ go run ./cmd/web -debug
هنگام اجرا در حالت دیباگ، هر خطای دقیق و stack trace باید در مرورگر مشابه این نمایش داده شود:
مرحله 1
یک flag خط فرمان جدید با نام debug و مقدار پیشفرض false ایجاد کنید. سپس مقدار این flag خط فرمان را از طریق struct application در دسترس handlerهای خود قرار دهید.
نکته: تابع flag.Bool() برای این کار مناسبترین است.
مرحله 2
به فایل cmd/web/helpers.go بروید و helper serverError() را بهروزرسانی کنید تا یک پیام خطای دقیق و stack trace را در یک پاسخ HTTP render کند اگر — و فقط اگر — flag debug تنظیم شده باشد. در غیر این صورت یک پیام خطای عمومی را به طور عادی ارسال کنید. میتوانید stack trace را با استفاده از تابع debug.Stack() دریافت کنید.
مرحله 3
تغییر را امتحان کنید. برنامه را اجرا کنید و یک خطای runtime را با استفاده از یک DSN بدون پارامتر parseTime=true ایجاد کنید:
$ go run ./cmd/web/ -debug -dsn=web:pass@/snippetbox
بازدید از https://localhost:4000/ باید منجر به یک پاسخ مشابه این شود:
اجرای برنامه دوباره بدون flag -debug باید منجر به یک پیام عمومی "Internal Server Error" شود.
کد پیشنهادی
کد پیشنهادی برای مرحله 1
package main ... type application struct { debug bool // Add a new debug field. logger *slog.Logger snippets models.SnippetModelInterface users models.UserModelInterface templateCache map[string]*template.Template formDecoder *form.Decoder sessionManager *scs.SessionManager } func main() { addr := flag.String("addr", ":4000", "HTTP network address") dsn := flag.String("dsn", "web:pass@/snippetbox?parseTime=true", "MySQL data source name") // Create a new debug flag with the default value of false. debug := flag.Bool("debug", false, "Enable debug mode") flag.Parse() logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) db, err := openDB(*dsn) if err != nil { logger.Error(err.Error()) os.Exit(1) } defer db.Close() templateCache, err := newTemplateCache() if err != nil { logger.Error(err.Error()) os.Exit(1) } formDecoder := form.NewDecoder() sessionManager := scs.New() sessionManager.Store = mysqlstore.New(db) sessionManager.Lifetime = 12 * time.Hour sessionManager.Cookie.Secure = true app := &application{ debug: *debug, // Add the debug flag value to the application struct. logger: logger, snippets: &models.SnippetModel{DB: db}, users: &models.UserModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } tlsConfig := &tls.Config{ CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}, } srv := &http.Server{ Addr: *addr, Handler: app.routes(), ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), TLSConfig: tlsConfig, IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } logger.Info("starting server", "addr", srv.Addr) err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem") logger.Error(err.Error()) os.Exit(1) } ...
کد پیشنهادی برای مرحله 2
... func (app *application) serverError(w http.ResponseWriter, r *http.Request, err error) { var ( method = r.Method uri = r.URL.RequestURI() trace = string(debug.Stack()) ) app.logger.Error(err.Error(), "method", method, "uri", uri) if app.debug { body := fmt.Sprintf("%s\n%s", err, trace) http.Error(w, body, http.StatusInternalServerError) return } http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) } ...