上一篇讲解了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"])
}