写在前面
博主很早之前就接触过net包,使用过里面的方法来进行前后端的一个交互。
当时,对于这一块知识是一点都不理解。也不明白为什么HandFunc后面加个函数就可以处理请求了。
时隔多日,博主终于再次捡起昔日的“科研精神”,直面自己内心的恐惧,对net/http进行基础的学习,也算是小于收获。写下此文,希望能给各位带来一定的帮助。
1.看图
首先给大家画一幅图…,如下
通过这张图,我们大致能看到Client和Sever通过http通信的过程,博主在这里简单描述一下:
1.客户端发送请求,通过多路复用器选择到对应的处理器
2.处理器和数据库交互,处理用户的请求
3.处理完毕后,需要通过模板引擎选择对应的模板,将数据渲染到模板上
4.并最终在用户界面上显示。
2.看例子
// 处理器
func sayHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"hello")
}
func main() {
// HandleFunc注册路径处理函数:在根路由上注册了sayHandler这个处理器
http.HandleFunc("/say",sayHandler)
e := http.ListenAndServe(":8080",nil)
if e != nil {
fmt.Println(e)
}
}
上述例子中,sayHandler
就是一个处理器。所以说,处理器的功能就是用来处理用户的请求。
接着我们来看http.ResponseWriter
这个参数
http.ResponseWriter 是一个接口类型
源码:
大家根据他的英文意思猜测一下,也能明白个七七八八。改类型是用来响应客户端的请求的。也就是向服务端对客户端的响应,发送到客户端。
下面我们来看ListenAndServe
:
e := http.ListenAndServe(":8080",nil)
大家一定能想到该方法的第一参数是要服务端要监听的端口。那么第二个参数是什么,为什么这里写的是nil
?
大家结合上面的图,我们已经了解了处理器、响应接口,那么聪明的你一定能想到,这第二个参数就是没有涉及到的复用器了。这里传入nil
,表示使用
默认的多路复用器DefalutServeMux
.
说了半天复用器,那它到底是个啥,起到什么作用?
复用器是一种特殊的处理器,它的作用是将用户的请求送到处理改请求的对应的处理器中。
不然,你想想,我们每天登录网站,进行一些操作。如果乱搭配处理器,那么整个功能不就乱套了。
那你说,我不想用默认的复用器,我自己造一个行不行?这必须行啊。
看代码:
type SayHello struct {
Name string
}
type SayNo struct {
Name string
}
// 创建一个处理器,实现ServerHTTP方法
func (s *SayHello) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"+s.Name))
}
func (s *SayNo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"+s.Name))
}
func main() {
// 创建一个多路复用器
sm := http.NewServeMux()
// 绑定相应的路由
sm.Handle("/hello",&SayHello{Name:"王小明"})
sm.Handle("/no",&SayNo{Name:"no"})
// 使用多路复用器
e := http.ListenAndServe("127.0.0.1:8080",sm)
if e != nil {
fmt.Println(e)
}
}
上面代码中,大家可能看到了创建一个处理器,实现ServerHTTP方法
这句话,那这又是什么意思呢,接着往下看吧。
1.首先我们去看下Hanlder
处理器源码
看我们发现了什么,处理器是一个接口类型。也就是只要是实现了ServeHTTP
方法的类型,都是一个处理器。
现在明白为什么要这样写了吧。
但是,每次都要实现serveHTTP方法,感觉好麻烦啊,有没有更简洁的写法,别慌,真有,看这里
// 处理器
func sayHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w,"hello")
}
func main() {
// HandleFunc注册路径处理函数:在根路由上注册了sayHandler这个处理器
http.HandleFunc("/say",sayHandler)
e := http.ListenAndServe(":8080",nil)
if e != nil {
fmt.Println(e)
}
}
细心的你一定发现,这是刚开始的例子。**这里我们只是写了一个函数,**注册的时候换成HandleFunc
就可以实现和之前例子的相同的功能。
这时候,好奇的你又该问了,这又是为什么啊?
让我们进一步探索,来到HandleFunc
的源码处
可以看到传入了一个处理器类型的函数
继续挖掘,最终来到了这里
看我们发现了什么,handlerFunc
(不是handleFunc
注意啊),也是一个处理器,只不过是函数类型的处理器。
到这里算是真相大白了,也就是说go在底层为我们做了一层封装,当我们传入一个函数类型的时候,它在底层也实现了ServeHTTP方法。所以也是一个处理器。
所以说如果你想简单的实现的话,可以使用HandleFunc
进行注册处理器。
不然的话,就使用Handle
吧。
插一句:
hanlder和handlerFunc
是处理器
handle和handlefunc
是注册函数…
不要搞混…(虽然博主曾经搞混过…笑)
说了这么多,咱们来看看http中一些常用的方法
http.Get()
http.Post()
想必大家都不陌生。发送请求嘛…
下面写个例子,练练手
Client端:
apiUrl := "http://127.0.0.1:8080/receive"
// url.Values里面是键值对
data := url.Values{}
var name string
var content string
fmt.Println("请输入发送的内容")
// 读取标准流输入
fmt.Scanln(&name,&content)
// fmt.Println(name,content)
// 设置要发送的数据
data.Set("send_name",name)
data.Set("content",content)
// 将原始url字符串传换成url对象
u,err := url.ParseRequestURI(apiUrl)
if err != nil {
fmt.Println("路径转换出错")
}
// 将data数据进行编码,并添加到url对象
u.RawQuery = data.Encode()
// 将url路径转换成字符串发送请求
response,err := http.Get(u.String())
if err != nil{
fmt.Println("发送失败")
}
defer response.Body.Close()
// 读取服务端响应的数据
b,err := ioutil.ReadAll(response.Body)
if err != nil{
fmt.Println("获取数据失败")
}
fmt.Println(string(b))
}
Server端:
// get请求
func receiveGetHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
// 获取路径中的数据
// 测试用...可以写上
// data := r.URL.Query()
// fmt.Println(data.Get("send_name"))
// fmt.Println(data.Get("content"))
answer := `{"status":"ok"}`
// 写入响应内容
w.Write([]byte(answer))
}
func main() {
server := &http.Server{
Addr: "127.0.0.1:8080",
Handler: nil,
}
http.HandleFunc("/receive",receiveGetHandler)
// 已经构造过地址和处理器了,不用在ListenAndServe中在写了啦
e := server.ListenAndServe()
if e != nil {
fmt.Println("连接服务器失败")
}
}
结果:
好了,码字不易,希望大家多多提出自己宝贵的意见!!