Go ile Web Sunucusu Geliştirme

Go’nun güçlü standart kütüphanesi ile web sunucuları geliştirmek çok kolaydır. Bu yazıda temel web sunucusu ve REST API geliştirmeyi inceleyeceğiz.

Basit Web Sunucusu

package main

import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Merhaba, %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    fmt.Println("Sunucu :8080 adresinde başlıyor")
    http.ListenAndServe(":8080", nil)
}

HTTP Server Yapılandırması

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    server := &http.Server{
        Addr:         ":8080",
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    http.HandleFunc("/api/users", usersHandler)

    fmt.Println("Sunucu başlıyor:", server.Addr)
    if err := server.ListenAndServe(); err != nil {
        fmt.Fatal("Sunucu hatası:", err)
    }
}

JSON API

package main

import (
    "encoding/json"
    "net/http"
)

// User modeli
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

// In-memory veritabanı
var users = []User{
    {ID: 1, Name: "Ahmet Yılmaz", Email: "ahmet@example.com"},
    {ID: 2, Name: "Ayşe Demir", Email: "ayse@example.com"},
}

// Tüm kullanıcıları listele
func getUsersHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

// Tek kullanıcı getir
func getUserHandler(w http.ResponseWriter, r *http.Request) {
    id := r.URL.Path[len("/api/users/"):]

    for _, user := range users {
        if fmt.Sprintf("%d", user.ID) == id {
            w.Header().Set("Content-Type", "application/json")
            json.NewEncoder(w).Encode(user)
            return
        }
    }

    http.NotFound(w, r)
}

// Yeni kullanıcı oluştur
func createUserHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Sadece POST istekleri kabul edilir", http.StatusMethodNotAllowed)
        return
    }

    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Geçersiz JSON", http.StatusBadRequest)
        return
    }

    user.ID = len(users) + 1
    users = append(users, user)

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

func main() {
    http.HandleFunc("/api/users", getUsersHandler)
    http.HandleFunc("/api/users/", getUserHandler)
    http.HandleFunc("/api/users/create", createUserHandler)

    fmt.Println("API :8080 adresinde başlıyor")
    http.ListenAndServe(":8080", nil)
}

Router Kullanımı (Chi)

Chi, hafif ve hızlı bir router’dır:

package main

import (
    "encoding/json"
    "net/http"

    "github.com/go-chi/chi/v5"
)

func main() {
    r := chi.NewRouter()

    // Middleware
    r.Use(middleware.Logger)
    r.Use(middleware.Recoverer)

    // Routes
    r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hoş geldiniz!"))
    })

    r.Get("/api/users", getUsersHandler)
    r.Post("/api/users", createUserHandler)
    r.Get("/api/users/{id}", getUserHandler)

    http.ListenAndServe(":8080", r)
}

Middleware Yazma

// Logger middleware
func Logger(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 %s\n",
            r.Method,
            r.RequestURI,
            time.Since(start),
        )
    })
}

// CORS middleware
func CORS(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 == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }

        next.ServeHTTP(w, r)
    })
}

// Kullanımı
func main() {
    r := chi.NewRouter()
    r.Use(Logger)
    r.Use(CORS)
    // ... routes
}

Template Kullanımı

func pageHandler(w http.ResponseWriter, r *http.Request) {
    tpl, err := template.ParseFiles("templates/page.html")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    data := struct {
        Title   string
        Content string
    }{
        Title:   "Hoş Geldiniz",
        Content: "Bu örnek bir sayfadır",
    }

    tpl.Execute(w, data)
}

Dosya Sunma

// Static dosyaları sunma
fs := http.FileServer(http.Dir("static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))

Production İpuçları

  1. Timeout kullan: İstek zaman aşımı belirleyin
  2. HTTPS kullan: TLS sertifikası kullanın
  3. Rate limiting: İstek sınırlaması ekleyin
  4. Logging: Tüm istekleri loglayın
  5. Graceful shutdown: Düzgün kapatma implement edin
// Graceful shutdown
srv := &http.Server{Addr: ":8080"}

go func() {
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatalf("Sunucu: %v", err)
    }
}()

// Shutdown signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := srv.Shutdown(ctx); err != nil {
    log.Fatal("Shutdown:", err)
}