Pembangunan pelayan web di Golang - daripada mudah kepada kompleks

Pembangunan pelayan web di Golang - daripada mudah kepada kompleks

Lima tahun lalu saya mulakan membangunkan Gophish, ini memberi peluang untuk belajar Golang. Saya menyedari bahawa Go ialah bahasa yang berkuasa, dilengkapi dengan banyak perpustakaan. Go adalah serba boleh: khususnya, ia boleh digunakan untuk membangunkan aplikasi sebelah pelayan tanpa sebarang masalah.

Artikel ini adalah tentang menulis pelayan dalam Go. Mari kita mulakan dengan perkara mudah seperti "Hello world!" dan berakhir dengan aplikasi dengan keupayaan berikut:

- Menggunakan Let's Encrypt untuk HTTPS.
β€” Berfungsi sebagai penghala API.
β€” Bekerja dengan perisian tengah.
β€” Pemprosesan fail statik.
- Penutupan yang betul.

Skillbox mengesyorkan: Kursus praktikal "Pembangun Python dari awal".

Kami mengingatkan: untuk semua pembaca "Habr" - diskaun sebanyak 10 rubel apabila mendaftar dalam mana-mana kursus Skillbox menggunakan kod promosi "Habr".

Hai dunia!

Anda boleh membuat pelayan web dalam Go dengan cepat. Berikut ialah contoh menggunakan pengendali yang mengembalikan "Hello, dunia!" yang dijanjikan di atas.

package main
 
import (
"fmt"
"net/http"
)
 
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World!")
})
http.ListenAndServe(":80", nil)
}

Selepas ini, jika anda menjalankan aplikasi dan membuka halaman localhost, maka anda akan segera melihat teks "Hello, dunia!" (jika semuanya berfungsi dengan betul, sudah tentu).

Kami akan menggunakan pengendali beberapa kali kemudian, tetapi mula-mula mari kita fahami cara semuanya berfungsi.

bersih / http

Contoh menggunakan pakej net/http, ia adalah alat utama dalam Go untuk membangunkan kedua-dua pelayan dan klien HTTP. Untuk memahami kod tersebut, mari kita fahami maksud tiga elemen penting: http.Handler, http.ServeMux dan http.Server.

Pengendali HTTP

Apabila kami menerima permintaan, pengendali menganalisisnya dan menjana respons. Pengendali dalam Go dilaksanakan seperti berikut:

type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
}

Contoh pertama menggunakan fungsi pembantu http.HandleFunc. Ia membungkus fungsi lain, yang seterusnya mengambil http.ResponseWriter dan http.Request ke dalam ServeHTTP.

Dalam erti kata lain, pengendali di Golang dibentangkan dalam antara muka tunggal, yang memberikan banyak pilihan kepada pengaturcara. Jadi, sebagai contoh, middleware dilaksanakan menggunakan pengendali, di mana ServeHTTP mula-mula melakukan sesuatu dan kemudian memanggil kaedah ServeHTTP pengendali lain.

Seperti yang dinyatakan di atas, pengendali hanya menjana respons kepada permintaan. Tetapi pengendali tertentu yang manakah harus digunakan pada masa tertentu?

Permintaan Penghalaan

Untuk membuat pilihan yang tepat, gunakan pemultipleks HTTP. Dalam beberapa perpustakaan ia dipanggil muxer atau penghala, tetapi semuanya adalah perkara yang sama. Fungsi pemultipleks adalah untuk menganalisis laluan permintaan dan memilih pengendali yang sesuai.

Jika anda memerlukan sokongan untuk penghalaan yang kompleks, maka lebih baik menggunakan perpustakaan pihak ketiga. Beberapa yang paling maju - gorila/mux ΠΈ go-chi/chi, perpustakaan ini memungkinkan untuk melaksanakan pemprosesan perantaraan tanpa sebarang masalah. Dengan bantuan mereka, anda boleh mengkonfigurasi penghalaan kad bebas dan melaksanakan beberapa tugas lain. Kelebihan mereka ialah keserasian dengan pengendali HTTP standard. Hasilnya, anda boleh menulis kod mudah yang boleh diubah suai pada masa hadapan.

Bekerja dengan rangka kerja yang kompleks dalam situasi biasa akan memerlukan penyelesaian bukan standard, dan ini merumitkan penggunaan pengendali lalai dengan ketara. Untuk mencipta sebahagian besar aplikasi, gabungan pustaka lalai dan penghala mudah sudah memadai.

Pemprosesan Pertanyaan

Di samping itu, kami memerlukan komponen yang akan "mendengar" untuk sambungan masuk dan mengalihkan semua permintaan kepada pengendali yang betul. http.Server boleh mengendalikan tugas ini dengan mudah.

Berikut menunjukkan bahawa pelayan bertanggungjawab untuk semua tugas yang berkaitan dengan pemprosesan sambungan. Ini, sebagai contoh, berfungsi menggunakan protokol TLS. Untuk melaksanakan panggilan http.ListenAndServer, pelayan HTTP standard digunakan.

Sekarang mari kita lihat contoh yang lebih kompleks.

Menambah Let's Encrypt

Secara lalai, aplikasi kami berjalan melalui protokol HTTP, tetapi disyorkan untuk menggunakan protokol HTTPS. Ini boleh dilakukan tanpa masalah dalam Go. Jika anda telah menerima sijil dan kunci peribadi, maka sudah cukup untuk mendaftarkan ListenAndServeTLS dengan sijil dan fail kunci yang betul.

http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)

Anda sentiasa boleh melakukan yang lebih baik.

Ayo Sulitkan menyediakan sijil percuma dengan pembaharuan automatik. Untuk menggunakan perkhidmatan ini, anda memerlukan pakej autocert.

Cara paling mudah untuk mengkonfigurasinya ialah menggunakan kaedah autocert.NewListener dalam kombinasi dengan http.Serve. Kaedah ini membolehkan anda mendapatkan dan mengemas kini sijil TLS semasa pelayan HTTP memproses permintaan:

http.Serve(autocert.NewListener("example.com"), nil)

Jika kita buka dalam pelayar example.com, kami akan menerima respons HTTPS "Hello, dunia!"

Jika anda memerlukan konfigurasi yang lebih terperinci, maka anda harus menggunakan pengurus autocert.Manager. Kemudian kami mencipta contoh http.Server kami sendiri (sehingga kini kami menggunakannya secara lalai) dan menambah pengurus pada pelayan TLSConfig:

m := &autocert.Manager{
Cache:      autocert.DirCache("golang-autocert"),
Prompt:     autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example.org", "www.example.org"),
}
server := &http.Server{
    Addr:      ":443",
    TLSConfig: m.TLSConfig(),
}
server.ListenAndServeTLS("", "")

Ini ialah cara mudah untuk melaksanakan sokongan penuh HTTPS dengan pembaharuan sijil automatik.

Menambah laluan tersuai

Penghala lalai yang disertakan dalam perpustakaan standard adalah baik, tetapi ia sangat asas. Kebanyakan aplikasi memerlukan penghalaan yang lebih kompleks, termasuk laluan bersarang dan kad bebas, atau prosedur untuk menetapkan corak dan parameter laluan.

Dalam kes ini, ia patut menggunakan pakej gorila/mux ΠΈ go-chi/chi. Kami akan belajar cara bekerja dengan yang terakhir - contoh ditunjukkan di bawah.

Diberikan ialah fail api/v1/api.go yang mengandungi laluan untuk API kami:

/ HelloResponse is the JSON representation for a customized message
type HelloResponse struct {
Message string `json:"message"`
}
 
// HelloName returns a personalized JSON message
func HelloName(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
response := HelloResponse{
Message: fmt.Sprintf("Hello %s!", name),
}
jsonResponse(w, response, http.StatusOK)
}
 
// NewRouter returns an HTTP handler that implements the routes for the API
func NewRouter() http.Handler {
r := chi.NewRouter()
r.Get("/{name}", HelloName)
return r
}

Kami menetapkan awalan api/vq untuk laluan dalam fail utama.

Kami kemudiannya boleh memasang ini ke penghala utama kami di bawah awalan api/v1/ kembali dalam aplikasi utama kami:

// NewRouter returns a new HTTP handler that implements the main server routes
func NewRouter() http.Handler {
router := chi.NewRouter()
    router.Mount("/api/v1/", v1.NewRouter())
    return router
}
http.Serve(autocert.NewListener("example.com"), NewRouter())

Kemudahan Go untuk bekerja dengan laluan yang kompleks memungkinkan untuk memudahkan penstrukturan dan penyelenggaraan aplikasi yang besar dan kompleks.

Bekerja dengan perisian tengah

Pementasan melibatkan pembungkusan satu pengendali HTTP dengan yang lain, menjadikannya mungkin untuk melaksanakan pengesahan, pemampatan, pengelogan dan beberapa fungsi lain dengan cepat.

Sebagai contoh, mari lihat antara muka http.Handler; kami akan menggunakannya untuk menulis pengendali yang mengesahkan pengguna perkhidmatan.

func RequireAuthentication(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !isAuthenticated(r) {
            http.Redirect(w, r, "/login", http.StatusTemporaryRedirect)
            return
        }
        // Assuming authentication passed, run the original handler
        next.ServeHTTP(w, r)
    })
}

Terdapat penghala pihak ketiga, seperti chi, yang membolehkan anda melanjutkan fungsi perisian tengah.

Bekerja dengan fail statik

Pustaka standard Go termasuk keupayaan untuk bekerja dengan kandungan statik, termasuk imej, fail JavaScript dan CSS. Ia boleh diakses melalui fungsi http.FileServer. Ia mengembalikan pengendali yang menyediakan fail dari direktori tertentu.

func NewRouter() http.Handler {
    router := chi.NewRouter()
    r.Get("/{name}", HelloName)
 
// Настройка Ρ€Π°Π·Π΄Π°Ρ‡ΠΈ статичСских Ρ„Π°ΠΉΠ»ΠΎΠ²
staticPath, _ := filepath.Abs("../../static/")
fs := http.FileServer(http.Dir(staticPath))
    router.Handle("/*", fs)
    
    return r

Perlu diingat bahawa http.Dir memaparkan kandungan direktori jika ia tidak mengandungi fail index.html utama. Dalam kes ini, untuk mengelakkan direktori daripada dikompromi, anda harus menggunakan pakej unindexed.

Penutupan yang betul

Go juga mempunyai ciri yang dipanggil penutupan anggun pelayan HTTP. Ini boleh dilakukan menggunakan kaedah Shutdown(). Pelayan dimulakan dalam goroutine, dan kemudian saluran didengar untuk menerima isyarat gangguan. Sebaik sahaja isyarat diterima, pelayan dimatikan, tetapi tidak serta-merta, tetapi selepas beberapa saat.

handler := server.NewRouter()
srv := &http.Server{
    Handler: handler,
}
 
go func() {
srv.Serve(autocert.NewListener(domains...))
}()
 
// Wait for an interrupt
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
<-c
 
// Attempt a graceful shutdown
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

Sebagai kesimpulan

Go ialah bahasa yang berkuasa dengan perpustakaan standard yang hampir universal. Keupayaan lalainya sangat luas, dan ia boleh dipertingkatkan menggunakan antara muka - ini membolehkan anda membangunkan pelayan HTTP yang benar-benar boleh dipercayai.

Skillbox mengesyorkan:

Sumber: www.habr.com

Tambah komen