xdm,咱今天分享一个 golang web 实战的 demo
go 的 http 包,以前都有或多或多的提到一些,也有一些笔记在我们的历史文章中,今天来一个简单的实战
HTTP 编程 Get
先来一个 小例子,简单的写一个 Get 请求
-
拿句柄
-
设置监听地址和端口
-
进行数据处理
package mainimport ("fmt""net/http" )func myHandle(w http.ResponseWriter, req *http.Request){defer req.Body.Close()par := req.URL.Query()fmt.Println("par :",par)//回写数据fmt.Fprintln(w,"name",par.Get("name"),"hobby",par.Get("hobby"))}// server 端 func main() {http.HandleFunc("/", myHandle)err := http.ListenAndServe("0.0.0.0:9999", nil)if err != nil{fmt.Printf("ListenAndServe err : %v",err)return}}
上述的代码比较简单,就是一个简单的 http get 请求 , 主要处理数据的是 myHandle 函数
Client 客户端 实现方法 get
-
client.go
- get方法、post方法、patch方法、head方法、put方法等等,用法基本一致
- 设置url
- get (或者其他方法)方法请求 url
- 处理数据
package mainimport ("fmt""io/ioutil""net/http""net/url" )//httpserver 端 func main() {//1.处理请求参数params := url.Values{}params.Set("name", "xiaomotong")params.Set("hobby", "乒乓球")//2.设置请求URLrawUrl := "http://127.0.0.1:9999"reqURL, err := url.ParseRequestURI(rawUrl)if err != nil {fmt.Printf("url.ParseRequestURI() 函数执行错误,错误为:%v\n", err)return}//3.整合请求URL和参数reqURL.RawQuery = params.Encode()//4.发送HTTP请求// reqURL.String() String将URL重构为一个合法URL字符串。fmt.Println("Get url:", reqURL.String())resp, err := http.Get(reqURL.String())if err != nil {fmt.Printf("http.Get()函数执行错误,错误为:%v\n", err)return}defer resp.Body.Close()//5.一次性读取响应的所有内容body, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Printf("ioutil.ReadAll()函数执行出错,错误为:%v\n", err)return}fmt.Println("Response: ", string(body)) }
上述编码中有使用到 reqURL.RawQuery = params.Encode()
Encode 方法将请求参数编码为 url 编码格式 (“a=123&b=345”),编码时会以键进行排序
常见状态码
- http.StatusContinue = 100
- http.StatusOK = 200
- http.StatusFound = 302
- http.StatusBadRequest = 400
- http.StatusUnauthorized = 401
- http.StatusForbidden = 403
- http.StatusNotFound = 404
- http.StatusInternalServerError = 500
HTTP 编程 Post 方法
-
编写 server 代码 server.go
-
设置句柄
-
设置监听地址和端口
-
处理相应数据
package mainimport ("fmt""io/ioutil""net/http" )func handPost(w http.ResponseWriter, req *http.Request) {defer req.Body.Close()if req.Method == http.MethodPost {b, err := ioutil.ReadAll(req.Body)if err != nil {fmt.Printf("ReadAll err %v", err)return}fmt.Println(string(b))resp := `{"status":"200 OK"}`w.Write([]byte(resp))fmt.Println("reponse post func")} else {fmt.Println("can't handle ", req.Method)w.Write([]byte(http.StatusText(http.StatusBadRequest)))} }//post serverfunc main() {http.HandleFunc("/", handPost)err := http.ListenAndServe("0.0.0.0:9999", nil)if err != nil {fmt.Printf("ListenAndServe err %v", err)return} }
Client 客户端 实现
-
client.go
- get方法、post方法、patch方法、head方法、put方法等等,用法基本一致
- 设置 url
- post 方法请求
- 处理数据
package mainimport ("fmt""io/ioutil""net/http""strings" )//post clientfunc main() {reqUrl := "http://127.0.0.1:9999"contentType := "application/json"data := `{"name":"xiaomotong","age":18}`resp, err := http.Post(reqUrl, contentType, strings.NewReader(data))if err != nil {fmt.Printf("Post err %v", err)return}defer resp.Body.Close()b, err := ioutil.ReadAll(resp.Body)if err != nil {fmt.Printf("ReadAll err %v", err)return}fmt.Println(string(b))}
上述 post 方法的编码 明显 比 get 方法的编码传参多了很多,我们一起来看看官方源码是如何做的
func Post(url, contentType string, body io.Reader) (resp *Response, err error) {return DefaultClient.Post(url, contentType, body)
}
- url
请求地址
- contentType
内容的类型,例如 application/json
- body
具体的请求体内容,此处是 io.Reader 类型的,因此我们传入数据的时候,也需要转成这个类型
表单 form 的处理
既然是 web 相关的实战,表单肯定是一个离不开的话题 , golang 里面当然有对表单的实际处理功能
- 前面逻辑一样,服务端开启服务,监听端口
- 每个路由对应这个处理函数
- 处理函数中
request.ParseForm()解析表单的具体数据
package mainimport ("fmt""io""net/http"
)const form = `<html><body><form action="#" method="post" name="bar"><input type="text" name="in"/><input type="text" name="out"/><input type="submit" value="Submit"/></form></html></body>`func HomeServer(w http.ResponseWriter, request *http.Request) {io.WriteString(w, "<h1>/test1 或者/test2</h1>")
}func SimpleServer(w http.ResponseWriter, request *http.Request) {io.WriteString(w, "<h1>hello, xiaomotong</h1>")
}func FormServer(w http.ResponseWriter, request *http.Request) {w.Header().Set("Content-Type", "text/html")switch request.Method {case "GET":io.WriteString(w, form)case "POST":request.ParseForm()fmt.Println("request.Form[in]:", request.Form["in"])io.WriteString(w, request.Form["in"][0])io.WriteString(w, "\n")io.WriteString(w, request.Form["out"][0])}
}
func main() {http.HandleFunc("/", HomeServer)http.HandleFunc("/test1", SimpleServer)http.HandleFunc("/test2", FormServer)err := http.ListenAndServe(":9999", nil)if err != nil {fmt.Printf("http.ListenAndServe()函数执行错误,错误为:%v\n", err)return}
}
上述编码解析表单的逻辑是:
对于 POST、PUT 和P ATCH 请求,它会读取请求体并解析它,作为一个表单,会将结果放入r.PostForm 和 r.Form 中
请求体 r.Form 中的参数优先于 URL 查询字符串值
先来看看 Request 的结构 ,参数会比较多
type Request struct {Method stringURL *url.URL.... 此处省略多行 ...ContentLength int64//Form包含解析过的表单数据,包括URL字段的查询参数和PATCH、POST或PUT表单数据。//此字段仅在调用 ParseForm 后可用Form url.Values//PostForm包含来自 PATCH、POST或PUT主体参数的解析表单数据。//此字段仅在调用 ParseForm 后可用。PostForm url.Values//MultipartForm是解析的多部分表单,包括文件上传。//该字段仅在调用 parsemmultipartform 后可用。MultipartForm *multipart.FormTrailer HeaderRemoteAddr stringRequestURI stringTLS *tls.ConnectionStateCancel <-chan struct{}Response *Responsectx context.Context
}
下面是具体实现的源码,感兴趣的 xdm 可以打开 goland 看起来

实际处理逻辑在 func parsePostForm(r *Request) (vs url.Values, err error) {

这里需要注意
- 请求提的大小上限为10MB , 需要注意请求体的大小是否会被 MaxBytesReader 限制
模板
听到 模板 这个名词应该不陌生了吧,很多组件或者语言里面都有模板的概念
感兴趣的可以琢磨一下,我们放在下一篇补充
欢迎点赞,关注,收藏
朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
我是阿兵云原生,欢迎点赞关注收藏,下次见~








