ساخت درخواستهای HTTP در Go یکی از قابلیتهای کلیدی زبان برنامهنویسی Go است که به توسعهدهندگان امکان ارتباط با سرورهای دیگر را میدهد. بسته net/http در Go ابزارهای قدرتمندی برای ایجاد درخواستهای HTTP به عنوان کلاینت فراهم میکند. در این آموزش، شما یک برنامه ایجاد خواهید کرد که انواع درخواستهای HTTP مانند GET و POST را انجام میدهد، هدرهای سفارشی اضافه میکند و زمانبندی برای جلوگیری از تأخیرهای طولانی تنظیم میکند.
پیشنیازها برای ساخت درخواستهای HTTP
برای شروع ساخت درخواستهای HTTP در Go، به یک محیط توسعه Go روی سیستم خود نیاز دارید. همچنین، دانش اولیه از زبان Go و مفاهیم HTTP مفید خواهد بود. اطمینان حاصل کنید که Go روی سیستم شما نصب شده و آماده استفاده است.
مرحله اول: ایجاد درخواست GET
بسته net/http در Go روشهای مختلفی برای ارسال درخواستهای HTTP ارائه میدهد. در این بخش، ابتدا با استفاده از تابع http.Get یک درخواست GET ساده ایجاد میکنید و سپس از http.Request برای سفارشیسازی بیشتر استفاده خواهید کرد.
استفاده از http.Get برای ارسال درخواست
تابع http.Get روشی سریع و ساده برای ارسال درخواست GET است. برای شروع، یک دایرکتوری برای پروژه خود ایجاد کنید:
mkdir projects cd projects mkdir httpclient cd httpclient
سپس، فایل main.go را با ویرایشگر خود باز کنید و کد زیر را وارد کنید:
package main
import (
"errors"
"fmt"
"net/http"
"os"
"time"
)
const serverPort = 3333
func main() {
go func() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("server: %s /\n", r.Method)
})
server := http.Server{
Addr: fmt.Sprintf(":%d", serverPort),
Handler: mux,
}
if err := server.ListenAndServe(); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
fmt.Printf("error running http server: %s\n", err)
}
}
}()
time.Sleep(100 * time.Millisecond)
requestURL := fmt.Sprintf("http://localhost:%d", serverPort)
res, err := http.Get(requestURL)
if err != nil {
fmt.Printf("error making http request: %s\n", err)
os.Exit(1)
}
fmt.Printf("client: got response!\n")
fmt.Printf("client: status code: %d\n", res.StatusCode)
}
این کد یک سرور HTTP ساده راهاندازی میکند و با استفاده از http.Get یک درخواست GET به آن ارسال میکند. برای اجرای برنامه، دستور زیر را اجرا کنید:
go run main.go
خروجی مشابه این خواهد بود:
server: GET / client: got response! client: status code: 200
استفاده از http.Request برای درخواست GET
برای کنترل بیشتر روی درخواست، میتوانید از http.Request استفاده کنید. فایل main.go را باز کنید و آن را بهروز کنید تا پاسخ JSON جعلی را برگرداند و از http.Request استفاده کند:
package main
import (
"errors"
"fmt"
"io"
"net/http"
"os"
"time"
)
const serverPort = 3333
func main() {
go func() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("server: %s /\n", r.Method)
fmt.Fprintf(w, `{"message": "hello!"}`)
})
server := http.Server{
Addr: fmt.Sprintf(":%d", serverPort),
Handler: mux,
}
if err := server.ListenAndServe(); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
fmt.Printf("error running http server: %s\n", err)
}
}
}()
time.Sleep(100 * time.Millisecond)
requestURL := fmt.Sprintf("http://localhost:%d", serverPort)
req, err := http.NewRequest(http.MethodGet, requestURL, nil)
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
fmt.Printf("client: got response!\n")
fmt.Printf("client: status code: %d\n", res.StatusCode)
resBody, err := io.ReadAll(res.Body)
if err != nil {
fmt.Printf("client: could not read response body: %s\n", err)
os.Exit(1)
}
fmt.Printf("client: response body: %s\n", resBody)
}
این کد از http.NewRequest برای ایجاد درخواست و http.DefaultClient.Do برای ارسال آن استفاده میکند. برای اجرا:
go run main.go
خروجی شامل بدنه پاسخ خواهد بود:
server: GET /
client: got response!
client: status code: 200
client: response body: {"message": "hello!"}
مرحله دوم: ارسال درخواست POST
درخواست POST برای ارسال داده به سرور استفاده میشود. در این بخش، برنامه را برای ارسال درخواست POST با بدنه بهروزرسانی میکنید.
بهروزرسانی برای درخواست POST
فایل main.go را باز کنید و بستههای جدید را به import اضافه کنید:
import ( "bytes" "errors" "fmt" "io" "net/http" "os" "strings" "time" )
سپس، handler سرور را برای نمایش اطلاعات درخواست بهروزرسانی کنید:
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("server: %s /\n", r.Method)
fmt.Printf("server: query id: %s\n", r.URL.Query().Get("id"))
fmt.Printf("server: content-type: %s\n", r.Header.Get("content-type"))
fmt.Printf("server: headers:\n")
for headerName, headerValue := range r.Header {
fmt.Printf("\t%s = %s\n", headerName, strings.Join(headerValue, ", "))
}
reqBody, err := io.ReadAll(r.Body)
if err != nil {
fmt.Printf("server: could not read request body: %s\n", err)
}
fmt.Printf("server: request body: %s\n", reqBody)
fmt.Fprintf(w, `{"message": "hello!"}`)
})
در نهایت، کد درخواست را برای ارسال POST بهروزرسانی کنید:
time.Sleep(100 * time.Millisecond)
jsonBody := []byte(`{"client_message": "hello, server!"}`)
bodyReader := bytes.NewReader(jsonBody)
requestURL := fmt.Sprintf("http://localhost:%d?id=1234", serverPort)
req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader)
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
برای اجرا:
go run main.go
خروجی شامل اطلاعات اضافی خواهد بود:
server: POST /
server: query id: 1234
server: content-type:
server: headers:
Accept-Encoding = gzip
User-Agent = Go-http-client/1.1
Content-Length = 36
server: request body: {"client_message": "hello, server!"}
client: got response!
client: status code: 200
client: response body: {"message": "hello!"}
مرحله سوم: سفارشیسازی درخواست HTTP
برای بهبود درخواستهای HTTP، میتوانید هدرها و زمانبندی را اضافه کنید. در این بخش، هدر Content-Type و timeout را تنظیم میکنید.
افزودن هدر Content-Type و Timeout
فایل main.go را باز کنید و درخواست را بهروزرسانی کنید:
req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader)
if err != nil {
fmt.Printf("client: could not create request: %s\n", err)
os.Exit(1)
}
req.Header.Set("Content-Type", "application/json")
client := http.Client{
Timeout: 30 * time.Second,
}
res, err := client.Do(req)
if err != nil {
fmt.Printf("client: error making http request: %s\n", err)
os.Exit(1)
}
برای تست timeout، handler سرور را با تأخیر 35 ثانیهای بهروزرسانی کنید:
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("server: %s /\n", r.Method)
fmt.Printf("server: query id: %s\n", r.URL.Query().Get("id"))
fmt.Printf("server: content-type: %s\n", r.Header.Get("content-type"))
fmt.Printf("server: headers:\n")
for headerName, headerValue := range r.Header {
fmt.Printf("\t%s = %s\n", headerName, strings.Join(headerValue, ", "))
}
reqBody, err := io.ReadAll(r.Body)
if err != nil {
fmt.Printf("server: could not read request body: %s\n", err)
}
fmt.Printf("server: request body: %s\n", reqBody)
fmt.Fprintf(w, `{"message": "hello!"}`)
time.Sleep(35 * time.Second)
})
برای اجرا:
go run main.go
خروجی نشاندهنده خطای timeout خواهد بود:
server: POST /
server: query id: 1234
server: content-type: application/json
server: headers:
Content-Type = application/json
Accept-Encoding = gzip
User-Agent = Go-http-client/1.1
Content-Length = 36
server: request body: {"client_message": "hello, server!"}
client: error making http request: Post "http://localhost:3333?id=1234": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
exit status 1
نتیجهگیری
در این آموزش، شما نحوه ساخت درخواستهای HTTP در Go را با استفاده از بسته net/http آموختید. ابتدا یک درخواست GET ساده ایجاد کردید، سپس آن را به POST با بدنه تغییر دادید و در نهایت هدرها و timeout را برای سفارشیسازی اضافه کردید. این مهارتها به شما کمک میکند تا برنامههای کلاینت HTTP قدرتمندی در Go ایجاد کنید.