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
}
- go中有2个函数用于获取参数,
FormValue()
用于解析url和body中的参数,PostFormValue()
只解析body中的参数而不解析url中的参数
- 当客户端设置
Content-Type
的值为application/x-www-form-urlencoded
时,body中的数据会被解码一次,然后当作键值对解析,最后保存在map结构中,当值为application/json
或application/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
中供我们使用,但这时流中已经没有数据可以读了,自然也就得不到正确的结果。