Web 操作
单控制器和多控制器
单控制器
type MyHandle struct{}
func (m *MyHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "返回的数据哈哈") // 相当于 w.Write([]byte("返回的数据哈哈"))
}
func main() {
h := MyHandle{}
server := http.Server{Addr: ":8090", Handler: &h}
server.ListenAndServe() // 相当于 http.ListenAndServe(":8090", &h)
}
多控制器
在实际开发中,大部分情况不应该只有一个控制器,不同的请求应该交给不同的处理单元。在 Golang 中支持两种多处理方式:
- 多个处理器 (Handle)
- 多个处理函数 (HandleFunc)
1、多处理器
即使用 http.Handle 把不同的 URL 绑定到不同的处理器中。
type MyHandle struct{}
type MyOtherHandle struct{}
func (m *MyHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "返回的数据哈哈")
}
func (m *MyOtherHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "返回的数据哈哈")
}
func main() {
h := MyHandle{}
h2 := MyOtherHandle{}
http.Handle("/a", &h)
http.Handle("/abc", &h2)
http.ListenAndServe(":8090", nil)
}
2、多处理函数
把不同的 URL 绑定到不同的函数上。
func first(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "多处理函数--first")
}
func second(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "多处理函数--second")
}
func main() {
http.HandleFunc("/f", first)
http.HandleFunc("/s", second)
http.ListenAndServe(":8090", nil)
}
获取请求头和获取请求参数
1、获取请求头
func now(w http.ResponseWriter, r *http.Request) {
h := r.Header
fmt.Println(h)
fmt.Println(h["Accept-Encoding"])
fmt.Println(len(h["Accept-Encoding"]))
}
func main() {
http.HandleFunc("/now", now)
http.ListenAndServe(":9090", nil)
}
2、获取请求参数
func now(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // 必须先解析成 Form,否则无数据
fmt.Println(r.Form)
fmt.Println(r.Form["url_long"])
fmt.Println(r.Form["url_long"][0])
}
func main() {
http.HandleFunc("/now", now)
http.ListenAndServe(":9090", nil)
}
3、验证表单的输入
// 验证手机号
// 使用 MatchString 函数和正则表达式
// 其他一些字符串都可以这样判断
if m, _ := regexp.MatchString(`^(1[3|4|5|8][0-9]\d{4,8})$`, r.Form.Get("mobile")); !m {
return false
}
Web 项目结构
--项目名
--src
--static
--css
--images
--js
--view
--index.html
--main.go
HTML 模版和静态资源显示
1、HTML 模版显示
Go 语言标准库中 html/template 包提供了 HTML 模版支持,把 HTML 当作模版可以在访问控制器时显示 HTML 模版信息。
使用 template.ParseFiles() 可以解析多个模版文件。
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, nil)
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
目录结构
(此处原图链接已移除)
2、静态资源显示
把静态资源文件放到特定的文件夹中,使用 Go 语言的文件服务就可以进行加载。
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, nil)
}
func main() {
http.HandleFunc("/", welcome)
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.ListenAndServe(":9090", nil)
}
目录结构
(此处原图链接已移除)
向模版中传递数据和调用函数
一、向模版中传递数据
- 可以在 HTML 中使用
{{}}获取template.Execute()第二个参数传递的值。 - 最常用的
{{.}}中的.是指针,指向当前变量,成为 "dot"。
1、字符串类型
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, "smallming")
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
<body>
<pre> 尊敬的{{.}}先生/女士 您已经被我公司录取。</pre>
</body>
2、结构体类型
type User struct {
Name string
Age int
}
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
t.Execute(w, User{"张三", 19})
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
<body>
<pre> 姓名:{{.Name}}<br/> 年龄:{{.Age}}</pre>
</body>
PS:结构体的字段一定要首字母大写,否则模版访问不到。
3、map 类型
type User struct {
Name string
Age int
}
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
m := make(map[string]interface{})
m["user"] = User{"张三", 19}
m["money"] = 1000
t.Execute(w, m)
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
<body>
<pre> 姓名:{{.user.Name}}<br/> 年龄:{{.user.Age}}<br/> 工资:{{.money}}</pre>
</body>
二、在模版中调用函数
1、调用系统函数
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/index.html")
time1 := time.Now()
t.Execute(w, time1)
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
<body>
<pre> 完整时间:{{.}} 年:{{.Year}} 月:{{.Month}} 日:{{.Day}} 格式化时间:{{.Format "2006-01-02 15:04:05"}}</pre>
</body>
2、调用自定义函数
如果希望调用自定义函数,需要借助 html/template 包下的 FuncMap 进行映射。
函数被添加映射后,只能通过函数在 FuncMap 中的 key 调用函数。
func MyFormat(t time.Time) string {
return t.Format("2006-01-02 15:04:05")
}
func welcome(w http.ResponseWriter, r *http.Request) {
// 将自定义函数绑定到 map 上
fm := template.FuncMap{"mf": MyFormat}
// 新建一个空模版,将函数绑定到该模版
t := template.New("index.html").Funcs(fm)
// 把我们的 html 解析到该空模版上
t, _ = t.ParseFiles("view/index.html")
time1 := time.Now()
t.Execute(w, time1)
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
<body>
<pre> 完整时间:{{.}} 调用格式化函数:{{mf .}}</pre>
</body>
模版使用 if、range 等
1、二元比较运算函数
eq <=>== ne <=>!= lt < gt > le <= ge >=
2、if 语句
<body> 取出数据{{.}}<br/>
{{if .}} 执行了 if {{else}} 执行了 else {{end}}<br/> html 模版信息 </body>
<body> {{$n:=123}} {{if gt $n 456}} 执行 if {{else}} 执行 else {{end}} </body>
3、range 的使用
<body> {{.}}<br/> {{range .}} {{.}}<br/> {{end}} </body>
模版嵌套
- 在实际项目中常常出现页面复用的情况,例如:整个网站的头部信息和底部信息复用。
- 可以使用动作
{{template '模版名称'}}来引用模版。 - 引用的模版必须在 HTML 中定义这个模版。
定义名称
{{define "模版名称"}} html {{end}}
示例:
{{define "head"}} <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body> head.html {{.}} </body></html> {{end}}
{{define "foot"}} <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body> foot.html {{.}} </body></html> {{end}}
{{define "layout"}} <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><scripttype="text/javascript"src="/static/js/index.js"></script></head><body> {{/*引用 head 模版*/}} {{template "head" "head 的参数"}}<br/> 中间内容<br/> {{template "foot" "head 的参数"}} </body></html> {{end}}
在 Go 中需要加载所有文件再选定主模版。
func welcome(w http.ResponseWriter, r *http.Request) {
// 解析所有文件
t, err := template.ParseFiles("view/index.gohtml", "view/head.gohtml", "view/foot.gohtml")
if err != nil {
log.Printf("err:%v", err)
}
// 选定主模版 layout
t.ExecuteTemplate(w, "layout", nil)
}
func main() {
http.HandleFunc("/", welcome)
http.ListenAndServe(":9090", nil)
}
文件上传
文件中 ioutil 包已经弃用,这里使用 io 包和 os 包。
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>文件上传</title></head><body><formaction="upload"method="post"enctype="multipart/form-data"> 文件名:<inputtype="text"name="name"/><br/> 文件:<inputtype="file"name="file"/><br/><inputtype="submit"value="提交"/></form></body></html>
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>文件上传</title></head><body> 文件上传成功 </body></html>
func welcome(w http.ResponseWriter, r *http.Request) {
t, _ := template.ParseFiles("view/filetest.gohtml")
t.Execute(w, nil)
}
func upload(w http.ResponseWriter, r *http.Request) {
// 从表单中获取名为"name"的值
fileName := r.FormValue("name")
// 从表单中获取名为"file"的文件字段
// file 是文件句柄
// fileHeader 是文件的元信息,包括文件名、大小等
file, fileHeader, _ := r.FormFile("file")
// 读取文件中的内容
b, _ := io.ReadAll(file)
os.WriteFile("D:/"+fileName+fileHeader.Filename[strings.LastIndex(fileHeader.Filename, "."):], b, 0777)
t, _ := template.ParseFiles("view/success.gohtml")
t.Execute(w, nil)
}
func main() {
http.HandleFunc("/", welcome)
http.HandleFunc("/upload", upload)
http.ListenAndServe(":9090", nil)
}
文件下载
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>文件上传</title></head><body><ahref="download?filename=abc.png">下载</a></body></html>
func download(w http.ResponseWriter, r *http.Request) {
// 从表单中获取文件名称
filename := r.FormValue("filename")
// 根据路径去找文件
f, err := os.ReadFile("D:/" + filename)
if err != nil {
fmt.Fprintln(w, "文件下载失败", err)
return
}
h := w.Header()
// 设置响应内容的 MIME 类型为二进制流
// 设置为文件处理,而不是直接显示
h.Set("Content-Type", "application/octet-stream")
// attachment 设置内容处置方式为附件下载
// filename="..."指定下载时的默认文件名
h.Set("Content-Disposition", "attachment;filename="+filename)
// 将文件内容写入 http 响应体发送
w.Write(f)
}


