راهاندازی مدیر جلسه (Setting Up the Session Manager)
در این بخش، نحوه راهاندازی مدیر جلسه (Setting Up Session Manager) را بررسی میکنیم. این شامل پیکربندی (Configuration) و مقداردهی اولیه (Initialization) مدیر جلسه میشود.
برای شروع، بیایید تنظیمات جلسه (Session Settings) را در ساختار برنامه (Application Structure) خود اضافه کنیم:
در این فصل، به اصول راهاندازی و استفاده از بسته alexedwards/scs میپردازم، اما اگر قصد دارید از آن در یک برنامه تولیدی استفاده کنید، توصیه میکنم مستندات و مرجع API را مطالعه کنید تا با تمام ویژگیهای آن آشنا شوید.
اولین کاری که باید انجام دهیم، ایجاد یک جدول sessions در پایگاه داده MySQL ما برای نگهداری دادههای نشست کاربران است. ابتدا از طریق ترمینال به MySQL به عنوان کاربر root متصل شوید و دستور SQL زیر را برای راهاندازی جدول sessions اجرا کنید:
USE snippetbox; CREATE TABLE sessions ( token CHAR(43) PRIMARY KEY, data BLOB NOT NULL, expiry TIMESTAMP(6) NOT NULL ); CREATE INDEX sessions_expiry_idx ON sessions (expiry);
در این جدول:
فیلد
tokenشامل یک شناسه منحصر به فرد و به صورت تصادفی تولید شده برای هر نشست خواهد بود.فیلد
dataشامل دادههای واقعی نشست است که میخواهید بین درخواستهای HTTP به اشتراک بگذارید. این دادهها به صورت دادههای باینری در نوعBLOB(شیء بزرگ باینری) ذخیره میشوند.فیلد
expiryشامل زمان انقضای نشست خواهد بود. بستهscsبه طور خودکار نشستهای منقضی شده را از جدولsessionsحذف میکند تا بیش از حد بزرگ نشود.
کار بعدی که باید انجام دهیم، ایجاد یک مدیر نشست در فایل main.go و در دسترس قرار دادن آن برای هندلرها از طریق ساختار application است. مدیر نشست تنظیمات پیکربندی نشستهای ما را نگه میدارد و همچنین برخی از میانافزارها و روشهای کمکی را برای بارگذاری و ذخیره دادههای نشست فراهم میکند.
فایل main.go خود را باز کنید و به صورت زیر بهروزرسانی کنید:
package main import ( "database/sql" "flag" "html/template" "log/slog" "net/http" "os" "time" // New import "snippetbox.alexedwards.net/internal/models" "github.com/alexedwards/scs/mysqlstore" // New import "github.com/alexedwards/scs/v2" // New import "github.com/go-playground/form/v4" _ "github.com/go-sql-driver/mysql" ) // Add a new sessionManager field to the application struct. type application struct { logger *slog.Logger snippets *models.SnippetModel 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") 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() // Use the scs.New() function to initialize a new session manager. Then we // configure it to use our MySQL database as the session store, and set a // lifetime of 12 hours (so that sessions automatically expire 12 hours // after first being created). sessionManager := scs.New() sessionManager.Store = mysqlstore.New(db) sessionManager.Lifetime = 12 * time.Hour // And add the session manager to our application dependencies. app := &application{ logger: logger, snippets: &models.SnippetModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } logger.Info("starting server", "addr", *addr) err = http.ListenAndServe(*addr, app.routes()) logger.Error(err.Error()) os.Exit(1) } ...
برای اینکه نشستها کار کنند، باید مسیرهای برنامه خود را با میانافزار ارائه شده توسط روش SessionManager.LoadAndSave() بپوشانیم. این میانافزار به طور خودکار دادههای نشست را با هر درخواست و پاسخ HTTP بارگذاری و ذخیره میکند.
مهم است که توجه داشته باشید که نیازی نیست این میانافزار بر روی همه مسیرهای برنامه ما عمل کند. به طور خاص، نیازی به آن در مسیر GET /static/ نداریم، زیرا همه این کار فقط ارائه فایلهای استاتیک است و نیازی به رفتار با حالت نیست.
بنابراین، به همین دلیل، منطقی نیست که میانافزار نشست را به زنجیره میانافزار standard موجود خود اضافه کنیم.
در عوض، بیایید یک زنجیره میانافزار dynamic جدید ایجاد کنیم که فقط شامل میانافزار مناسب برای مسیرهای برنامه پویا ما باشد.
فایل routes.go را باز کنید و به صورت زیر بهروزرسانی کنید:
package main import ( "net/http" "github.com/justinas/alice" ) func (app *application) routes() http.Handler { mux := http.NewServeMux() // Leave the static files route unchanged. fileServer := http.FileServer(http.Dir("./ui/static/")) mux.Handle("GET /static/", http.StripPrefix("/static", fileServer)) // Create a new middleware chain containing the middleware specific to our // dynamic application routes. For now, this chain will only contain the // LoadAndSave session middleware but we'll add more to it later. dynamic := alice.New(app.sessionManager.LoadAndSave) // Update these routes to use the new dynamic middleware chain followed by // the appropriate handler function. Note that because the alice ThenFunc() // method returns a http.Handler (rather than a http.HandlerFunc) we also // need to switch to registering the route using the mux.Handle() method. mux.Handle("GET /{$}", dynamic.ThenFunc(app.home)) mux.Handle("GET /snippet/view/{id}", dynamic.ThenFunc(app.snippetView)) mux.Handle("GET /snippet/create", dynamic.ThenFunc(app.snippetCreate)) mux.Handle("POST /snippet/create", dynamic.ThenFunc(app.snippetCreatePost)) standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders) return standard.Then(mux) }
اگر اکنون برنامه را اجرا کنید، باید متوجه شوید که همه چیز به درستی کامپایل میشود و مسیرهای برنامه شما به طور عادی کار میکنند.
اطلاعات اضافی
بدون استفاده از alice
اگر از بسته justinas/alice برای مدیریت زنجیرههای میانافزار خود استفاده نمیکنید، باید از آداپتور http.HandlerFunc() برای تبدیل توابع هندلر خود مانند app.home به http.Handler استفاده کنید و سپس آن را با میانافزار نشست بپوشانید. مانند این:
mux := http.NewServeMux() mux.Handle("GET /{$}", app.sessionManager.LoadAndSave(http.HandlerFunc(app.home))) mux.Handle("GET /snippet/view/:id", app.sessionManager.LoadAndSave(http.HandlerFunc(app.snippetView))) // ... etc
واژهنامه اصطلاحات فنی
| اصطلاح فارسی | معادل انگلیسی | توضیح |
|---|---|---|
| راهاندازی مدیر جلسه | Setting Up Session Manager | آمادهسازی و پیکربندی مدیر جلسه |
| پیکربندی | Configuration | تنظیمات و پارامترهای سیستم |
| مقداردهی اولیه | Initialization | راهاندازی اولیه سیستم |
| تنظیمات جلسه | Session Settings | پارامترهای پیکربندی جلسه |
| ساختار برنامه | Application Structure | ساختار و سازماندهی کد برنامه |
| میانافزار جلسه | Session Middleware | کد واسط برای مدیریت جلسه |
| کوکیهای جلسه | Session Cookies | کوکیهای مربوط به جلسه |
| زمان انقضا | Expiry Time | مدت زمان اعتبار جلسه |
| امنیت جلسه | Session Security | حفاظت از دادههای جلسه |
| مسیریابی | Routing | هدایت درخواستها به هندلرها |