ps: 之前以为阅读作业都是自己默默做了就好了,现在才知道写了博客是有加分的o(╥﹏╥)o
本次博客是关于《Golang web 应用开发》的阅读分享,本书在github
共14章,有着32.8k star(我也在其中哈哈),目录结构为
我原以为它只是一本关于golang web开发的书,结果它居然从头开始讲起,看了一下质量很高,是十分优质的入门tutorial(gotour也是,不知道为什么golang的教程都这么优质)。前面两章我们已有基础略过,后面的章节有些超出我时间精力范围,所以没有阅读到,我主要想分享的是第三第四章的重要部分(主要是顶层部分和与下次作业相关的部分),它们也和我们的下次作业有关。
我们在用golang进行web开发之前需要先了解web的工作流程,它的工作流程图如下:
客户端先请求DNS解析域名,然后访问服务端,等待回复,最后更新页面,这就是WEB的工作简单流程,这实际上是计算机网络的知识。
其中DNS解析的过程常常涉及到多级DNS域名服务器:
向服务器发出请求也有不同的方法:
最为常见的方法是GET和POST,一个用于获取信息,一个用于提交信息。
Request包代表了请求信息的内容:
服务器会返回相应的内容Response:
这就是web的简单基础,我们尝试着用golang来搭建一个简易的WEB服务器,这里我们使用书中的示例代码
package main
import (
"fmt"
"net/http"
"strings"
"log"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}
func main() {
http.HandleFunc("/", sayhelloName) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
这部分代码实现的功能就是监听本地9090端口,并且根据任何路径和任何请求方法都返回"Hello astaxie!"字符串
代码实际上只设置了一个访问路由,并为其分配了sayhelloName方法:
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析参数,默认是不会解析的
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}
该方法会打印reques的各个字段,并且给response写入字符串。
这里涉及到监听端口的部分:
err := http.ListenAndServe(":9090", nil) //设置监听的端口
一台主机有许多端口,每个端口和各自的进程相绑定,端口类似于门牌号,为数据包找到正确的程序宿主,端口的绑定常常用socket(套接字),一个WEB服务器通过套接字来监听端口的大概流程是这样的:
Web服务器通常有两种请求方法:GET和POST。
POST是提交方法,它一般就是通过表单来实现的。
我们先来看看示例代码
package main
import (
"fmt"
"html/template"
"log"
"net/http"
"strings"
)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() //解析url传递的参数,对于POST则解析响应包的主体(request body)
//注意:如果没有调用ParseForm方法,下面无法获取表单的数据
fmt.Println(r.Form) //这些信息是输出到服务器端的打印信息
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //获取请求的方法
if r.Method == "GET" {
t, _ := template.ParseFiles("login.gtpl")
log.Println(t.Execute(w, nil))
} else {
//请求的是登录数据,那么执行登录的逻辑判断
r.ParseForm()
fmt.Println("username:", r.Form["username"])
fmt.Println("password:", r.Form["password"])
}
}
func main() {
http.HandleFunc("/", sayhelloName) //设置访问的路由
http.HandleFunc("/login", login) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
需要先准备好一个login.gtpl
<html>
<head>
<title>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>
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //获取请求的方法
if r.Method == "GET" {
t, _ := template.ParseFiles("login.gtpl")
log.Println(t.Execute(w, nil))
} else {
//请求的是登录数据,那么执行登录的逻辑判断
r.PaserForm()
fmt.Println("username:", r.Form["username"])
fmt.Println("password:", r.Form["password"])
}
}
它会根据请求方法的不同执行不同的逻辑
需要注意的是源代码漏了r.PaserForm()
,这会导致解析出来的字段全是空的。
验证表达的输入是否合法我们用正则表达式即可,在这里不赘述了。
func upload(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //获取请求的方法
if r.Method == "GET" {
crutime := time.Now().Unix()
h := md5.New()
io.WriteString(h, strconv.FormatInt(crutime, 10))
token := fmt.Sprintf("%x", h.Sum(nil))
t, _ := template.ParseFiles("upload.gtpl")
t.Execute(w, token)
} else {
r.ParseMultipartForm(32 << 20)
file, handler, err := r.FormFile("uploadfile")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) // 此处假设当前目录下已存在test目录
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
}
}
我们需要提前创建好test文件夹,不然会出现路径错误
这段代码的逻辑也比较简单,主要涉及到的就是几个api的调用。
看了这本书的几个章节(其实是个github),感叹道golang的相关教程资源之完善强大,比CSDN高到不知道哪里去了,感谢开源精神。
看了几个章节对golang搭建web有了初步了解了,感觉比以前的node.js好用不少,golang让我看到了未来编程语言的希望,的确如潘老师所讲,有了golang,你还要框架干什么,golang这种简单易用的特性让我很喜欢。