搭建
go的标准库net/http提供了HTTP客户端和服务端的实现,本章参考《Go语言高并发与微服务实战》一书,对其goweb代码进行分析实践。
该代码的主要目标是实现一个简单的http服务器,对通过浏览器传过来的数据进行解析并保存,并通过浏览器查询保存的数据。包括一个简单的html代码与go代码。
html页面代码如下,将其保存为login.tpl文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Test</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="登录">
</form>
</body>
</html>
go代码如下,被命名为StoreTest.go,在该代码中有详细注释
package main
import (
"encoding/json"
"fmt"
"html/template"
"log"
"net/http"
)
/**
* @Description: 构建一个用户的结构体
*/
type UserS struct {
Id int
Name string
Password string
}
// 模拟存储,以id为键,UserS为值
var UserById = make(map[int]*UserS)
// 用户名为键,同名的人组成的切片为值
var UserByName = make(map[string][]*UserS)
/**
* @Description: 用于接收页面提交的表单数据
* @param w ResponseWriter接口被HTTP处理器用于构造HTTP回复
* @param r Request类型代表一个服务端接受到的或者客户端发送出去的HTTP请求
*/
func loginMemory(w http.ResponseWriter, r *http.Request) {
// 打印页面请求方式
fmt.Println("method: ", r.Method)
if r.Method == "GET" {
// ParseFiles方法解析filenames指定的文件里的模板定义并将解析结果与t关联。
// 如果发生错误,会停止解析并返回nil,否则返回(t, nil)。至少要提供一个文件。
t, _ := template.ParseFiles("login.tpl")
// Execute方法将解析好的模板应用到data上,并将输出写入wr。
// 如果执行时出现错误,会停止执行,但有可能已经写入wr部分数据。模板可以安全的并发执行
log.Println(t.Execute(w, nil))
} else {
// ParseForm解析URL中的查询字符串,并将解析结果更新到r.Form字段
_ = r.ParseForm()
fmt.Println("username: ", r.Form["username"])
fmt.Println("password: ", r.Form["password"])
// 创建对应的结构体,这里将id固定为1
user1 := UserS{1, r.Form.Get("username"), r.Form.Get("password")}
if pwd := r.Form.Get("password"); pwd == "123456" {
// 将该用户存储
store(user1)
// Fprintf根据format参数生成格式化的字符串并写入w。返回写入的字节数和遇到的任何错误
// 即返回页面显示内容
w.Header().Set("Content-Type","application/json")
// 自定义头部
w.Header().Set("X-Custom-Header","custom")
// 写入状态码
w.WriteHeader(201)
// 将该结构体解析成json
json,_ := json.Marshal(user1)
// 写入用户
w.Write(json)
fmt.Fprintf(w, "欢迎登录,Hello %s!", r.Form.Get("username"))
} else {
fmt.Fprintf(w, "密码错误,请重新输入!")
}
}
}
/**
* @Description: 存储用户信息
* @param user 含有用户信息的结构体
*/
func store(user UserS) {
UserById[user.Id] = &user
UserByName[user.Name] = append(UserByName[user.Name], &user)
}
/**
* @Description: 查询用户信息,这里都写死了,没有给出info页面所需要的表单
* @param w ResponseWriter接口被HTTP处理器用于构造HTTP回复
* @param r Request类型代表一个服务端接受到的或者客户端发送出去的HTTP请求
*/
func userInfo(w http.ResponseWriter, r *http.Request) {
fmt.Println(UserById[1])
r.ParseForm()
// 遍历同名的人
for _, user := range UserByName[r.Form.Get("username")] {
fmt.Println(w, " %v", user)
}
}
func main() {
// HandleFunc注册一个处理器函数handler和对应的模式pattern(注册到DefaultServeMux)。
// ServeMux的文档解释了模式的匹配机制。
// 即将相对路径与对应处理方法绑定,就像java里的@RequestMapping注解,映射URL
http.HandleFunc("/login", loginMemory)
http.HandleFunc("/info", userInfo)
// ListenAndServe监听TCP地址addr,并且会使用handler参数调用Serve函数处理接收到的连接。
// handler参数一般会设为nil,此时会使用DefaultServeMux。
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
两个文件的存放结构如下,go代码放在main包,login.tpl与main包是同级关系
一些函数作用
为了方便大家,就把go标准库中文版的一些注释贴上来了
html/template包
就是用于解析上述login.tpl文件的,不过中文注释方法名有误,注意一下就行。
net/http包
http.ResponseWriter,嵌套了Header
http.ResponseWriter里面的Header
http.Request,这个结构体字段太多了,可前往手册查阅
http.Request的ParseForm()方法
HandleFunc
ListenAndServe
fmt包
FPrintf()
运行
刚运行时,浏览器输入localhost:8080/login,界面是这样的
控制台界面是这样的
在表单提交数据后,密码错误时
密码正确时
然后在浏览器输入localhost:8080/info,因为并没有给浏览器返回数据,所以是空白的
控制台会出现