xdm,咱明天分享一个 golang web 实战的 demo
go 的 http 包,以前都有或多或多的提到一些,也有一些笔记在咱们的历史文章中,明天来一个 简略的实战
HTTP 编程 Get
先来一个 小例子,简略的写一个 Get 申请
- 拿句柄
- 设置监听地址和端口
-
进行数据处理
package main import ( "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 main import ( "fmt" "io/ioutil" "net/http" "net/url" ) //httpserver 端 func main() { //1. 解决申请参数 params := url.Values{} params.Set("name", "xiaomotong") params.Set("hobby", "乒乓球") //2. 设置申请 URL rawUrl := "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 main import ( "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 server func 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 main import ( "fmt" "io/ioutil" "net/http" "strings" ) //post client func 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 main
import (
"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 string
URL *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.Form
Trailer Header
RemoteAddr string
RequestURI string
TLS *tls.ConnectionState
Cancel <-chan struct{}
Response *Response
ctx context.Context
}
上面是具体实现的源码,感兴趣的 xdm 能够关上 goland 看起来
理论解决逻辑在 func parsePostForm(r *Request) (vs url.Values, err error) {
这里须要留神
- 申请提的大小下限为 10MB,须要留神申请体的大小是否会被 MaxBytesReader 限度
模板
听到 模板 这个名词应该不生疏了吧,很多组件或者语言外面都有模板的概念
感兴趣的能够推敲一下,咱们放在下一篇补充
欢送点赞,关注,珍藏
敌人们,你的反对和激励,是我保持分享,提高质量的能源
好了,本次就到这里
技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。
我是 阿兵云原生,欢送点赞关注珍藏,下次见~