默认的 http web 服务器

在线运行open in new window启动 AI 助手open in new window

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })

    http.ListenAndServe(":8080", nil)
}

首先,在 main 函数中,我们使用 http.HandleFunc 函数来注册一个 HTTP 处理程序。第一个参数是 URL 路径,我们将其设置为 /,表示这个处理程序将处理所有来自该 URL 路径的 HTTP 请求。第二个参数是一个回调函数,它将在接收到 HTTP 请求时被调用,并负责处理请求。在这个例子中,回调函数简单地将字符串 "Hello, World!" 通过 http.ResponseWriter 写入 HTTP 响应中。

然后,我们使用 http.ListenAndServe 函数来启动 HTTP 服务器,并指定它要监听的端口号。在这个例子中,我们将服务器设置为监听本地计算机的 8080 端口。这个函数将一直监听 HTTP 请求,直到收到 kill 命令,或者发生错误。

当启动这个程序时,可以在浏览器中访问 http://localhost:8080 来查看输出的 "Hello, World!" 消息。

可以接受中断信号的 http 服务器

在线运行open in new window启动 AI 助手open in new window

func main() {
    // 创建 HTTP 服务器
    server := &http.Server{
        Addr: ":8080",
        Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprint(w, "Hello, World!")
        }),
    }

    // 启动 HTTP 服务器
    go func() {
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            fmt.Println(err)
        }
    }()

    // 等待中断信号
    sigc := make(chan os.Signal, 1)
    signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)
    <-sigc

    // 收到中断信号,优雅地关闭 HTTP 服务器
    fmt.Println("Shutting down server...")
    if err := server.Shutdown(nil); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

程序使用了 Go 语言标准库中的 net/http 包和 os/signal 包,实现了一个 HTTP 服务器,可以在接收到中断信号时优雅地关闭服务器。

首先,在 main 函数中,我们定义了一个 http.Server 对象,它绑定在本地计算机的 8080 端口上,当接收到一个 HTTP 请求时,它会调用一个简单的处理程序并向客户端发送字符串 Hello, World! 。然后,我们在一个 goroutine 中启动了 HTTP 服务器,因为 ListenAndServe 方法是一个阻塞方法。

接下来,我们创建了一个 chan os.Signal 通道,用于等待中断信号和 syscall.SIGTERM 信号,当收到这些信号后,我们会从中断信号通道接收到一个值。然后,我们使用 http.Server.Shutdown 方法来关闭服务器并释放资源,通过传递 nil 作为参数,HTTP 服务器将优雅地关闭,即等待当前正在处理的请求处理完毕之后再关闭。

这个程序的 web 服务在新启动的 goroutine 中,也可以反过来写运行在主 goroutine 中。

func main() {
    var srv http.Server

    // 用来阻塞协助 goroutine 通讯
    exit := make(chan struct{})

    go func() {
        sigc := make(chan os.Signal, 1)
        signal.Notify(sigc, os.Interrupt, syscall.SIGTERM)

        // 等待中断信号
        <-sigc

        // 关闭服务器
        if err := srv.Shutdown(context.Background()); err != nil {
            // 关闭本身出错了
            log.Printf("HTTP server Shutdown: %v", err)
        }

        // 通知主进程退出
        close(exit)
    }()

    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatalf("HTTP server ListenAndServe: %v", err)
    }

    <-exit
}

手动创建 ServerMux 路由器

在线运行open in new window启动 AI 助手open in new window

func main() {
    mux := http.NewServeMux()

    mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(w, "hello world")
    })
    mux.HandleFunc("/about", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(w, "about us")
    })

    http.ListenAndServe(":8080", mux)
}

首先使用 http.NewServeMux() 函数创建了一个HTTP路由器,它可以帮助我们处理来自客户端的HTTP请求。接着,我们创建了两个处理器函数,一个用来处理根路径请求,另一个用来处理/about路径请求。

然后,我们使用 mux.HandleFunc() 函数将这两个处理器函数注册到路由器中,这样当收到对应路径的请求时,路由器就会调用对应的处理器函数进行处理。最后,我们使用 http.ListenAndServe() 函数启动了一个HTTP服务,并将路由器传递给它。

如果 http.ListenAndServe() 函数的第二个参数传递 nil,将默认使用 DefaultServerMux(对应 http.HandleFunc 函数挂载路由处理器),该程序传递了手动创建的 mux 对象。

http.Handler 类型和 ServeHTTP 方法

在线运行open in new window启动 AI 助手open in new window

type myHandler struct{}

func (h *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

func main() {
    handler := &myHandler{}
    http.ListenAndServe(":8080", handler)
}

在这个例子中,我们定义了一个名为 myHandler 的自定义类型,并实现了 http.Handler 接口的 ServeHTTP 方法。在这个方法中,我们使用 fmt.Fprintf() 函数将客户端请求的 URL 路径发送给客户端,并在路径前加上 "Hello,"。这个方法可以将 "Hello, /xxx!" 的字符串写回客户端。

然后,我们创建了一个名为 handler 的 myHandler 类型的指针,并将其传递给 http.ListenAndServe() 函数。当 HTTP 服务器接收到请求时,它会调用 handler 对象的 ServeHTTP 方法来处理请求。这个方法可以将 "Hello, /xxx!" 的字符串写回客户端。

在这个例子中,我们使用了一个自定义类型实现了 http.Handler 接口。这个方法可以让我们更加灵活地定义处理器对象,并根据自己的需求定制处理器的功能。

接口实现函数 http.HandleFunc 的作用

在线运行open in new window启动 AI 助手open in new window

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

定义了一个名为 handler 的函数,它接受一个 http.ResponseWriter 对象和一个 *http.Request 对象,这两个对象是 http.Handler 接口定义的方法 ServeHTTP 的参数。在 handler 函数中,我们使用 fmt.Fprintf() 函数将 "Hello, World!" 字符串写回客户端。

然后,我们使用 http.HandleFunc() 函数将处理器函数添加到默认的路由器 DefaultServeMux 中,这样当收到根路径请求时,路由器就会调用 handler 函数来处理请求。最后,我们使用 http.ListenAndServe() 函数启动了一个 HTTP 服务,并将路由器传递给它。

在这个例子中,我们使用了 http.HandleFunc 函数将处理器函数注册到 DefaultServeMux 路由器中。这个函数实际上是对 DefaultServeMux 实现 http.Handler 接口的一种简化实现。因为 http.HandleFunc 可以自动将函数转换为一个实现 http.Handler 接口的类型,并将其注册到 DefaultServeMux 路由器中,从而简化了代码的编写。

获取请求参数 path-query 信息

在线运行open in new window启动 AI 助手open in new window

func handler(w http.ResponseWriter, r *http.Request) {
    // 获取查询参数的值
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "World"
    }
    fmt.Fprintf(w, "Hello, %s!", name)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

在这个例子中,我们定义了一个名为 handler 的处理器函数,它可以获取名为 name 的查询参数的值。我们使用 r.URL.Query().Get("name") 来获取查询参数,并将其保存在 name 变量中。如果查询参数中没有名为 name 的参数,则 name 变量的值将设置为 "World"。

然后,我们使用 http.HandleFunc() 函数将处理器函数添加到默认的路由器 DefaultServeMux 中,这样当收到根路径请求时,路由器就会调用 handler 函数来处理请求,并在请求路径中包含一个名为 name 的查询参数时,将查询参数的值写回客户端。

获取请求参数 form-data 信息

在线运行open in new window启动 AI 助手open in new window

func handler(w http.ResponseWriter, r *http.Request) {
    name := r.FormValue("name")
    age := r.FormValue("age")
    fmt.Fprintf(w, "Hello, %s! You are %s years old.", name, age)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

在这个例子中,我们定义了一个名为 handler 的处理器函数,它可以获取名为 name 和 age 的表单参数的值。我们使用 r.FormValue("name") 和 r.FormValue("age") 来获取这些参数的值,并将它们分别保存在 name 和 age 变量中。然后,我们使用 fmt.Fprintf() 函数将这些值写回客户端。

然后,我们使用 http.HandleFunc() 函数将处理器函数添加到默认的路由器 DefaultServeMux 中,这样当收到根路径请求时,路由器就会调用 handler 函数来处理请求,并在请求中包含一个名为 name 和 age 的参数时,将参数的值写回客户端。

获取请求参数 json-body 信息

在线运行open in new window启动 AI 助手open in new window

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func handler(w http.ResponseWriter, r *http.Request) {
    // 解析 JSON 请求体
    var p Person
    if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    fmt.Fprintf(w, "Hello, %s! You are %d years old.", p.Name, p.Age)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

在这个例子中,我们定义了一个名为 Person 的结构体,并使用了 json 标签来指定结构体字段与 JSON 属性之间的映射关系。在 handler 函数中,我们使用 json.NewDecoder(r.Body).Decode(&p) 将请求体解析为 Person 结构体,并将结果保存在 p 变量中。然后,我们使用 fmt.Fprintf() 函数将这些值写回客户端。

然后,我们使用 http.HandleFunc() 函数将处理器函数添加到默认的路由器 DefaultServeMux 中,这样当收到根路径请求时,路由器就会调用 handler 函数来处理请求,并在请求体中包含 JSON 格式数据时,将数据解析为结构体,并将其值写回客户端。

输出 json 文本

在线运行open in new window启动 AI 助手open in new window

type Person struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func handler(w http.ResponseWriter, r *http.Request) {
    p := Person{"Bob", 20}

    // 将结构体序列化成 JSON 格式的字符串
    data, err := json.Marshal(p)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    w.Write(data)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

在这个例子中,我们定义了一个名为 Person 的结构体,并使用了 json 标签来指定结构体字段与 JSON 属性之间的映射关系。在 handler 函数中,我们创建了一个 Person 类型的对象 p,然后使用 json.Marshal() 函数将 p 序列化为 JSON 格式的字符串,并将结果保存在 data 变量中。

然后,我们设置响应头中的 Content-Type 为 application/json,这样客户端就可以正确地解析 JSON 响应。最后,我们将序列化后的 JSON 字符串写回客户端。

在这个例子中,我们使用了 json.Marshal() 函数将一个结构体序列化成 JSON 格式的字符串,并将其写回客户端。这个函数的使用方法非常简单,并且可以很容易地对结构体进行定制化的序列化。

中间件的作用和自定义

在线运行open in new window启动 AI 助手open in new window

// 自定义中间件
func logger(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        // 调用下一个处理器函数
        next(w, r)

        fmt.Printf("Request handled in %s\n", time.Since(start))
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, World!")
}

func main() {
    // 注册默认的路由规则
    http.HandleFunc("/", logger(handler))

    // 启动服务
    http.ListenAndServe(":8080", nil)
}

在这个例子中,我们定义了一个名为 logger 的中间件函数,并返回一个 http.HandlerFunc 类型的函数。这个函数接受一个 http.HandlerFunc 类型的参数 next,并返回一个新的 http.HandlerFunc 类型的函数。

在这个新的函数中,我们记录请求处理的开始时间,然后调用传入的处理器函数处理请求。当处理器函数完成时,我们再次记录时间,并打印出请求处理所花费的时间。

然后我们将中间件函数 logger 传递给了 http.HandleFunc() 函数。这样,每当有请求到达时,它都会先被 logger 中间件处理,然后再被真正的处理器函数 handler 处理。

这个例子展示了如何在 Go 语言中创建和使用中间件。中间件可以帮助我们在请求处理前后执行额外的逻辑,如记录日志、验证权限等。它可以让我们更加灵活地进行 HTTP 请求的处理。

第三方 web 框架 - echo

在线运行open in new window启动 AI 助手open in new window

func main() {
    // 创建一个 Echo 实例
    e := echo.New()

    // 定义一个处理器函数
    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello, World!")
    })

    // 启动 HTTP 服务
    e.Logger.Fatal(e.Start(":8080"))
}

在这个例子中,我们使用 github.com/labstack/echo/v4 包创建了一个 Echo 实例 e,并使用 e.GET() 函数定义了一个处理器函数,用于处理根路径的 GET 请求。这个函数接受一个 echo.Context 类型的参数 c,并使用 c.String() 函数将 "Hello, World!" 字符串作为响应写回客户端。

然后,我们使用 e.Start() 函数启动了一个 HTTP 服务,并将路由器传递给它。这个服务会一直运行,直到收到一个中断信号为止。

Echo 框架非常易用和灵活,支持中间件、路由分组、静态文件服务等多种功能。可以根据需求灵活地进行配置和使用。

第三方 web 框架 - chi

在线运行open in new window启动 AI 助手open in new window

func main() {
    // 创建一个 Chi 路由器
    r := chi.NewRouter()

    // 添加一个中间件
    r.Use(middleware.Logger)

    // 定义一个处理器函数
    r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })

    // 启动 HTTP 服务
    http.ListenAndServe(":8080", r)
}

在这个例子中,我们使用 github.com/go-chi/chi 包创建了一个 Chi 路由器 r,并使用 r.Use() 函数添加了一个日志中间件。这个中间件会记录每次请求的 URL、方法、状态码等信息,并打印到标准输出中。

然后,我们使用 r.Get() 函数定义了一个处理器函数,用于处理根路径的 GET 请求。这个函数接受一个 http.ResponseWriter 类型的参数 w 和一个 *http.Request 类型的参数 r,并使用 w.Write() 函数将 "Hello, World!" 字符串作为响应写回客户端。

最后,我们使用 http.ListenAndServe() 函数启动了一个 HTTP 服务,并将路由器传递给它。

Chi 框架提供了路由、中间件、子路由、模板渲染等多种功能,并且使用起来也非常简单和方便。可以根据需求灵活地进行配置和使用。

第三方 web 框架 - gin

在线运行open in new window启动 AI 助手open in new window

func main() {
    // 创建一个 Gin 实例
    r := gin.Default()

    // 定义一个处理器函数
    r.GET("/", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello, World!")
    })

    // 启动 HTTP 服务
    r.Run(":8080")
}

在这个例子中,我们使用 github.com/gin-gonic/gin 包创建了一个 Gin 实例 r,并使用 r.GET() 函数定义了一个处理器函数,用于处理根路径的 GET 请求。这个函数接受一个 *gin.Context 类型的参数 c,并使用 c.String() 函数将 "Hello, World!" 字符串作为响应写回客户端。

然后,我们使用 r.Run() 函数启动了一个 HTTP 服务,并将路由器传递给它。这个服务会一直运行,直到收到一个中断信号为止。

Gin 框架提供了路由、中间件、模板渲染、文件上传下载等多种功能,并且使用起来非常简单和方便。可以根据需求灵活地进行配置和使用。

第三方 web 框架 - fasthttp

在线运行open in new window启动 AI 助手open in new window

func main() {
    // 创建一个 fasthttp.Handler 实例
    handler := func(ctx *fasthttp.RequestCtx) {
        ctx.WriteString("Hello, World!")
    }

    // 启动 HTTP 服务
    fasthttp.ListenAndServe(":8080", handler)
}

在这个例子中,我们使用 github.com/valyala/fasthttp 包创建了一个 fasthttp.Handler 类型的处理器函数 handler,用于处理根路径的 HTTP 请求。这个函数接受一个 *fasthttp.RequestCtx 类型的参数 ctx,并使用 ctx.WriteString() 函数将 "Hello, World!" 字符串作为响应写回客户端。

然后,我们使用 fasthttp.ListenAndServe() 函数启动了一个 HTTP 服务,并将 handler 函数传递给它。

Fasthttp 框架是一个高性能的 HTTP 库,使用起来简单且速度非常快,可以支持路由、中间件等多种功能。由于 Fasthttp 是专为高性能而设计的,因此可以比标准的 net/http 包更快地处理请求。

Last Updated:
Contributors: Bob Wang