context درخواست چگونه کار میکند
هر http.Request که middleware و handlerهای ما پردازش میکنند یک شیء context.Context در خود دارد که میتوانیم از آن برای ذخیره اطلاعات در طول عمر درخواست استفاده کنیم.
همانطور که قبلاً اشاره کردم، در یک برنامه وب یک مورد استفاده رایج برای این کار، انتقال اطلاعات بین قطعات middleware و handlerهای دیگر است.
در مورد ما، میخواهیم از آن برای بررسی اینکه آیا یک کاربر یک بار در یک middleware احراز هویت شده است استفاده کنیم، و اگر احراز هویت شده است، این اطلاعات را در دسترس تمام middleware و handlerهای دیگر خود قرار دهیم.
بیایید با کمی تئوری شروع کنیم و نحو کار با context درخواست را توضیح دهیم. سپس، در فصل بعدی، دوباره کمی عملیتر میشویم و نشان میدهیم که چگونه در عمل از آن در برنامه خود استفاده کنیم.
نحو context درخواست
کد پایه برای افزودن اطلاعات به context یک درخواست به این صورت است:
// Where r is a *http.Request... ctx := r.Context() ctx = context.WithValue(ctx, "isAuthenticated", true) r = r.WithContext(ctx)
بیایید این را خط به خط بررسی کنیم.
- ابتدا، از متد
r.Context()برای بازیابی context موجود از یک درخواست استفاده میکنیم و آن را به متغیرctxاختصاص میدهیم. - سپس از متد
context.WithValue()برای ایجاد یک کپی جدید از context موجود استفاده میکنیم که شامل کلید"isAuthenticated"و مقدارtrueاست. - سپس در نهایت از متد
r.WithContext()برای ایجاد یک کپی از درخواست حاوی context جدید خود استفاده میکنیم.
همچنین باید اشاره کنم که، برای وضوح، آن قطعه کد را کمی بیشتر از آنچه لازم است طولانی کردم. معمولاً آن را به این صورت مینویسند:
ctx = context.WithValue(r.Context(), "isAuthenticated", true) r = r.WithContext(ctx)
پس اینطور است که داده را به context یک درخواست اضافه میکنید. اما در مورد بازیابی دوباره آن چطور؟
نکته مهمی که باید توضیح دهم این است که، در پشت صحنه، مقادیر context درخواست با نوع any ذخیره میشوند. و این یعنی که، پس از بازیابی آنها از context، قبل از استفاده باید آنها را به نوع اصلی خود تبدیل کنید.
برای بازیابی یک مقدار باید از متد r.Context().Value() استفاده کنیم، به این صورت:
isAuthenticated, ok := r.Context().Value("isAuthenticated").(bool) if !ok { return errors.New("could not convert value to bool") }
اجتناب از برخورد کلیدها
در نمونههای کد بالا، از رشته "isAuthenticated" به عنوان کلید برای ذخیره و بازیابی داده از context یک درخواست استفاده کردم. اما این توصیه نمیشود چون خطر این وجود دارد که بستههای شخص ثالث دیگر مورد استفاده برنامه شما نیز بخواهند داده را با استفاده از کلید "isAuthenticated" ذخیره کنند — و این باعث برخورد نام میشود.
برای اجتناب از این، یک عمل خوب این است که نوع سفارشی خود را ایجاد کنید که میتوانید برای کلیدهای context خود استفاده کنید. با گسترش کد نمونه ما، خیلی بهتر است که کاری شبیه به این انجام دهید:
// Declare a custom "contextKey" type for your context keys. type contextKey string // Create a constant with the type contextKey that we can use. const isAuthenticatedContextKey = contextKey("isAuthenticated") ... // Set the value in the request context, using our isAuthenticatedContextKey // constant as the key. ctx := r.Context() ctx = context.WithValue(ctx, isAuthenticatedContextKey, true) r = r.WithContext(ctx) ... // Retrieve the value from the request context using our constant as the key. isAuthenticated, ok := r.Context().Value(isAuthenticatedContextKey).(bool) if !ok { return errors.New("could not convert value to bool") }