1 引子
$ curl localhost:6666
当我们执行下面curl命令时,curl程序会发送一条HTTP Request:
GET / HTTP/1.1
Host: localhost:6666
User-Agent: curl/7.68.0
Accept: */*
我们接下来的实验就是用Go语言写一个简单的HTTP服务器,来处理这条HTTP Request.
2 注册回调函数并启动服务器
http.HandleFunc("/", helloWorld) <- 关联URL与回调函数
log.Fatal(http.ListenAndServe(":6666", nil)) <- 监听端口并启动服务器
完整例子:
package main
import (
"log"
"net/http"
)
func helloWorld(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!\n"))
}
func main() {
http.HandleFunc("/", helloWorld)
log.Fatal(http.ListenAndServe(":6666", nil))
}
打开一个终端窗口,启动HTTP服务器:
$ go run server_1.go
在另外一个终端窗口中运行curl命令:
$ curl localhost:6666
Hello World!
重复启动此服务器时,因为端口6666已被占用,会报错:
$ go run server_1.go
2021/06/17 05:44:04 listen tcp :6666: bind: address already in use
exit status 1
3 服务器端得到更多信息
$ curl -X "POST" localhost:6667/path -d "content"
如果我们把curl命令改的在复杂一点,那么发出去的HTTP Reqeust将带有更多信息:
POST /path HTTP/1.1
Host: localhost:6666
User-Agent: curl/7.68.0
Accept: */*
Content-Length: 7
Content-Type: application/x-www-form-urlencoded
content
其中,POST
称为HTTP Method,/path
称为URL,content
时HTTP body,在服务器端怎么得到这些信息呢,再看一个完整的例子:
package main
import (
"net/http"
"io/ioutil"
"log"
)
func helloWorld(w http.ResponseWriter, r *http.Request) {
log.Print("URL:", r.URL.Path)
log.Print("Method:", r.Method)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Print("Error reading body: %v", err)
return
}
log.Print("Body:", string(body))
w.Write([]byte("Hello World!\n"))
}
func main() {
http.HandleFunc("/", helloWorld)
log.Fatal(http.ListenAndServe(":6666", nil))
}
接到HTTP Reqeust后,服务器端打印如下:
2021/07/23 09:00:37 URL:/path
2021/07/23 09:00:37 Method:POST
2021/07/23 09:00:37 Body:content
4 处理HTTP Body
ioutil.ReadAll
读完一次以后,再读就没数据了。
假设我们有多个检查处理步骤,第一步处理完后,再将body交给下一步处理,我们可以这样。
package main
import (
"net/http"
"io/ioutil"
"bytes"
"log"
)
func helloWorld(w http.ResponseWriter, r *http.Request) {
log.Print("URL:", r.URL.Path)
log.Print("Method:", r.Method)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
log.Print("Error reading body: %v", err)
return
}
log.Print("Body(1st):", string(body))
r.Body = ioutil.NopCloser(bytes.NewBuffer(body))
body, err = ioutil.ReadAll(r.Body)
if err != nil {
log.Print("Error reading body: %v", err)
return
}
log.Print("Body(2nd):", string(body))
w.Write([]byte("Hello World!\n"))
}
func main() {
http.HandleFunc("/", helloWorld)
log.Fatal(http.ListenAndServe(":6666", nil))
}
运行结果:
2021/07/23 09:16:19 URL:/path
2021/07/23 09:16:19 Method:POST
2021/07/23 09:16:19 Body(1st):content
2021/07/23 09:16:19 Body(2nd):content
5 当HTTP Body为Json格式时
package main
import (
"net/http"
"encoding/json"
"log"
)
type UserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
}
func helloWorld(w http.ResponseWriter, r *http.Request) {
var req UserRequest
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
w.Write([]byte("Hello " + req.Name + "\n"))
}
func main() {
http.HandleFunc("/", helloWorld)
log.Fatal(http.ListenAndServe(":6666", nil))
}
执行crul命令的窗口,准备一个a.json,内容为:
{"Name": "Lane","Email": "123456@qq.com"}
$ curl localhost:6666/path -d @./a.json
Hello Lane
参考
https://yourbasic.org/golang/http-server-example/
https://www.honeybadger.io/blog/go-web-services/
https://zetcode.com/golang/http-server/
https://www.informit.com/articles/article.aspx?p=2861456&seqNum=7
https://stackoverflow.com/questions/43021058/golang-read-request-body
https://stackoverflow.com/questions/46948050/how-to-read-request-body-twice-in-golang-middleware
网友评论