如果您曾经使用过 Express.js 这样的 Web 框架,您就会知道它们是多么方便易用。现在,想象一下 Go 的易用性及其性能和稳健性。好吧,这激励我创建了express-go,这是一个受 Express.js 启发的微框架,最重要的是:我在 19 小时内构建了它!旅程很紧张,但每一秒都值得。让我告诉你这一切是如何发生的。官方存储库链接
这一切都始于我的想法:“如果有像 Express.js 这样简单的东西,但又具有 Go 的性能,那就太酷了!”。 Go 已经以极简和高性能而闻名,但在编写 Web 服务器时,我觉得仍然缺少像 Express.js 这样更容易使用的东西。
因此,我没有抱怨,而是决定亲自动手,让事情发生。我决心创建一个微框架,让我能够快速轻松地配置路由、处理 HTTP 请求和响应。
我从基本结构开始:一个可以侦听 HTTP 请求并根据路由执行不同功能的 Go 应用程序。
我需要做的第一件事是设置路由。我希望能够以与 Express.js 类似的方式定义路由,在其中指定 URL 和处理该路由的函数。
这就是路线的魔力:
type App struct { routes map[string]func(req *req.Request, res *req.Response) } func NewApp() *App { return &App{ routes: make(map[string]func(req *req.Request, res *req.Response)), } } func (a *App) Route(path string, handler func(req *req.Request, res *req.Response)) { a.routes[path] = handler } func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { if handler, exists := a.routes[r.URL.Path]; exists { request := req.NewRequest(r) response := req.NewResponse(w) handler(request, response) } else { http.NotFound(w, r) } } func (a *App) Listen(addr string) error { return http.ListenAndServe(addr, a) }
这里的想法很简单:我想要一个路由映射 (map[string]func),其中键是 URL,值是处理请求的函数。
我最喜欢 Express.js 的事情之一是路由处理程序非常易于使用。因此,我采用了这样的想法:每个路由只是一个接收两个参数的函数:请求和响应。在 Go 中,这需要更多的工作,因为标准库需要大量的手动工作,所以我编写了一些抽象以使其更容易。
处理请求
Go 中的 HTTP 请求涉及到很多结构和方法,因此我将所有这些封装在一个名为 Request 的结构中,并提供了一些方便的方法来获取查询参数、标头和请求正文。
type Request struct { Req *http.Request Body string } func NewRequest(req *http.Request) *Request { bodyBytes, _ := io.ReadAll(req.Body) bodyString := string(bodyBytes) return &Request{ Req: req, Body: bodyString, } } func (r *Request) QueryParam(key string) string { params := r.Req.URL.Query() return params.Get(key) } func (r *Request) Header(key string) string { return r.Req.Header.Get(key) } func (r *Request) BodyAsString() string { return r.Body }
现在,我可以执行以下操作,而不是直接处理 http.Request:
app.Route("/greet", func(r *req.Request, w *req.Response) { name := r.QueryParam("name") if name == "" { name = "Guest" } w.Send("Hello, " + name + "!") })
这使得事情变得更加干净和更具可读性!
发出请求后,是时候让发送响应变得更容易了。响应还需要一定程度的简单性,以便我可以快速发送文本或 JSON。
type App struct { routes map[string]func(req *req.Request, res *req.Response) } func NewApp() *App { return &App{ routes: make(map[string]func(req *req.Request, res *req.Response)), } } func (a *App) Route(path string, handler func(req *req.Request, res *req.Response)) { a.routes[path] = handler } func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { if handler, exists := a.routes[r.URL.Path]; exists { request := req.NewRequest(r) response := req.NewResponse(w) handler(request, response) } else { http.NotFound(w, r) } } func (a *App) Listen(addr string) error { return http.ListenAndServe(addr, a) }
在这 19 个小时的工作结束后,我成功创建了express-go:一个快速且易于使用的微框架,其中配置路由和发送响应就像 Express.js 一样简单,但具有所有功能Go 的力量和性能。
这是一个完整的示例,说明它们如何组合在一起:
type Request struct { Req *http.Request Body string } func NewRequest(req *http.Request) *Request { bodyBytes, _ := io.ReadAll(req.Body) bodyString := string(bodyBytes) return &Request{ Req: req, Body: bodyString, } } func (r *Request) QueryParam(key string) string { params := r.Req.URL.Query() return params.Get(key) } func (r *Request) Header(key string) string { return r.Req.Header.Get(key) } func (r *Request) BodyAsString() string { return r.Body }
简单、干净、切题。我很自豪地说,我能够在不到一天的时间内构建它,最酷的是它为小型项目提供了足够的灵活性,而没有大型框架的所有复杂性。
在 19 小时内创建 Express Go 是一次有趣且充满挑战的旅程。我专注于解决我在使用 Go 服务器时遇到的实际问题,并尝试使一切尽可能直观。当然,还有更多工作要做,但还有很多可以玩的!
如果您好奇,请查看代码并随时做出贡献。毕竟,当我们可以分享过程时,构建这样的工具会更酷!
现在,请原谅我,我要去喝杯咖啡......19个小时后,我应得的,对吧?
以上是我如何在几小时内写出 Express-Go的详细内容。更多信息请关注PHP中文网其他相关文章!