Let's Go پایه‌ها › مسیریابی مبتنی بر روش
قبلی · فهرست · بعدی
فصل 2.5.

مسیریابی مبتنی بر روش (Method-based Routing)

در ادامه، بیایید با رعایت اصول خوب HTTP، برنامه خود را محدود کنیم تا فقط به درخواست‌هایی با روش HTTP (HTTP Method) مناسب پاسخ دهد.

همانطور که در ساخت برنامه خود پیش می‌رویم، هندلرهای home، snippetView و snippetCreate فقط اطلاعات را بازیابی کرده و صفحات را به کاربر نمایش می‌دهند، بنابراین منطقی است که این هندلرها را به درخواست‌های GET محدود کنیم.

برای محدود کردن یک مسیر به یک روش HTTP خاص، می‌توانید الگوی مسیر را با روش HTTP مورد نیاز هنگام اعلام آن پیشوند کنید، به این صورت:

File: main.go
package main

...

func main() {
    mux := http.NewServeMux()
    // Prefix the route patterns with the required HTTP method (for now, we will
    // restrict all three routes to acting on GET requests).
    mux.HandleFunc("GET /{$}", home)
    mux.HandleFunc("GET /snippet/view/{id}", snippetView)
    mux.HandleFunc("GET /snippet/create", snippetCreate)

    log.Print("starting server on :4000")

    err := http.ListenAndServe(":4000", mux)
    log.Fatal(err)
}

همچنین شایان ذکر است که وقتی یک الگوی مسیر را که از روش GET استفاده می‌کند ثبت می‌کنید، هم GET و هم HEAD را مطابقت می‌دهد. همه روش‌های دیگر (مانند POST، PUT و DELETE) نیاز به مطابقت دقیق دارند.

بیایید این تغییر را با استفاده از curl برای ارسال چند درخواست به برنامه خود آزمایش کنیم. اگر شما هم در حال دنبال کردن هستید، با ارسال یک درخواست GET معمولی به http://localhost:4000/ شروع کنید، به این صورت:

$ 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

پاسخ اینجا خوب به نظر می‌رسد. می‌توانیم ببینیم که مسیر ما هنوز کار می‌کند و یک وضعیت 200 OK و یک بدنه پاسخ Hello from Snippetbox دریافت می‌کنیم، درست مثل قبل.

همچنین می‌توانید یک درخواست HEAD برای همان URL ارسال کنید. باید ببینید که این نیز به درستی کار می‌کند و فقط هدرهای پاسخ HTTP را برمی‌گرداند و هیچ بدنه پاسخی ندارد.

$ curl --head 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

در مقابل، بیایید یک درخواست POST به http://localhost:4000/ ارسال کنیم. روش POST برای این مسیر پشتیبانی نمی‌شود، بنابراین باید یک پاسخ خطا مشابه این دریافت کنید:

$ curl -i -d "" localhost:4000/
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Wed, 18 Mar 2024 11:29:23 GMT
Content-Length: 19

Method Not Allowed*

بنابراین این خیلی خوب به نظر می‌رسد. می‌توانیم ببینیم که servemux گو به طور خودکار یک پاسخ 405 Method Not Allowed برای ما ارسال کرده است، شامل یک هدر Allow که روش‌های HTTP را که برای URL درخواست پشتیبانی می‌شوند، فهرست می‌کند.

افزودن یک مسیر و هندلر فقط برای POST (Adding a POST-only Route and Handler)

بیایید همچنین یک هندلر جدید snippetCreatePost به کد خود اضافه کنیم، که بعداً برای ایجاد و ذخیره یک قطعه جدید در یک پایگاه داده استفاده خواهیم کرد. از آنجا که ایجاد و ذخیره یک قطعه یک عمل غیرقابل تکرار است که وضعیت سرور ما را تغییر می‌دهد، می‌خواهیم مطمئن شویم که این هندلر فقط به درخواست‌های POST عمل می‌کند.

در مجموع، می‌خواهیم هندلر و مسیر چهارم ما به این صورت باشد:

الگوی مسیر هندلر عمل
GET /{$} home نمایش صفحه اصلی
GET /snippet/view/{id} snippetView نمایش یک قطعه خاص
GET /snippet/create snippetCreate نمایش فرم برای ایجاد یک قطعه جدید
POST /snippet/create snippetCreatePost ذخیره یک قطعه جدید

بیایید کد لازم را به فایل main.go خود اضافه کنیم، به این صورت:

File: main.go
package main

...

// Add a snippetCreatePost handler function.
func snippetCreatePost(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Save a new snippet..."))
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("GET /{$}", home)
    mux.HandleFunc("GET /snippet/view/{id}", snippetView)
    mux.HandleFunc("GET /snippet/create", snippetCreate)
    // Create the new route, which is restricted to POST requests only.
    mux.HandleFunc("POST /snippet/create", snippetCreatePost)
    
    log.Print("starting server on :4000")

    err := http.ListenAndServe(":4000", mux)
    log.Fatal(err)
}

توجه داشته باشید که اعلام دو (یا بیشتر) مسیر جداگانه که روش‌های HTTP متفاوتی دارند اما الگوی یکسانی دارند، کاملاً مجاز است، مانند آنچه که در اینجا با "GET /snippet/create" و "POST /snippet/create" انجام می‌دهیم.

اگر برنامه را مجدداً راه‌اندازی کنید و سعی کنید با استفاده از مسیر URL /snippet/create درخواست‌هایی ارسال کنید، باید اکنون پاسخ‌های متفاوتی بسته به روش درخواست که استفاده می‌کنید، ببینید.

$ curl -i localhost:4000/snippet/create
HTTP/1.1 200 OK
Date: Wed, 18 Mar 2024 11:29:23 GMT
Content-Length: 50
Content-Type: text/plain; charset=utf-8

Display a form for creating a new snippet...

$ curl -i -d "" localhost:4000/snippet/create
HTTP/1.1 200 OK
Date: Wed, 18 Mar 2024 11:29:23 GMT
Content-Length: 21
Content-Type: text/plain; charset=utf-8

Save a new snippet...

$ curl -i -X DELETE localhost:4000/snippet/create
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, POST
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Wed, 18 Mar 2024 11:29:23 GMT
Content-Length: 19

Method Not Allowed

اطلاعات اضافی

اولویت روش (Method Precedence)

قانون الگوی خاص‌تر برنده می‌شود (More Specific Pattern Wins) نیز در صورتی که الگوهای مسیر به دلیل یک روش HTTP همپوشانی داشته باشند، اعمال می‌شود.

مهم است که بدانید یک الگوی مسیری که شامل یک روش نیست — مانند "/article/{id}" — با درخواست‌های HTTP با هر روشی مطابقت خواهد داشت. در مقابل، مسیری مانند "POST /article/{id}" فقط با درخواست‌هایی که روش POST دارند مطابقت خواهد داشت. بنابراین اگر مسیرهای همپوشانی "/article/{id}" و "POST /article/{id}" را در برنامه خود اعلام کنید، مسیر "POST /article/{id}" اولویت خواهد داشت.

نام‌گذاری هندلر (Handler Naming)

همچنین می‌خواهم تأکید کنم که هیچ راه درست یا غلطی برای نام‌گذاری هندلرهای خود در Go وجود ندارد.

در این پروژه، ما از یک قرارداد پیروی می‌کنیم که نام هندلرهایی که با درخواست‌های POST سروکار دارند را با کلمه ‘Post’ پسوند می‌دهیم. به این صورت:

الگوی مسیر هندلر عمل
GET /snippet/create snippetCreate نمایش فرم برای ایجاد یک قطعه جدید
POST /snippet/create snippetCreatePost ایجاد یک قطعه جدید

اما در کار خودتان لازم نیست که از این الگو پیروی کنید. به عنوان مثال، می‌توانید نام هندلرها را با کلمات ‘get’ و ‘post’ پیشوند کنید، به این صورت:

الگوی مسیر هندلر عمل
GET /snippet/create getSnippetCreate نمایش فرم برای ایجاد یک قطعه جدید
POST /snippet/create postSnippetCreate ایجاد یک قطعه جدید

یا حتی به هندلرها نام‌های کاملاً متفاوتی بدهید. به عنوان مثال:

الگوی مسیر هندلر عمل
GET /snippet/create newSnippetForm نمایش فرم برای ایجاد یک قطعه جدید
POST /snippet/create createSnippet ایجاد یک قطعه جدید

به طور کلی، شما در Go آزادی دارید که یک الگوی نام‌گذاری برای هندلرهای خود انتخاب کنید که برای شما کار کند و با ذهن شما سازگار باشد.

روترهای شخص ثالث

عملکرد مسیریابی مبتنی بر کاراکترهای جایگزین و روش که در دو فصل گذشته استفاده کرده‌ایم نسبتاً جدید در Go است — این ویژگی تنها در نسخه 1.22 به کتابخانه استاندارد اضافه شد. در حالی که این یک افزودنی بسیار خوشایند به زبان و یک بهبود بزرگ است، ممکن است متوجه شوید که هنوز هم مواقعی وجود دارد که عملکرد مسیریابی کتابخانه استاندارد همه چیزهایی که نیاز دارید را فراهم نمی‌کند.

به عنوان مثال، موارد زیر در حال حاضر پشتیبانی نمی‌شوند:

اگر به این ویژگی‌ها در برنامه خود نیاز دارید، باید از یک بسته روتر شخص ثالث استفاده کنید. آنهایی که من توصیه می‌کنم httprouter، chi، flow و gorilla/mux هستند، و می‌توانید مقایسه‌ای از آنها و راهنمایی درباره اینکه کدام یک را استفاده کنید در این پست وبلاگ پیدا کنید.

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
مسیریابی مبتنی بر روش Method-based Routing تکنیکی برای محدود کردن مسیرها به روش‌های HTTP خاص
روش HTTP HTTP Method نوع عملیاتی که یک درخواست HTTP می‌خواهد انجام دهد (مانند GET، POST، PUT، DELETE)
هندلر Handler تابعی که درخواست‌های HTTP را پردازش می‌کند
الگوی مسیر Route Pattern الگویی که برای تطبیق با URL‌های درخواست استفاده می‌شود
وضعیت پاسخ Response Status کد عددی که نتیجه پردازش درخواست HTTP را نشان می‌دهد (مانند 200 OK، 405 Method Not Allowed)
هدر پاسخ Response Header متادیتای اضافی که همراه با پاسخ HTTP ارسال می‌شود
بدنه پاسخ Response Body محتوای اصلی که در پاسخ HTTP برگردانده می‌شود
درخواست غیرقابل تکرار Non-idempotent Request درخواستی که هر بار اجرا می‌تواند نتایج متفاوتی داشته باشد و وضعیت سرور را تغییر می‌دهد
همپوشانی مسیرها Route Overlap وضعیتی که در آن چندین الگوی مسیر می‌توانند با یک URL یکسان مطابقت داشته باشند
اولویت روش Method Precedence قوانینی که تعیین می‌کنند کدام مسیر در صورت همپوشانی باید استفاده شود