Go Web 编程入门:中间件

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
简介: 中间件通常位于前端的客户端站点请求和请求的后端资源之间。中间件非常有用,尤其是当我们希望在进行 API 调用之前进行一些验证,例如请求方法、标头和 JWT 等。

前言

中间件通常位于前端的客户端站点请求和请求的后端资源之间。中间件非常有用,尤其是当我们希望在进行 API 调用之前进行一些验证,例如请求方法、标头和 JWT 等。

网络异常,图片无法展示
|


日志中间件


http.Handler 包装器是一个具有一个输入参数和一个输出参数的函数,两者都是 http.Handler 类型。

func Middleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        //code before make the backend api call
  next.ServeHTTP(w, r) //serve and handle the http request
        //code after make the backend api call
  })
}

现在,而不是直接服务和监听我们的 http 处理程序,例如:

http.ListenAndServe(":3000", handler)

我们现在可以将它包装在我们的中间件中并变成:

http.ListenAndServe(":3000", Middleware(handler))


接下来将介绍如何创建一个 Go 语言的日志中间件。


中间件只需将 http.HandlerFunc 作为其参数之一,将其包装并返回一个新的 http.HandlerFunc 供服务器调用。


新建一个 main.go 文件:

package main
import (
    "fmt"
    "log"
    "net/http"
)
func logging(f http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        log.Println(r.URL.Path)
        f(w, r)
    }
}
func foo(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "foo")
}
func bar(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "bar")
}
func main() {
    http.HandleFunc("/foo", logging(foo))
    http.HandleFunc("/bar", logging(bar))
    http.ListenAndServe(":8080", nil)
}


创建 mod 文件:

$ go mod init main.go 
go: creating new go.mod: module main.go
go: to add module requirements and sums:
        go mod tidy


打开另一个终端:

$ curl -s http://localhost:8080/foo
foo
$ curl -s http://localhost:8080/bar
bar
$ curl -s http://localhost:8080/foo\?bar
foo

运行上述代码:

$ go run main.go         
2022/05/25 23:12:00 /foo
2022/05/25 23:12:09 /bar
2022/05/25 23:12:16 /foo


中间件框架

中间件本身只是将 a 作为其参数之一,将其包装并返回一个新的供服务器调用。在这里,我们定义了一种新类型,它最终使将多个中间件链接在一起变得更加容易。这个想法的灵感来自于 Mat Ryers 关于构建 API 的演讲。

这个片段详细解释了如何创建一个新的中间件。在下面的完整示例中,代码结构示例如下:

func createNewMiddleware() Middleware {
    // Create a new Middleware
    middleware := func(next http.HandlerFunc) http.HandlerFunc {
        // Define the http.HandlerFunc which is called by the server eventually
        handler := func(w http.ResponseWriter, r *http.Request) {
            // ... do middleware things
            // Call the next middleware/handler in chain
            next(w, r)
        }
        // Return newly created handler
        return handler
    }
    // Return newly created middleware
    return middleware
}

完整测试

package main
import (
    "fmt"
    "log"
    "net/http"
    "time"
)
type Middleware func(http.HandlerFunc) http.HandlerFunc
// Logging logs all requests with its path and the time it took to process
func Logging() Middleware {
    // Create a new Middleware
    return func(f http.HandlerFunc) http.HandlerFunc {
        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {
            // Do middleware things
            start := time.Now()
            defer func() { log.Println(r.URL.Path, time.Since(start)) }()
            // Call the next middleware/handler in chain
            f(w, r)
        }
    }
}
// Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
func Method(m string) Middleware {
    // Create a new Middleware
    return func(f http.HandlerFunc) http.HandlerFunc {
        // Define the http.HandlerFunc
        return func(w http.ResponseWriter, r *http.Request) {
            // Do middleware things
            if r.Method != m {
                http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
                return
            }
            // Call the next middleware/handler in chain
            f(w, r)
        }
    }
}
// Chain applies middlewares to a http.HandlerFunc
func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    for _, m := range middlewares {
        f = m(f)
    }
    return f
}
func Hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
}
func main() {
    http.HandleFunc("/", Chain(Hello, Method("GET"), Logging()))
    http.ListenAndServe(":8080", nil)
}

运行代码:

$ go run main.go 

打开另外的终端:

$ curl -s http://localhost:8080/
hello world
$ curl -s -XPOST http://localhost:8080/
Bad Request
$ go run main.go         
2022/05/25 23:20:01 / 21.595µs
2022/05/25 23:20:13 / 5.129µs
相关文章
|
28天前
|
中间件 Go API
Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架
本文概述了Go语言中几种流行的Web框架,如Beego、Gin和Echo,分析了它们的特点、性能及适用场景,并讨论了如何根据项目需求、性能要求、团队经验和社区支持等因素选择最合适的框架。
70 1
|
29天前
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
29 1
|
1月前
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
41 7
|
1月前
|
存储 设计模式 安全
Go语言中的并发编程:从入门到精通###
本文深入探讨了Go语言中并发编程的核心概念与实践技巧,旨在帮助读者从理论到实战全面掌握Go的并发机制。不同于传统的技术文章摘要,本部分将通过一系列生动的案例和代码示例,直观展示Go语言如何优雅地处理并发任务,提升程序性能与响应速度。无论你是Go语言初学者还是有一定经验的开发者,都能在本文中找到实用的知识与灵感。 ###
|
1月前
|
缓存 前端开发 中间件
go语言中Web框架
【10月更文挑战第22天】
44 4
|
1月前
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
|
1月前
|
Serverless Go
Go语言中的并发编程:从入门到精通
本文将深入探讨Go语言中并发编程的核心概念和实践,包括goroutine、channel以及sync包等。通过实例演示如何利用这些工具实现高效的并发处理,同时避免常见的陷阱和错误。
|
2月前
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
53 7
|
2月前
|
安全 Go 开发者
破译Go语言中的并发模式:从入门到精通
在这篇技术性文章中,我们将跳过常规的摘要模式,直接带你进入Go语言的并发世界。你将不会看到枯燥的介绍,而是一段代码的旅程,从Go的并发基础构建块(goroutine和channel)开始,到高级模式的实践应用,我们共同探索如何高效地使用Go来处理并发任务。准备好,让Go带你飞。
|
2月前
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。
下一篇
DataWorks