文章目录
- 目的
- 监听请求并响应
- 请求解析
- 进行响应
- 静态文件服务
- 总结
目的
WebServer是一种非常常用的功能,Golang的高并发特性在处理此类工作中也有较大的优势,同时借助标准库中的 net/http
包可以非常快速的编写WebServer应用。这篇文章将简单记录下相关内容。
https://pkg.go.dev/net/http
Package http provides HTTP client and server implementations.
监听请求并响应
WebServer本质上来说很简单,主要可以为两步:
- 监听某个端口号上收到的HTTP数据;
- 根据收到的HTTP请求中的路径执行对应的操作;
下面是个简单的示例:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func hello(w http.ResponseWriter, r *http.Request) { //回调函数,回调函数将在协程中运行
time.Sleep(4 * time.Second)
fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path) // 向客户端应答数据
}
func naisu(w http.ResponseWriter, r *http.Request) { //回调函数
fmt.Fprintln(w, "Naisu 233!") // 向客户端应答数据
}
func main() {
http.HandleFunc("/", hello) // 设置路由地址与对应回调函数
http.HandleFunc("/naisu", naisu) // 实际应用中通常根据需求设置多个路由地址与对应回调函数
err := http.ListenAndServe(":80", nil) // 启动WebServer,监听80端口,可以在本机浏览器中通过 http://localhost:<端口号><路由地址> 进行访问(80端口可以省略:80)
// err := http.ListenAndServe("0.0.0.0:80", nil) // 在本机所有网卡上进行监听,同网段下的设备可以通过 http://本机IP地址/ 进行访问
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
上面示例中核心涉及的就两个方法:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))
该方法用于设置路由和对应的回调函数;func ListenAndServe(addr string, handler Handler) error
该方法用于监听某个端口号,启动WebServer;
该方法还有个变种ListenAndServeTLS
用于启动HTTPS服务;
请求解析
上面的回调函数原型是 func(http.ResponseWriter, *http.Request)
包含了请求对象和返回的响应对象,实际操作中也主要就是根据请求的内容和业务逻辑进行响应。
下面例子中打印了请求对象中的一些内容:
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.Method)
fmt.Println(r.URL)
fmt.Println(r.Header) // 请求头,各个请求头字段以字典形式保存
fmt.Println(r.Body)
fmt.Println(r.ContentLength)
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":80", nil)
}
上面演示中 /favicon.ico
是浏览器自己发起的请求。
更多使用方法可以查看官方文档中 Request
类型和相关的方法。
进行响应
进行响应主要就是设置响应头字段,设置状态码,写正文内容这些。
package main
import (
"io"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Trailer", "AtEnd1, AtEnd2") // 设置(添加)响应头字段
w.Header().Add("Trailer", "AtEnd3") // 添加响应头字段
w.Header().Set("Content-Type", "text/plain; charset=utf-8") // 添加响应头字段
w.WriteHeader(http.StatusOK) // 写状态码
io.WriteString(w, "Hello Naisu.\n") // 响应body内容
// 注意写头字段、状态码和body的顺序
}
func main() {
http.HandleFunc("/", hello)
http.ListenAndServe(":80", nil)
}
更多使用方法可以查看官方文档中 ResponseWriter
类型和相关的方法。
静态文件服务
对于WebServer来说静态文件服务是非常常用的功能。静态文件服务最常用的就是提供前端网页文件等,所以我们这里弦准备一个名为 index.html
文件,放在当前项目目录下,文件内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1 style="color: #4DD498;">Hello Naisu!</h1>
</body>
</html>
然后使用下面方式启动静态文件服务:
package main
import (
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("./")) // 设置静态文件存放路径,可以使用绝对或相对路径
http.Handle("/", http.StripPrefix("/", fs)) // 设置静态文件路由处理,通过 http://localhost/文件名 进行访问
// http.Handle("/static/", http.StripPrefix("/static/", fs)) // 通过 http://localhost/static/文件名 进行访问
// 设置静态文件存放路径根目录下的 index.html 文件可以不加文件名进行访问
http.ListenAndServe(":80", nil)
}
当然只有一个文件的话也可以使用 func ServeFile(w ResponseWriter, r *Request, name string)
方法来处理,比如上面情况下也可以指定路径返回指定文件:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "index.html")
})
静态文件服务除了上面这样简单使用外,也可以进行定制,比如官方文档中的例子,过滤了设置的目录下所有文件名以 .
开头的文件:
https://pkg.go.dev/net/http#example-FileServer-DotFileHiding
总结
Golang使用标准库中的 net/http
来编写WebServer是比较简单的。更多教程可以参考下面链接:
https://gowebexamples.com/
https://github.com/astaxie/build-web-application-with-golang
实际工作中如果需求比较全面的话全部细节功能都从头实现的话就比较麻烦了。Golang有一个非常出名的用于Web开发的工具库Gorilla,实现了很多常用的功能:
https://www.gorillatoolkit.org/
对于更加复杂的项目也可以直接使用现成的框架。Gin是一个Golang的主流的轻量级Web框架,其官网和项目地址如下:
https://gin-gonic.com/
https://github.com/gin-gonic/gin