Golang Web服务器开发-从简单到复杂



五年前,我开始开发Gophish ,这使得学习Golang成为可能。 我意识到Go是一种强大的语言,许多库都对Go的功能进行了补充。 Go是通用的:特别是在它的帮助下,可以毫无问题地开发服务器应用程序。

本文是关于在Go中编写服务器。 让我们从简单的事物开始,例如“ Hello world!”,然后以具有以下功能的应用程序结束:

-使用“让我们加密HTTPS”。
-用作API路由器。
-使用中间件。
-处理静态文件。
-正确关机。

Skillbox建议: Python开发人员从头开始 实践课程。

我们提醒您: 对于所有“ Habr”读者来说,使用“ Habr”促销代码注册任何Skillbox课程时均可享受10,000卢布的折扣。

世界,您好!


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

之后,如果启动应用程序并打开localhost页面,您将立即看到文本“ Hello,world!”。 (当然,如果一切正常)。

接下来,我们将重复使用该处理程序,但首先,让我们了解一切的工作原理。

网络/ HTTP


该示例使用了net/http包,这是Go中用于开发服务器和HTTP客户端的主要工具。 为了理解代码,让我们看一下三个重要元素的含义:http.Handler,http.ServeMux和http.Server。

HTTP处理程序


当我们收到请求时,处理程序将对其进行分析并形成响应。 Go处理程序的实现如下:

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

第一个示例使用http.HandleFunc帮助函数。 它包装了另一个函数,该函数又在ServeHTTP中接受http.ResponseWriter和http.Request。

换句话说,Golang处理程序由单个接口表示,这为程序员提供了很多机会。 因此,例如,中间件是使用处理程序实现的,其中ServeHTTP首先执行某项操作,然后调用另一个处理程序的ServeHTTP方法。

如上所述,处理程序仅生成对请求的响应。 但是,应该在特定时间使用哪个特定处理程序?

请求路由


为了做出正确的选择,请使用HTTP多路复用器。 在许多库中,它被称为多路复用器或路由器,但它们都是一样的。 多路复用器功能是分析请求路径并选择适当的处理程序。

如果您需要对复杂路由的支持,那么最好使用第三方库。 最先进的软件之一是大猩猩/混合go-chi / chi ,这些库可以实现中间处理而不会出现任何问题。 在他们的帮助下,您可以配置通配符路由并执行许多其他任务。 它们的优点是与标准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) 

如果在浏览器中打开example.com则会收到HTTPS响应“ Hello,world!”。

如果需要更全面的配置,则应使用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支持的简便方法。

添加自定义路线


标准库中包含的默认路由器是好的,但是非常简单。 大多数应用程序需要更复杂的路由,包括嵌套和通配符路由,或者设置模式和路径参数的过程。

在这种情况下,您应该使用大猩猩/ muxgo-chi / chi软件包。 我们将学习如何使用后者-下面显示了一个示例。

给出的是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启动,然后侦听该通道以接收中断信号。 收到信号后,服务器将立即关闭,但不会立即关闭,而是会在几秒钟后关闭。

 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服务器。
Skillbox建议:

Source: https://habr.com/ru/post/zh-CN446454/


All Articles