所有文章

web应用开发

编写一个基于安全协议的WEB服务器,并将80端口重定向到443端口。

创建一个服务端

package main

func main() {
	// 指定证书文件并启动服务
	s := &http.Server{Addr: ":443", Handler: buildMux()}
	go s.ListenAndServeTLS("/opt/ssl/cert.pem", "/opt/ssl/privkey.pem")

	// 再创建一个监听在80端口的Server,它负责把80上的所有请求重定向到443端口
	s80 := &http.Server{Addr: ":80", Handler: http.HandlerFunc(redirect80), ErrorLog: ctx.GetLogger()}
	go s80.ListenAndServe()

	// 创建一个channel,用来监听kill信号
	stop := make(chan string, 1)
	defer close(stop)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
	<-stop
	
	s.Shutdown(context.Background())
	s80.Shutdown(context.Background())
}

func redirect80(w http.ResponseWriter, r *http.Request) {
        target := "https://" + r.Host + r.URL.Path
        if len(r.URL.RawQuery) > 0 {
                target += "?" + r.URL.RawQuery
        }

        http.Redirect(w, r, target, http.StatusTemporaryRedirect)
}

URI匹配规则

下面代码是向ServerMux注册一个Handler:

mux.HandleFunc("/users", user)

理想情况是当访问/users/jack时执行user函数,而实事上你会得到一个404,它只能通过/users被访问,为了达到理想结果,正确的写法应该是下面这样:

mux.HandleFunc("/users/", user)

这样一来,当我们访问/users/jack时,服务器会先匹配/users/jack,如果没有该路径则会查找/users/,如果还没有匹配到则查找/,如果还没有就404。

参数解析规则

func (m *Manager) myHandler(w http.ResponseWriter, r *http.Request) {
	println(r.FormValue("name")) // jack
	println(r.PostFormValue("phone")) // 空

	resume, _ := ioutil.ReadAll(r.Body)
	println(string(resume)) // user=jack&age=18
}
  1. go中有2个函数用于获取参数,FormValue()用于解析url和body中的参数,PostFormValue()只解析body中的参数而不解析url中的参数
  2. 当客户端设置Content-Type的值为application/x-www-form-urlencoded时,body中的数据会被解码一次,然后当作键值对解析,最后保存在map结构中,当值为application/jsonapplication/octet-stream等时,body中的原始数据不会被进行任何处理。

请求体只能被读取一次

有时候我们会写这样的代码:

func user(w http.ResponseWriter, r *http.Request) {
	// 先测试一下r.Body中有没有值
	body := ioutil.ReadAll(r.Body)
	println(body)
	
        method := r.Method
        if method == POST {
		user := r.FormValue("user")
                println(user)
        }
}

然后发起请求:

curl -H "Content-Type: application/x-www-form-urlencoded" -d 'user=jack' localhost/users/

结果是在第一次获取请求体时没问题,但r.FormValue("user")返回的值为空,为什么?这是因为r.Body本身是个输入流,即然是流当然是有指针的,当第一次从流中读完数据后,流中的指针已经指向了流的最后,当执行r.FormValue("user")的时候,它会触发r.ParseForm()函数,进而再次从r.Body中读取数据并解析到map中供我们使用,但这时流中已经没有数据可以读了,自然也就得不到正确的结果。


编写日期:2017-12-27