五年前我开始
本文是关于用 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》 .
来源: habr.com