سفارشیسازی پاسخها (Customizing Responses)
به طور پیشفرض، هر پاسخی که هندلرهای شما ارسال میکنند دارای کد وضعیت HTTP (HTTP Status Code) 200 OK است (که به کاربر نشان میدهد درخواست او دریافت و با موفقیت پردازش شده است)، به علاوه سه هدر سیستمتولید (System-generated) خودکار: یک هدر Date، و Content-Length و Content-Type بدنه پاسخ (Response Body).
$ curl -i localhost:4000/ HTTP/1.1 200 OK Date: Wed, 18 Mar 2024 11:29:23 GMT Content-Length: 21 Content-Type: text/plain; charset=utf-8 Hello from Snippetbox
در این فصل به چگونگی سفارشیسازی هدرهای پاسخ که هندلرهای شما ارسال میکنند میپردازیم و همچنین به چند روش دیگر برای ارسال پاسخهای متنی ساده به کاربران نگاه میکنیم.
کدهای وضعیت HTTP (HTTP Status Codes)
ابتدا، بیایید هندلر snippetCreatePost خود را بهروزرسانی کنیم تا به جای 200 OK کد وضعیت 201 Created ارسال کند. برای انجام این کار، میتوانید از متد w.WriteHeader() در هندلرهای خود به این صورت استفاده کنید:
package main ... func snippetCreatePost(w http.ResponseWriter, r *http.Request) { // Use the w.WriteHeader() method to send a 201 status code. w.WriteHeader(201) // Then w.Write() method to write the response body as normal. w.Write([]byte("Save a new snippet...")) } ...
(بله، این کمی احمقانه است زیرا هندلر هنوز چیزی ایجاد نمیکند! اما به خوبی الگوی تنظیم یک کد وضعیت سفارشی را نشان میدهد.)
اگرچه این تغییر به نظر ساده میآید، اما چند نکته ظریف وجود دارد که باید توضیح دهم:
فقط میتوانید یک بار
w.WriteHeader()را برای هر پاسخ فراخوانی کنید و پس از اینکه کد وضعیت نوشته شد، نمیتوان آن را تغییر داد. اگر سعی کنیدw.WriteHeader()را برای بار دوم فراخوانی کنید، Go یک پیام هشدار ثبت خواهد کرد.اگر
w.WriteHeader()را بهطور صریح فراخوانی نکنید، اولین فراخوانی بهw.Write()بهطور خودکار یک کد وضعیت200به کاربر ارسال میکند. بنابراین، اگر میخواهید یک کد وضعیت غیر 200 ارسال کنید، بایدw.WriteHeader()را قبل از هر فراخوانی بهw.Write()فراخوانی کنید.
سرور را مجدداً راهاندازی کنید، سپس از curl برای ارسال یک درخواست POST به http://localhost:4000/snippet/create استفاده کنید. باید ببینید که پاسخ HTTP اکنون دارای یک کد وضعیت 201 Created مشابه این است:
$ curl -i -d "" http://localhost:4000/snippet/create HTTP/1.1 201 Created Date: Wed, 18 Mar 2024 11:29:23 GMT Content-Length: 21 Content-Type: text/plain; charset=utf-8 Save a new snippet...
ثابتهای کد وضعیت (Status Code Constants)
پکیج net/http ثابتهایی برای کدهای وضعیت HTTP فراهم میکند که میتوانیم به جای نوشتن شماره کد وضعیت خودمان از آنها استفاده کنیم. استفاده از این ثابتها یک عمل خوب است زیرا به جلوگیری از اشتباهات ناشی از تایپ اشتباه کمک میکند و همچنین میتواند کد شما را واضحتر و خودمستند کند — بهویژه هنگام کار با کدهای وضعیت کمتر استفادهشده.
بیایید هندلر snippetCreatePost خود را بهروزرسانی کنیم تا از ثابت http.StatusCreated به جای عدد 201 استفاده کند، به این صورت:
package main ... func snippetCreatePost(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) w.Write([]byte("Save a new snippet...")) } ...
سفارشیسازی هدرها (Customizing Headers)
شما همچنین میتوانید هدرهای HTTP ارسالشده به کاربر را با تغییر نقشه هدر پاسخ (Response Header Map) سفارشی کنید. احتمالاً رایجترین کاری که میخواهید انجام دهید این است که یک هدر اضافی به نقشه اضافه کنید، که میتوانید با استفاده از متد w.Header().Add() این کار را انجام دهید.
برای نشان دادن این موضوع، بیایید یک هدر Server: Go به پاسخی که هندلر home ما ارسال میکند اضافه کنیم. اگر در حال دنبال کردن هستید، کد هندلر را به این صورت بهروزرسانی کنید:
package main ... func home(w http.ResponseWriter, r *http.Request) { // Use the Header().Add() method to add a 'Server: Go' header to the // response header map. The first parameter is the header name, and // the second parameter is the header value. w.Header().Add("Server", "Go") w.Write([]byte("Hello from Snippetbox")) } ...
بیایید این را امتحان کنیم و با استفاده از curl یک درخواست دیگر به http://localhost:4000/ ارسال کنیم. این بار باید ببینید که پاسخ اکنون شامل یک هدر جدید Server: Go است، به این صورت:
$ curl -i http://localhost:4000 HTTP/1.1 200 OK Server: Go Date: Wed, 18 Mar 2024 11:29:23 GMT Content-Length: 21 Content-Type: text/plain; charset=utf-8 Hello from Snippetbox
نوشتن بدنههای پاسخ (Writing Response Bodies)
تا کنون در این کتاب از w.Write() برای ارسال بدنههای پاسخ HTTP خاص به کاربر استفاده کردهایم. و در حالی که این سادهترین و اساسیترین راه برای ارسال یک پاسخ است، در عمل بسیار رایجتر است که مقدار http.ResponseWriter خود را به یک تابع دیگر که پاسخ را برای شما مینویسد، ارسال کنید.
در واقع، توابع زیادی وجود دارند که میتوانید برای نوشتن یک پاسخ استفاده کنید!
نکته کلیدی که باید درک کنید این است… زیرا مقدار http.ResponseWriter در هندلرهای شما یک متد Write() دارد، آن io.Writer را برآورده میکند.
اگر در Go تازهکار هستید، ممکن است مفهوم اینترفیسها کمی گیجکننده باشد و نمیخواهم در حال حاضر خیلی به آن بپردازم. اما در سطح عملی، این به این معناست که هر تابعی که یک پارامتر io.Writer دارد، میتوانید مقدار http.ResponseWriter خود را به آن ارسال کنید و هر چیزی که نوشته میشود به عنوان بدنه پاسخ HTTP ارسال خواهد شد.
این به این معناست که میتوانید از توابع کتابخانه استاندارد مانند io.WriteString() و خانواده fmt.Fprint*() (همه آنها یک پارامتر io.Writer میپذیرند) برای نوشتن بدنههای پاسخ متنی ساده نیز استفاده کنید.
// Instead of this... w.Write([]byte("Hello world")) // You can do this... io.WriteString(w, "Hello world") fmt.Fprint(w, "Hello world")
بیایید از این استفاده کنیم و کد در هندلر snippetView خود را بهروزرسانی کنیم تا از تابع fmt.Fprintf() استفاده کند. این به ما اجازه میدهد تا مقدار id وایلدکارد را در پیام بدنه پاسخ خود جایگذاری کنیم و پاسخ را در یک خط بنویسیم، به این صورت:
package main ... func snippetView(w http.ResponseWriter, r *http.Request) { id, err := strconv.Atoi(r.PathValue("id")) if err != nil || id < 1 { http.NotFound(w, r) return } fmt.Fprintf(w, "Display a specific snippet with ID %d...", id) } ...
اطلاعات اضافی
تشخیص محتوا (Content Sniffing)
برای تنظیم خودکار هدر Content-Type، Go محتوای بدنه پاسخ را تشخیص میدهد با استفاده از تابع http.DetectContentType(). اگر این تابع نتواند نوع محتوا را حدس بزند، Go به جای آن هدر Content-Type: application/octet-stream را تنظیم میکند.
تابع http.DetectContentType() بهطور کلی به خوبی کار میکند، اما یک مشکل رایج برای توسعهدهندگان وب این است که نمیتواند JSON را از متن ساده تشخیص دهد. بنابراین، بهطور پیشفرض، پاسخهای JSON با هدر Content-Type: text/plain; charset=utf-8 ارسال میشوند. میتوانید با تنظیم دستی هدر صحیح در هندلر خود از این اتفاق جلوگیری کنید، به این صورت:
w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"name":"letsgofa"}`))
دستکاری نقشه هدر (Manipulating the Header Map)
در این فصل از w.Header().Add() برای افزودن یک هدر جدید به نقشه هدر پاسخ استفاده کردیم. اما همچنین متدهای Set()، Del()، Get() و Values() وجود دارند که میتوانید برای دستکاری و خواندن از نقشه هدر استفاده کنید.
// Set a new cache-control header. If an existing "Cache-Control" header exists // it will be overwritten. w.Header().Set("Cache-Control", "public, max-age=31536000") // In contrast, the Add() method appends a new "Cache-Control" header and can // be called multiple times. w.Header().Add("Cache-Control", "public") w.Header().Add("Cache-Control", "max-age=31536000") // Delete all values for the "Cache-Control" header. w.Header().Del("Cache-Control") // Retrieve the first value for the "Cache-Control" header. w.Header().Get("Cache-Control") // Retrieve a slice of all values for the "Cache-Control" header. w.Header().Values("Cache-Control")
قانونیسازی هدر (Header Canonicalization)
هنگامی که از متدهای Set()، Add()، Del()، Get() و Values() بر روی نقشه هدر استفاده میکنید، نام هدر همیشه با استفاده از تابع textproto.CanonicalMIMEHeaderKey() قانونیسازی میشود. این تابع اولین حرف و هر حرفی که بعد از یک خط تیره میآید را به حروف بزرگ تبدیل میکند و بقیه حروف را به حروف کوچک تبدیل میکند. این به این معناست که هنگام فراخوانی این متدها، نام هدر غیر حساس به حروف بزرگ و کوچک است.
اگر نیاز دارید که از این رفتار قانونیسازی جلوگیری کنید، میتوانید نقشه هدر زیرین را بهطور مستقیم ویرایش کنید. این نقشه در پشت صحنه نوع map[string][]string دارد. برای مثال:
w.Header()["X-XSS-Protection"] = []string{"1; mode=block"}
واژهنامه اصطلاحات فنی
| اصطلاح فارسی | معادل انگلیسی | توضیح |
|---|---|---|
| سفارشیسازی پاسخها | Customizing Responses | تنظیم و تغییر جنبههای مختلف پاسخ HTTP مانند هدرها، کد وضعیت و بدنه پاسخ |
| کد وضعیت HTTP | HTTP Status Code | عددی سه رقمی که نتیجه پردازش درخواست HTTP را نشان میدهد |
| هدر سیستمتولید | System-generated Header | هدرهایی که به طور خودکار توسط سرور HTTP تولید میشوند |
| بدنه پاسخ | Response Body | محتوای اصلی که در پاسخ HTTP به کاربر ارسال میشود |
| نقشه هدر پاسخ | Response Header Map | مجموعهای از جفتهای کلید-مقدار که هدرهای HTTP را در پاسخ نگهداری میکند |
| تشخیص محتوا | Content Sniffing | فرآیند تشخیص خودکار نوع محتوا با بررسی دادههای واقعی |
| قانونیسازی هدر | Header Canonicalization | فرآیند استانداردسازی نامهای هدر به یک فرمت یکسان |
| نویسنده | Writer | اینترفیسی در Go که قابلیت نوشتن داده را فراهم میکند |
| ثابتهای کد وضعیت | Status Code Constants | مقادیر ثابت تعریف شده در Go برای کدهای وضعیت HTTP |
| دستکاری نقشه هدر | Header Map Manipulation | عملیات افزودن، حذف یا تغییر هدرهای HTTP در پاسخ |