Pengembangan Server Web Golang - Dari Mudah ke Kompleks



Lima tahun yang lalu saya mulai mengembangkan Gophish , yang memungkinkan untuk belajar Golang. Saya menyadari bahwa Go adalah bahasa yang kuat, kemampuan yang dilengkapi oleh banyak perpustakaan. Go bersifat universal: khususnya, dengan bantuannya adalah mungkin untuk mengembangkan aplikasi server tanpa masalah.

Artikel ini adalah tentang menulis server di Go. Mari kita mulai dengan hal-hal sederhana, seperti "Halo dunia!", Dan akhiri dengan aplikasi dengan fitur berikut:

- Menggunakan Let's Encrypt untuk HTTPS.
- Bekerja sebagai router API.
- Bekerja dengan middleware.
- Pemrosesan file statis.
- Pematian yang benar.

Skillbox merekomendasikan: Pengembang Python dari awal tentu saja.

Kami mengingatkan Anda: untuk semua pembaca "Habr" - diskon 10.000 rubel saat mendaftar untuk kursus Skillbox apa pun menggunakan kode promo "Habr".

Halo dunia!


Membuat server web on Go sangat cepat. Berikut adalah contoh menggunakan pawang yang mengembalikan "Halo, dunia!" 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) } 

Setelah itu, jika Anda memulai aplikasi dan membuka halaman localhost , Anda akan segera melihat teks "Halo, dunia!" (tentu saja, jika semuanya berfungsi dengan benar).

Selanjutnya, kita akan berulang kali menggunakan handler, tetapi pertama-tama, mari kita pahami bagaimana semuanya berjalan.

net / http


Contohnya menggunakan paket net/http , yang merupakan alat utama di Go untuk mengembangkan kedua server dan klien HTTP. Untuk memahami kode ini, mari kita lihat makna dari tiga elemen penting: http.Handler, http.ServeMux dan http.Server.

Penangan HTTP


Ketika kami menerima permintaan, pawang menganalisanya dan membentuk respons. Go handler diimplementasikan sebagai berikut:

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

Contoh pertama menggunakan fungsi pembantu http.HandleFunc. Itu membungkus fungsi lain, yang pada gilirannya menerima http.ResponseWriter dan http.Request di ServeHTTP.

Dengan kata lain, penangan Golang diwakili oleh satu antarmuka, yang menyediakan banyak peluang bagi programmer. Jadi, misalnya, middleware diimplementasikan menggunakan handler, di mana ServeHTTP pertama kali melakukan sesuatu, dan kemudian memanggil metode ServeHTTP dari handler lain.

Seperti disebutkan di atas, penangan hanya menghasilkan tanggapan terhadap permintaan. Tetapi pawang mana yang harus digunakan pada titik waktu tertentu?

Meminta Routing


Untuk membuat pilihan yang tepat, gunakan multiplexer HTTP. Ini disebut muxer atau router di sejumlah perpustakaan, tetapi semuanya sama. Fungsi multiplexer adalah untuk menganalisis jalur permintaan dan memilih penangan yang sesuai.

Jika Anda memerlukan dukungan untuk perutean yang rumit, maka lebih baik menggunakan perpustakaan pihak ketiga. Salah satu yang paling canggih adalah gorilla / mux dan go-chi / chi , perpustakaan ini memungkinkan untuk mengimplementasikan pemrosesan perantara tanpa masalah. Dengan bantuan mereka, Anda dapat mengonfigurasi perutean wildcard dan melakukan sejumlah tugas lainnya. Nilai tambah mereka adalah kompatibilitas dengan penangan HTTP standar. Hasilnya, Anda dapat menulis kode sederhana dengan kemungkinan modifikasinya di masa mendatang.

Bekerja dengan kerangka kerja kompleks dalam situasi normal tidak memerlukan solusi standar, dan ini sangat mempersulit penggunaan penangan default. Untuk membuat sebagian besar aplikasi, kombinasi dari perpustakaan default dan router sederhana sudah cukup.

Meminta pemrosesan


Selain itu, kita memerlukan komponen yang akan "mendengarkan" koneksi yang masuk dan mengalihkan semua permintaan ke penangan yang benar. Tugas ini dapat dengan mudah dilakukan oleh http.Server.

Berikut ini menunjukkan bahwa server bertanggung jawab untuk semua tugas yang terkait dengan pemrosesan koneksi. Ini, misalnya, bekerja pada protokol TLS. Untuk menerapkan panggilan http.ListenAndServer, server HTTP standar digunakan.

Sekarang mari kita lihat contoh yang lebih kompleks.

Menambahkan Let's Encrypt


Secara default, aplikasi kami menggunakan protokol HTTP, tetapi disarankan untuk menggunakan protokol HTTPS. Dalam Go, ini bisa dilakukan tanpa masalah. Jika Anda menerima sertifikat dan kunci pribadi, maka cukup daftarkan ListenAndServeTLS dengan sertifikat dan file kunci yang benar.

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

Anda selalu bisa melakukan yang lebih baik.

Let's Encrypt memberikan sertifikat gratis dengan kemampuan untuk memperbarui secara otomatis. Untuk menggunakan layanan ini, Anda memerlukan paket autocert .

Cara termudah untuk mengkonfigurasinya adalah dengan menggunakan metode autocert.NewListener dalam kombinasi dengan http.Serve. Metode ini memungkinkan Anda untuk menerima dan memperbarui sertifikat TLS, sementara server HTTP memproses permintaan:

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

Jika kami membuka example.com di browser, kami mendapat respons HTTPS "Halo, dunia!".

Jika Anda memerlukan konfigurasi yang lebih menyeluruh, maka Anda harus menggunakan manajer autocert.Manager. Kemudian kita membuat instance http.Server kita sendiri (sampai sekarang kita menggunakannya secara default) dan menambahkan manajer ke server 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 adalah cara mudah untuk menerapkan dukungan HTTPS penuh dengan pembaruan sertifikat otomatis.

Menambahkan rute khusus


Router default yang termasuk dalam pustaka standar baik, tetapi sangat sederhana. Sebagian besar aplikasi memerlukan perutean yang lebih kompleks, termasuk rute bersarang dan wildcard, atau prosedur untuk mengatur pola dan parameter jalur.

Dalam hal ini, Anda harus menggunakan paket gorilla / mux dan go-chi / chi . Kami akan belajar untuk bekerja dengan yang terakhir - contoh ditunjukkan di bawah ini.

Diberikan adalah file api / v1 / api.go yang berisi rute 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 rute di file utama.

Kita kemudian dapat memasang ini ke router utama kita di bawah awalan api / v1 / di aplikasi utama kita:

 // 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()) 

Kesederhanaan bekerja dengan rute kompleks di Go memungkinkan penyederhanaan penyederhanaan dengan melayani aplikasi rumit yang besar.

Bekerja dengan middleware


Dalam hal pemrosesan antara, digunakan membungkus satu penangan HTTP dengan yang lain, yang memungkinkan untuk mengotentikasi, mengompres, jurnal, dan beberapa fungsi lainnya dengan cepat.

Sebagai contoh, perhatikan antarmuka http.Handler, dengan bantuannya kami menulis handler dengan otentikasi pengguna layanan.

 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) }) } 

Ada router pihak ketiga, misalnya, chi, yang memungkinkan Anda untuk memperluas fungsi pemrosesan perantara.

Bekerja dengan file statis


Pustaka Go standar mencakup fitur untuk bekerja dengan konten statis, termasuk gambar, serta file JavaScript dan CSS. Mereka dapat diakses melalui fungsi http.FileServer. Ini mengembalikan penangan yang mendistribusikan file 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 bahwa http.Dir menampilkan konten direktori jika tidak memiliki file index.html utama. Dalam hal ini, untuk mencegah direktori tidak disusupi, ada baiknya menggunakan paket yang tidak unindexed .

Pematian yang benar


Go juga memiliki fitur seperti mematikan server HTTP dengan benar. Ini dapat dilakukan dengan menggunakan metode Shutdown (). Server mulai di goroutine, dan kemudian saluran didengarkan untuk menerima sinyal interupsi. Segera setelah sinyal diterima, server dimatikan, tetapi tidak segera, tetapi setelah beberapa detik.

 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) 

Kesimpulannya


Go adalah bahasa yang kuat dengan perpustakaan standar yang hampir universal. Kemampuan standarnya sangat luas, dan Anda dapat memperkuatnya dengan bantuan antarmuka - ini memungkinkan Anda untuk mengembangkan server HTTP yang benar-benar andal.
Skillbox merekomendasikan:

Source: https://habr.com/ru/post/id446454/


All Articles