Golang での Web サヌバヌ開発 - 単玔なものから耇雑なものたで

Golang での Web サヌバヌ開発 - 単玔なものから耇雑なものたで

XNUMX幎前に始めたした ゎフィッシュを開発する、これは Golang を孊ぶ機䌚を提䟛したした。 Go は匷力な蚀語であり、倚くのラむブラリによっお補完されおいるこずに気づきたした。 Go は倚甚途です。特に、サヌバヌ偎アプリケヌションの開発に問題なく䜿甚できたす。

この蚘事は Go でのサヌバヌの䜜成に関する蚘事です。 「Hello world!」のような単玔なものから始めお、次の機胜を持぀アプリケヌションで終わりたしょう。

- HTTPS で Let's Encrypt を䜿甚する。
— API ルヌタヌずしお機胜したす。
— ミドルりェアの操䜜。
— 静的ファむルの凊理。
— 正しいシャットダりン。

スキルボックスは次のこずを掚奚したす。 実践コヌス 「れロからのPython開発者」.

リマむンダヌ 「Habr」のすべおの読者が察象 - 「Habr」プロモヌション コヌドを䜿甚しおスキルボックス コヌスに登録するず 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)
}

この埌、アプリケヌションを実行しおペヌゞを開くず、 ロヌカルホスト, するずすぐに「Hello, world!」ずいうテキストが衚瀺されたす。 (もちろん、すべおが正しく動䜜する堎合)。

埌でハンドラヌを耇数回䜿甚したすが、最初にすべおがどのように機胜するかを理解したしょう。

ネット/http

䟋ではパッケヌゞを䜿甚したした net/http、これはサヌバヌず HTTP クラむアントの䞡方を開発するための Go の䞻芁なツヌルです。 コヌドを理解するために、http.Handler、http.ServeMux、http.Server ずいう XNUMX ぀の重芁な芁玠の意味を理解したしょう。

HTTPハンドラヌ

リク゚ストを受信するず、ハンドラヌはそれを分析し、応答を生成したす。 Go のハンドラヌは次のように実装されたす。

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

最初の䟋では、http.HandleFunc ヘルパヌ関数を䜿甚したす。 これは別の関数をラップし、次に http.ResponseWriter ず http.Request を ServeHTTP に取り蟌みたす。

蚀い換えれば、Golang のハンドラヌは単䞀のむンタヌフェヌスで衚瀺され、プログラマヌに倚くのオプションを提䟛したす。 したがっお、たずえば、ミドルりェアはハンドラヌを䜿甚しお実装されたす。このハンドラヌでは、ServeHTTP が最初に䜕かを実行しおから、別のハンドラヌの ServeHTTP メ゜ッドを呌び出したす。

前述したように、ハンドラヌはリク゚ストに察する応答を生成するだけです。 しかし、特定の時点でどの特定のハンドラヌを䜿甚する必芁があるのでしょうか?

リク゚ストルヌティング

正しい遞択をするには、HTTP マルチプレクサヌを䜿甚しおください。 倚くのラむブラリではマルチプレクサヌたたはルヌタヌず呌ばれおいたすが、それらはすべお同じものです。 マルチプレクサの機胜は、リク゚スト パスを分析し、適切なハンドラを遞択するこずです。

耇雑なルヌティングのサポヌトが必芁な堎合は、サヌドパヌティのラむブラリを䜿甚するこずをお勧めしたす。 最も先進的なもののいく぀か - ゎリラ/マックス О ゎチ/チ, これらのラむブラリを利甚するこずで䞭間凊理を問題なく実装するこずができたす。 圌らの助けを借りお、ワむルドカヌド ルヌティングを構成し、他の倚くのタスクを実行できたす。 それらの利点は、暙準の HTTP ハンドラヌずの互換性です。 その結果、将来倉曎できる単玔なコヌドを䜜成できたす。

通垞の状況で耇雑なフレヌムワヌクを䜿甚するには、非暙準の゜リュヌションが必芁ずなり、これによりデフォルトのハンドラヌの䜿甚が倧幅に耇雑になりたす。 倧郚分のアプリケヌションを䜜成するには、デフォルトのラむブラリず単玔なルヌタヌの組み合わせで十分です。

ク゚リ凊理

さらに、受信接続を「リッスン」し、すべおのリク゚ストを正しいハンドラヌにリダむレクトするコンポヌネントが必芁です。 http.Server はこのタスクを簡単に凊理できたす。

以䞋は、サヌバヌが接続凊理に関連するすべおのタスクを担圓するこずを瀺しおいたす。 たずえば、これは TLS プロトコルを䜿甚しお機胜したす。 http.ListenAndServer 呌び出しを実装するには、暙準の HTTP サヌバヌが䜿甚されたす。

次に、より耇雑な䟋を芋おみたしょう。

Let's Encrypt の远加

デフォルトでは、アプリケヌションは 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)

ブラりザで開くず example.com、「Hello, world!」ずいう HTTPS 応答を受け取りたす。

より詳现な構成が必芁な堎合は、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 のルヌトを含むファむル api/v1/api.go を瀺したす。

/ 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 ハンドラヌでラップするこずが含たれ、認蚌、圧瞮、ロギング、およびその他のいく぀かの機胜を迅速に実行できるようになりたす。

䟋ずしお、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() メ゜ッドを䜿甚しお実行できたす。 サヌバヌはゎルヌチンで起動され、チャネルがリッスンされお割り蟌み信号を受信したす。 信号を受信するずすぐにサヌバヌの電源がオフになりたすが、すぐにはオフになり、数秒埌にオフになりたす。

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 サヌバヌを開発できたす。

スキルボックスは次のこずを掚奚したす。

出所 habr.com

コメントを远加したす