五年前我開始
本文是關於用 Go 編寫伺服器的。 讓我們從「Hello world!」之類的簡單內容開始,以具有以下功能的應用程式結束:
- 使用 Let's Encrypt 進行 HTTPS。
— 以 API 路由器工作。
— 使用中介軟體。
— 靜態文件的處理。
— 正確關閉。
技能箱推薦: 實踐課程
《從零開始的Python開發》 .提醒: 對於“Habr”的所有讀者 - 使用“Habr”促銷代碼註冊任何 Skillbox 課程可享受 10 盧布的折扣。
您好,世界!
您可以非常快速地在 Go 中建立一個 Web 伺服器。 以下是一個使用處理程序傳回上面承諾的「Hello, world!」的範例。
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)
}
之後,如果您運行應用程式並打開頁面
稍後我們將多次使用該處理程序,但首先讓我們了解一切是如何運作的。
網絡/http
此範例使用了包 net/http
HTTP 處理程式
當我們收到請求時,處理程序會分析它並產生回應。 Go 中的處理程序實作如下:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
第一個範例使用 http.HandleFunc 輔助函數。 它包裝了另一個函數,該函數又將 http.ResponseWriter 和 http.Request 放入 ServeHTTP 中。
換句話說,Golang 中的處理程序呈現在單一介面中,這為程式設計師提供了許多選擇。 例如,中間件是使用處理程序實現的,其中 ServeHTTP 首先執行某些操作,然後呼叫另一個處理程序的 ServeHTTP 方法。
如上所述,處理程序只是產生對請求的回應。 但是在特定的時間點應該使用哪個特定的處理程序?
請求路由
若要做出正確的選擇,請使用 HTTP 多工器。 在許多庫中,它被稱為複用器或路由器,但它們都是相同的東西。 多工器的作用是分析請求路徑並選擇適當的處理程序。
如果您需要支援複雜的路由,那麼最好使用第三方程式庫。 一些最先進的——
在正常情況下使用複雜的框架將需要非標準解決方案,這使得預設處理程序的使用變得非常複雜。 要創建絕大多數應用程序,預設庫和簡單路由器的組合就足夠了。
查詢處理
此外,我們需要一個元件來「偵聽」傳入連線並將所有請求重新導向到正確的處理程序。 http.Server 可以輕鬆處理這個任務。
下面顯示伺服器負責與連線處理相關的所有任務。 例如,這可以使用 TLS 協定進行工作。 為了實作 http.ListenAndServer 調用,需要使用標準 HTTP 伺服器。
現在讓我們來看看更複雜的範例。
新增讓我們加密
預設情況下,我們的應用程式運行在HTTP協定上,但建議使用HTTPS協定。 這在 Go 中可以毫無問題地完成。 如果您已收到憑證和私鑰,那麼使用正確的憑證和金鑰檔案註冊 ListenAndServeTLS 就足夠了。
http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil)
你總是可以做得更好。
autocert
.
最簡單的設定方法是將 autocert.NewListener 方法與 http.Serve 結合使用。 此方法可讓您在 HTTP 伺服器處理請求時取得和更新 TLS 憑證:
http.Serve(autocert.NewListener("example.com"), nil)
如果我們在瀏覽器中打開
如果您需要更詳細的配置,那麼您應該使用 autocert.Manager 管理員。 然後我們建立自己的 http.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("", "")
這是透過自動憑證更新實現完整 HTTPS 支援的簡單方法。
新增自訂路線
標準庫中包含的預設路由器很好,但它非常基礎。 大多數應用程式需要更複雜的路由,包括嵌套和通配符路由,或設定路徑模式和參數的過程。
在這種情況下,值得使用套件
給出的檔案 api/v1/api.go 包含我們的 API 的路由:
/ 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
}
我們在主檔案中設定路由的 api/vq 前綴。
然後我們可以將其安裝到我們主應用程式中 api/v1/ 前綴下的主路由器:
// 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())
Go 可以輕鬆處理複雜的路由,從而可以簡化大型複雜應用程式的建置和維護。
使用中介軟體
分段涉及將一個 HTTP 處理程序與另一個處理程序包裝在一起,從而可以快速執行身份驗證、壓縮、日誌記錄和其他一些功能。
作為範例,讓我們來看看 http.Handler 介面;我們將使用它來編寫一個對服務使用者進行身份驗證的處理程序。
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)
})
}
有第三方路由器(例如 chi)可讓您擴展中間件功能。
處理靜態文件
Go 標準函式庫包含處理靜態內容的功能,包括圖片、JavaScript 和 CSS 檔案。 可以透過 http.FileServer 函數存取它們。 它會傳回一個處理程序,為特定目錄中的檔案提供服務。
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
絕對值得記住的是,如果 http.Dir 不包含主 index.html 文件,則它會顯示目錄的內容。 在這種情況下,為了防止目錄被破壞,您應該使用該包 unindexed
正確關機
Go 還有一個稱為 HTTP 伺服器正常關閉的功能。 這可以使用 Shutdown() 方法來完成。 伺服器在goroutine中啟動,然後監聽channel接收中斷訊號。 一旦收到訊號,伺服器就會關閉,但不是立即關閉,而是在幾秒鐘後關閉。
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)
作為結論
Go 是一種功能強大的語言,擁有幾乎通用的標準函式庫。 它的預設功能非常廣泛,並且可以使用介面進行增強 - 這使您可以開發真正可靠的 HTTP 伺服器。
技能箱推薦:
- 兩年實踐課程
“我是專業網頁開發人員” .- 教育在線課程
《職業Java開發人員》 .- 實踐年課程
《PHP 開發者從 0 到 PRO》 .
來源: www.habr.com