上一篇讲解了client端相关的知识,这一篇讲解服务端。golang语言可以快速实现一个简单的server端,如下所示:
package main
import (
"net/http"
"log"
)
type TestHandler struct {
str string
}
func SayHello(w http.ResponseWriter, r *http.Request){
log.Printf("HandleFunc")
w.Write([]byte(string("HandleFunc")))
}
//ServeHTTP方法,绑定TestHandler
func (th *TestHandler)ServeHTTP(w http.ResponseWriter, r *http.Request){
log.Printf("Handle")
w.Write([]byte(string("Handle")))
}
func main(){
http.Handle("/", &TestHandler{"Hi"})//根路由
http.HandleFunc("/test", SayHello) //test路由
http.ListenAndServe("127.0.0.1:8000",nil)}
上述代码就轻松实现一个监听本地8000端口的服务端。大家可能注意到,代码调用两个路由的处理函数:Handle和HandleFunc,大家可以任选一个使用。大多数情况下我们选择HandleFunc,因为其第二个参数是我们想要的处理函数;而Handle的第二个参数是一个handler对象,该对象必须实现ServeHTTP方法,我们在ServeHTTP方法中完成我们的处理逻辑,显然直接使用HandleFunc要方便一些。实际上从源码中可以看到HandleFunc最终也是调用了Handle函数完成操作。在接下来的代码中将只用HandleFunc进行演示。
假设做一个登录功能,结合上一篇文章的知识点,很容易写出client端的代码如下:
package main
import (
"io/ioutil"
"log"
"net/http"
"sync"
)
var wc sync.WaitGroup
func HelloClient(client *http.Client, url string, method string){
defer wc.Done()
req, err := http.NewRequest(method, url, nil)
if err != nil {
log.Fatal(err)
}
rep, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(rep.Body)
rep.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
func main() {
client := &http.Client{}
rootURL := "http://127.0.0.1:8000/"
wc.Add(1)
go HelloClient(client, rootURL + string("login"), "GET")
wc.Wait()
}
那么服务端经过改造后得到如下代码:
package main
import (
"net/http"
"log"
)
func login(w http.ResponseWriter, r *http.Request) {
log.Printf("login")
w.Write([]byte(string("login")))
}
func main(){
http.HandleFunc("/login", login)
http.ListenAndServe("127.0.0.1:8000", nil)
}
这就可以实现client端访问server端,当然也可以从浏览器直接访问。然而这种方式存在一些问题,在如下所示的HandleFunc函数的源码中发现其调用的实际上是默认的DefaultServeMux对象。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
那么如果我们只要自定义ServeMux对象,然后再调用HandleFunc函数就可以实现更加灵活的路由功能,接下来我们对mian函数进行改造。
func main() {
mux := http.NewServeMux()//声明多路复用mux对象
mux.HandleFunc("/login", login)
//通过实现mux的ServeHTTP方法可实现路由功能
http.ListenAndServe("127.0.0.1:8000", mux)//路由注册
}
另外可以自定义server对象,设置读超时、写超时等多种参数。如下所示,我们声明一个server变量,设置超时时间为2秒。
func main() {
server := &http.Server{
Addr: "127.0.0.1:8000",
ReadTimeout: 2 * time.Second,
WriteTimeout: 2 * time.Second,
}
mux := http.NewServeMux()
mux.HandleFunc("/login", login)
server.Handler = mux
server.ListenAndServe()
}
同时将login函数修改一下,产生3秒的睡眠,超过设置的2秒超时,服务器端则不会返回任何数据,所以此时client端也无法获取数据。
func login(w http.ResponseWriter, r *http.Request) {
log.Printf("login")
time.Sleep(time.Second * 3)
w.Write([]byte(string("login")))
}
下面演示一下client如何用POST方法向服务器提交数据,需要将GET方法修改成POST方法。
var wc sync.WaitGroup
//HelloClient sdfsa.
func HelloClient(client *http.Client, url string, method string) {
defer wc.Done()
if method=="POST":
reqData := "name=ali&age=19"
req, err := http.NewRequest(method, url, strings.NewReader(reqData))
else:
req, err := http.NewRequest(method, url, nil) //GET
if err != nil {
log.Fatal(err)
}
//设置Content-Type很重要
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rep, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
data, err := ioutil.ReadAll(rep.Body)
rep.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("%s", data)
}
func main() {
client := &http.Client{}
rootURL := "http://127.0.0.1:8001/post"
wc.Add(1)
go HelloClient(client, rootURL, "POST")
wc.Wait()
}
而对应的server端需要在login函数中加上接受数据的逻辑即可:
func login(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(string("post")))
r.ParseForm()
fmt.Println(r.Form)
fmt.Println(r.Form["name"])
fmt.Println(r.Form["age"])
}