این جزوه به بررسی جامع برنامهنویسی شبکه و پروتکل HTTP در Go میپردازد. Go به دلیل کتابخانه استاندارد قدرتمند (net/http
) و سادگی در مدیریت همزمانی، برای ساخت سرورهای وب و برنامههای شبکهای بسیار مناسب است. تمام موضوعات درخواستی با جزئیات کامل و مثالهای عملی شرح داده شدهاند.
پکیج net/http
ابزارهای لازم برای ساخت سرورهای HTTP را فراهم میکند. یک سرور HTTP درخواستها را دریافت کرده، پردازش میکند و پاسخ مناسب را ارسال میکند.
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}
func main() {
http.HandleFunc("/", handler)
fmt.Println("Server running on :8080")
http.ListenAndServe(":8080", nil)
}
http://localhost:8080
بروید تا خروجی Hello, World!
را ببینید.http.HandleFunc
: یک مسیر (route) را به یک تابع هندلر متصل میکند.http.ResponseWriter
: برای نوشتن پاسخ به کلاینت استفاده میشود.http.Request
: اطلاعات درخواست (مثل مسیر، هدرها) را نگه میدارد.ListenAndServe
: سرور را روی پورت مشخص راهاندازی میکند.هندلرها توابعی هستند که درخواستهای HTTP را پردازش میکنند. دو نوع اصلی:
http.Handler
:
type CustomHandler struct{}
func (h CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Custom Handler"))
}
func main() {
http.Handle("/", CustomHandler{})
http.ListenAndServe(":8080", nil)
}
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK) // 200
http.Error(w, "Not Found", http.StatusNotFound) // 404
server := &http.Server{
Addr: ":8080",
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
server.ListenAndServe()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server.Shutdown(ctx)
ListenAndServe
.مدیریت درخواستها شامل استخراج اطلاعات از درخواست (مثل Query Parameters، Form Data، و Methodها) و پاسخ مناسب است.
Query Parameters در URL (مثل ?name=Ali
) قرار دارند.
func handler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "Guest"
}
fmt.Fprintf(w, "Hello, %s!", name)
}
برای درخواستهای POST یا PUT که دادههای فرم دارند:
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
r.ParseForm()
username := r.Form.Get("username")
fmt.Fprintf(w, "Welcome, %s!", username)
}
r.ParseMultipartForm
استفاده کنید.func handler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
fmt.Fprintf(w, "GET request received")
case http.MethodPost:
fmt.Fprintf(w, "POST request received")
default:
http.Error(w, "Method not supported", http.StatusMethodNotAllowed)
}
}
path := r.URL.Path // مثلاً /users/123
parts := strings.Split(path, "/")
userID := parts[len(parts)-1]
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Bad request", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Body: %s", body)
ParseForm
یا ReadAll
.JSON فرمت رایج برای تبادل داده در برنامههای وب است. Go با پکیج encoding/json
از آن پشتیبانی میکند.
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func handler(w http.ResponseWriter, r *http.Request) {
user := User{ID: 1, Name: "Ali"}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
}
{"id":1,"name":"Ali"}
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Received: %+v", user)
}
Middleware توابعی هستند که قبل یا بعد از هندلر اجرا میشوند (مثل لاگگیری، احراز هویت).
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
fmt.Printf("%s %s %v\n", r.Method, r.URL.Path, time.Since(start))
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
})
http.ListenAndServe(":8080", loggingMiddleware(mux))
}
enc := json.NewEncoder(w)
for _, user := range users {
enc.Encode(user)
}
type ErrorResponse struct {
Error string `json:"error"`
}
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(ErrorResponse{Error: "Invalid input"})
}
Content-Type
.Decode
یا Encode
.فریمورکهای وب ابزارهایی هستند که توسعه برنامههای وب را سادهتر میکنند. سه فریمورک محبوب در Go عبارتند از Gin، Echo، و Fiber.
go get -u github.com/gin-gonic/gin
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run(":8080")
}
go get -u github.com/labstack/echo/v4
package main
import (
"net/http"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
e.GET("/ping", func(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]string{"message": "pong"})
})
e.Start(":8080")
}
go get -u github.com/gofiber/fiber/v2
package main
import "github.com/gofiber/fiber/v2"
func main() {
app := fiber.New()
app.Get("/ping", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "pong"})
})
app.Listen(":8080")
}
net/http
کافی است؛ برای پروژههای بزرگ، Gin یا Echo توصیه میشود.REST (Representational State Transfer) یک معماری برای طراحی APIهای وب است که از متدهای HTTP (GET، POST، PUT، DELETE) استفاده میکند.
/users
)./users
: لیست کاربران./users/:id
: کاربر خاص.package main
import (
"encoding/json"
"net/http"
"strconv"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
var users = map[int]User{
1: {ID: 1, Name: "Ali"},
}
func main() {
mux := http.NewServeMux()
// دریافت همه کاربران
mux.HandleFunc("GET /users", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
})
// دریافت کاربر خاص
mux.HandleFunc("GET /users/", func(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Path[len("/users/"):]
id, _ := strconv.Atoi(idStr)
user, ok := users[id]
if !ok {
http.Error(w, "User not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(user)
})
// ایجاد کاربر
mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
var user User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, "Invalid input", http.StatusBadRequest)
return
}
user.ID = len(users) + 1
users[user.ID] = user
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(user)
})
http.ListenAndServe(":8080", mux)
}
/v1/users
برای نسخهبندی API استفاده کنید.{
"id": 1,
"name": "Ali",
"links": {
"self": "/users/1"
}
}
{
"error": "Invalid input",
"details": "Name is required"
}
func rateLimit(next http.Handler) http.Handler {
// پیادهسازی محدودیت
}
limit := r.URL.Query().Get("limit")
offset := r.URL.Query().Get("offset")
WebSocket پروتکلی برای ارتباط دوطرفه و بلادرنگ بین سرور و کلاینت است. پکیج github.com/gorilla/websocket
ابزار محبوبی در Go است.
go get -u github.com/gorilla/websocket
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true // در تولید محدود کنید
},
}
func handler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
for {
msgType, msg, err := conn.ReadMessage()
if err != nil {
log.Println(err)
return
}
log.Printf("Received: %s", msg)
err = conn.WriteMessage(msgType, msg)
if err != nil {
log.Println(err)
return
}
}
}
func main() {
http.HandleFunc("/ws", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
var clients = make(map[*websocket.Conn]bool)
var mutex = &sync.Mutex{}
func broadcast(msg []byte) {
mutex.Lock()
defer mutex.Unlock()
for client := range clients {
client.WriteMessage(websocket.TextMessage, msg)
}
}
CheckOrigin
در تولید.کلاینت HTTP برای ارسال درخواستها به سرورهای دیگر استفاده میشود.
resp, err := http.Get("https://api.example.com/data")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
fmt.Println(string(body))
data := []byte(`{"name":"Ali"}`)
resp, err := http.Post("https://api.example.com/users", "application/json", bytes.NewBuffer(data))
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
client := &http.Client{
Timeout: 10 * time.Second,
}
req, err := http.NewRequest("GET", "https://api.example.com/data", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer token")
resp, err := client.Do(req)
if resp.StatusCode != http.StatusOK {
log.Printf("Unexpected status: %s", resp.Status)
}
for i := 0; i < 3; i++ {
resp, err := client.Do(req)
if err == nil && resp.StatusCode == http.StatusOK {
return resp, nil
}
time.Sleep(time.Second * time.Duration(i+1))
}
resp.Body
.برای اجازه دسترسی از دامنههای دیگر:
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == http.MethodOptions {
return
}
next.ServeHTTP(w, r)
})
}
برای جلوگیری از حملات CSRF:
token := generateCSRFToken()
http.SetCookie(w, &http.Cookie{Name: "csrf_token", Value: token})
if r.FormValue("csrf_token") != expectedToken {
http.Error(w, "Invalid CSRF token", http.StatusForbidden)
}
برای فعالسازی HTTPS:
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
w.Header().Set("Content-Security-Policy", "default-src 'self'")
/myapp
├── /handlers
├── /models
├── /middleware
├── main.go
pprof
برای بررسی عملکرد سرور استفاده کنید.func TestHandler(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
handler(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
}
}
این جزوه تمام جنبههای برنامهنویسی شبکه و HTTP در Go را با جزئیات کامل پوشش داد. از ساخت سرورهای ساده تا پیادهسازی REST API، WebSocket، و امنیت، هر بخش با مثالهای عملی و نکات پیشرفته ارائه شد. برای یادگیری عمیقتر:
https://golang.org/doc/