实现HTTP服务端

Go语言标准库内置了强大的net/http包,它为我们提供了丰富的HTTP协议处理工具,基于该包实现HTTP服务端是非常简单的。这篇笔记我们介绍如何使用net/http包构建一个简单的HTTP服务端程序。

创建HTTP服务端

使用net/http创建HTTP服务端很简单,我们需要先注册HTTP路由处理函数然后启动HTTP服务端。

package main

import (
    "log/slog"
    "net/http"
)

func main() {
    // 注册路由处理函数
    http.HandleFunc("/demo", func(writer http.ResponseWriter, request *http.Request) {
        _, err := writer.Write([]byte("Hello, world!"))
        if err != nil {
            slog.Error(err.Error())
            return
        }
    })

    // 启动HTTP服务器
    if err := http.ListenAndServe(":8080", nil); err != nil {
        slog.Error(err.Error())
        return
    }
}

net/http包实现的HTTP服务端中有一个重要的概念,多路复用器ServeMux,它在HTTP服务端中起到并发请求路由的作用,简单来说就是根据请求URL找到对应的处理函数。DefaultServeMux是标准库中默认的内置多路复用器,此外还有第三方包gorilla/mux等,我们这里以内置的DefaultServeMux作为例子进行讲解。

代码中,http.HandleFunc()函数用于在默认的处理器DefaultServeMux上注册路由处理函数,我们定义了一个匿名函数作为处理函数,它的2个参数分别用于向响应写数据和读取请求信息。注册完路由处理函数后我们启动HTTP服务器,我们绑定了所有网络接口的8080端口来提供HTTP服务。代码中http.ListenAndServe()用于启动HTTP服务,它的第1个参数是绑定的主机和端口,第2个参数是处理器,我们这里由于使用默认的DefaultServeMux,因此传nil即可。

HTTP路由

Go语言内置的DefaultServeMux路由比较简单,不已/结尾的路由表示精确匹配,我们可以定义路由字符串,当HTTP请求路径匹配路由字符串时,对应的处理函数会被调用。

http.HandleFunc("/demo", demoHandler)

http.HandleFunc()函数中,第1个参数就是路由,第2个参数是处理函数。

/结尾的路由表示前缀匹配,下面例子会匹配所有/demo/为前缀的URL,例如/demo/123等。

http.HandleFunc("/demo/", demoHandler)

类似于Nginx的路由匹配优先级,当请求URL有两条路由规则同时满足时,DefaultServeMux会优先匹配更精确的路径,只有当没有精确匹配的路径时,才会进行前缀匹配。

如果路由字符串只有一个/表示它是一个默认路由(catch-all路由),它能匹配所有URL但优先级最低,任何没有匹配其他已注册路径的请求都会被这个默认路由捕获。

http.HandleFunc("/", demoHandler)

读取请求信息

所有的请求信息都被封装在http.Request结构体中,我们可以从其中读取请求方法、请求路径、请求头、各种参数和请求体等,这里我们简单介绍一下。

读取URL参数

URL参数可以通过如下方式获取,下面代码中我们读取了name参数。

func demoHandler(writer http.ResponseWriter, request *http.Request) {
    query := request.URL.Query()
    name := query.Get("name")
    slog.Info("name: " + name)
}

读取表单参数

读取表单参数复杂一些,我们需要先调用request.ParseForm()方法将请求解析为表单数据,然后调用request.Form.Get()方法读取表单字段。

func demoHandler(writer http.ResponseWriter, request *http.Request) {
    if err := request.ParseForm(); err != nil {
        slog.Error(err.Error())
        return
    }
    name := request.Form.Get("name")
    slog.Info("name: " + name)
}

读取请求体

下面例子我们读取了JSON格式的请求体信息,代码中我们定义了一个结构体,并用encoding/json包解析JSON。

type Message struct {
    Name string `json:"name"`
}

func demoHandler(writer http.ResponseWriter, request *http.Request) {
    // 以JSON格式解析请求体
    message := Message{}
    decoder := json.NewDecoder(request.Body)
    if err := decoder.Decode(&message); err != nil {
        slog.Error(err.Error())
        return
    }
    // 打印解析结果
    slog.Info("Name " + message.Name)
}

返回响应信息

HTTP服务端返回的响应信息主要包括响应码、响应体等。我们可以调用writer.WriteHeader()方法设置响应码,net/http包中定义了很多常量供我们使用。下面代码我们返回了http.StatusOK响应码,它的值实际上是200

func demoHandler(writer http.ResponseWriter, request *http.Request) {
    writer.WriteHeader(http.StatusOK)
}

对于响应体,我们直接调用writer.Write()方法写数据即可。

func demoHandler(writer http.ResponseWriter, request *http.Request) {
    _, err := writer.Write([]byte("Hello, world!"))
    if err != nil {
        slog.Error(err.Error())
        return
    }
}

静态文件服务

http.FileServer()函数可以用来响应静态文件,下面是一个例子。

package main

import (
    "log/slog"
    "net/http"
)

func main() {
    // 注册静态文件处理路由
    fsHandler := http.FileServer(http.Dir("D:\\static"))
    http.Handle("/static/", fsHandler)
    // 启动HTTP服务器
    if err := http.ListenAndServe(":8080", nil); err != nil {
        slog.Error(err.Error())
        return
    }
}

代码中,我们使用http.FileServer()注册了一个文件系统路径作为响应静态文件的目录,假如我们有一个静态资源D:\\static\\1.png,我们使用浏览器请求/static/1.png即可获取该文件。

启用HTTPS

net/http支持创建HTTPS服务器,下面是一个例子。

package main

import (
    "log/slog"
    "net/http"
)

func main() {
    // 注册路由处理函数
    http.HandleFunc("/demo", func(writer http.ResponseWriter, request *http.Request) {
        _, err := writer.Write([]byte("Hello, world!"))
        if err != nil {
            slog.Error(err.Error())
            return
        }
    })
    // 启动HTTPS服务器
    if err := http.ListenAndServeTLS(":8443", "server.crt", "server.key", nil); err != nil {
        slog.Error(err.Error())
        return
    }
}

启动HTTPS服务器需要证书和私钥,前面介绍TLS时我们已经详细解释过X.509证书相关的用法以及如何签发自签名证书用于测试,这里就不赘述了。代码和之前区别不大,只是我们调用的是http.ListenAndServeTLS()来启动HTTPS服务端。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。
Copyright © 2017-2024 Gacfox All Rights Reserved.
Build with NextJS | Sitemap