首先须要知道client是如何经过 http 协议 实现信息和参数的传递,以及server是怎么接受参数.php
能够看两篇博客 :html
Go发起Http请求及获取相关参数golang
golang web开发获取get、post、cookie参数web
client 发送请求主要使用的是 net/http 包中提供的方法来实现编程
tcp socket 通信须要本身封装协议下篇总结.json
?
分割URL和传输数据,参数之间以&
相连.如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD
。若是数据是英文字母/数字,原样发送,若是是空格,转换为+,若是是中文/其余字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD
,其中%XX中的XX为该符号以16进制表示的ASCII。服务器
参考:浅谈HTTP中Get与Post的区别cookie
func httpGet() { //发送get 请求 resp, err := http.Get("http://www.01happy.com/demo/accept.php?id=1") if err != nil { // handle error } defer resp.Body.Close() //读取response body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body)) }
使用这个方法的话,第二个参数要设置成 application/x-www-form-urlencoded
,不然post参数没法传递。app
若是是多个普通参数,使用 "&"
进行链接, 拼成字符串. 如 strings.NewReader("name=cjb&age=12&sex=man")
dom
func httpPost() { resp, err := http.Post("http://www.01happy.com/demo/accept.php", "application/x-www-form-urlencoded", strings.NewReader("name=cjb")) if err != nil { fmt.Println(err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body)) }
http.PostForm 底层依然是http.Post, 只是默认已经设置了 application/x-www-form-urlencoded
func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) { return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) }
因此在传数据的时候可使用 url.Values{} (type Values map[string][]string
) 进行设置值
func httpPostForm() { resp, err := http.PostForm("http://www.01happy.com/demo/accept.php", url.Values{"key": {"Value"}, "id": {"123"}}) if err != nil { // handle error } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body)) }
有时须要在请求的时候设置头参数、cookie之类的数据,就可使用http.Do方法。
必需要设定Content-Type为application/x-www-form-urlencoded
,post参数才可正常传递
若是是多个普通参数,使用 "&"
进行链接, 拼成字符串. 如 strings.NewReader("name=cjb&age=12&sex=man")
func httpDo() { client := &http.Client{} req, err := http.NewRequest("POST", "http://www.01happy.com/demo/accept.php", strings.NewReader("name=cjb")) if err != nil { // handle error } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Cookie", "name=anny") resp, err := client.Do(req) defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { // handle error } fmt.Println(string(body)) }
这里能够设置链接后读取超市等,这个时候须要用到 http.Transport
package main import ( "fmt" "io/ioutil" "net/http" "strings" "time" ) var timeout = time.Duration(20 * time.Second) func dialTimeout(network, addr string) (net.Conn, error) { return net.DialTimeout(network, addr, timeout) } func main() { tr := &http.Transport{ //使用带超时的链接函数 Dial: dialTimeout, //创建链接后读超时 ResponseHeaderTimeout: time.Second * 2, } client := &http.Client{ Transport: tr, //总超时,包含链接读写 Timeout: timeout, } req, _ := http.NewRequest("GET", "http://www.haiyun.me", nil) req.Header.Set("Connection", "keep-alive") res, err := client.Do(req) if err != nil { return } defer res.Body.Close() body, _ := ioutil.ReadAll(res.Body) fmt.Println(string(body)) for k, v := range res.Header { fmt.Println(k, strings.Join(v, "")) } }
使用代理或指定出口ip
//使用HTTP PROXY proxyUrl, err := url.Parse("http://host:port") tr := &http.Transport{ Proxy: http.ProxyURL(proxyUrl), } //指定出口IP ief, err := net.InterfaceByName("eth0") addrs, err := ief.Addrs() addr := &net.TCPAddr{ IP: addrs[0].(*net.IPNet).IP, } dia := net.Dialer{LocalAddr: addr} tr := &http.Transport{ Dial: dia.Dial, }
要发起head请求能够直接使用http client的 Head()方法
// Head issues a HEAD to the specified URL. If the response is one of the // following redirect codes, Head follows the redirect after calling the // Client's CheckRedirect function: // // 301 (Moved Permanently) // 302 (Found) // 303 (See Other) // 307 (Temporary Redirect) func (c *Client) Head(url string) (resp *Response, err error) { req, err := NewRequest("HEAD", url, nil) if err != nil { return nil, err } return c.doFollowingRedirects(req, shouldRedirectGet) }
post 带文件的客户端, 须要使用 mime/multipart
包将数据封装成一个form.
package main import ( "bytes" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "os" ) func postFile(url, filename, path, deviceType, deviceId string, filePath string) error { //打开文件句柄操做 file, err := os.Open(filePath) if err != nil { fmt.Println("error opening file") return err } defer file.Close() //建立一个模拟的form中的一个选项,这个form项如今是空的 bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) //关键的一步操做, 设置文件的上传参数叫uploadfile, 文件名是filename, //至关于如今还没选择文件, form项里选择文件的选项 fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) if err != nil { fmt.Println("error writing to buffer") return err } //iocopy 这里至关于选择了文件,将文件放到form中 _, err = io.Copy(fileWriter, file) if err != nil { return err } //获取上传文件的类型,multipart/form-data; boundary=... contentType := bodyWriter.FormDataContentType() //这个很关键,必须这样写关闭,不能使用defer关闭,否则会致使错误 bodyWriter.Close() //这里就是上传的其余参数设置,可使用 bodyWriter.WriteField(key, val) 方法 //也能够本身在从新使用 multipart.NewWriter 从新创建一项,这个再server 会有例子 params := map[string]string{ "filename" : filename, "path" : path, "deviceType" : deviceType, "deviceId" : deviceId, } //这种设置值得仿佛 和下面再重新建立一个的同样 for key, val := range params { _ = bodyWriter.WriteField(key, val) } //发送post请求到服务端 resp, err := http.Post(url, contentType, bodyBuf) if err != nil { return err } defer resp.Body.Close() resp_body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } fmt.Println(resp.Status) fmt.Println(string(resp_body)) return nil } // sample usage func main() { url := "http://localhost:8088/upload" filename := "json.zip" path := "/eagleeye" deviceType := "iphone" deviceId := "e6c5a83c5e20420286bb00b90b938d92" file := "./json.zip" //上传的文件 postFile(url, filename, path, deviceType, deviceId, file) }
使用 go http.request 的三个属性Form、PostForm、MultipartForm,来处理参数
r表示*http.Request类型,w表示http.ResponseWriter类型
go中参数传递为值传递,由于会在多个地方使用到 request 中传递的参数,其底层是struct 因此使用*Request。而ResponseWriter 是一个 interface{} 因此无所谓指针不指针,只要传递进去符合接口的类型就能够了。
r.ParseForm() r.Form.Get("filename")
详细例子查看示例 get 请求参数
这种取法在一般状况下都没有问题,可是若是是以下请求则没法取到须要的值:
<form action="http://localhost:9090/?id=1" method="POST"> <input type="text" name="id" value="2" /> <input type="submit" value="submit" /> </form>
由于r.Form包含了get和post参数,而且以post参数为先,上例post参数和get参数都有id,因此应当会取到post参数2。虽然这种状况并很少见,可是从严谨的角度来看程序上仍是应当处理这种状况。立马补上改进代码:
queryForm, err := url.ParseQuery(r.URL.RawQuery) if err == nil && len(queryForm["id"]) > 0 { fmt.Fprintln(w, queryForm["id"][0]) }
Content-Type = application/x-www-form-urlencoded
r.ParseForm() //第一种方式 id := r.Form.Get("id") //第二种方式 id2 := r.PostForm.Get("id") //第三种方式,底层是r.Form id3 := r.FormValue("id") //第四种方式,底层是FormValue id4 := r.PostFormValue("id")
详细例子查看示例 普通 post
**Content-Type=multipart/form-data **
由于须要上传文件,因此表单enctype要设置成multipart/form-data。此时没法经过PostFormValue来获取值,由于golang库里还未实现这个方法
//由于上传文件的类型是multipart/form-data 因此不能使用 r.ParseForm(), 这个只能得到普通post r.ParseMultipartForm(32 << 20) //上传最大文件限制32M //文件 file, handler, err := r.FormFile("uploadfile") if err != nil { fmt.Println(err)//上传错误 } defer file.Close() //普通参数同上普通post user := r.Form.Get("user")
cookie, err := r.Cookie("id") if err == nil { fmt.Fprintln(w, "Domain:", cookie.Domain) fmt.Fprintln(w, "Expires:", cookie.Expires) fmt.Fprintln(w, "Name:", cookie.Name) fmt.Fprintln(w, "Value:", cookie.Value) } r.Cookie返回*http.Cookie类型,能够获取到domain、过时时间、值等数据。
client :
package main import ( "net/http" ) func main() { httpGet() } func httpGet() { //发送get 请求 resp, err := http.Get("http://127.0.0.1:9090/upload?id=1&filename=test.zip") if err != nil { // handle error } defer resp.Body.Close() }
server :
package main import ( "net/http" "log" "fmt" ) func main() { http.HandleFunc("/upload", upload) err := http.ListenAndServe("127.0.0.1:9090", nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } } func upload(w http.ResponseWriter, r *http.Request) { fmt.Println(r.Method) //GET //这个很重要,必须写 r.ParseForm() id := r.Form.Get("id") filename := r.Form.Get("filename") fmt.Println(id, filename) // 1 test.zip //第二种方式,底层是r.Form id2 := r.FormValue("id") filename2 := r.FormValue("filename") fmt.Println(id2, filename2) // 1 test.zip }
client :
package main import ( "net/http" "strings" ) func main() { httpPost() } func httpPost() { //发送get 请求 resp, err := http.Post("http://127.0.0.1:9090/upload", "application/x-www-form-urlencoded", strings.NewReader("id=1&filename=test.zip")) if err != nil { // handle error } defer resp.Body.Close() }
server :
package main import ( "net/http" "log" "fmt" ) func main() { http.HandleFunc("/upload", upload) err := http.ListenAndServe("127.0.0.1:9090", nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } } func upload(w http.ResponseWriter, r *http.Request) { fmt.Println(r.Method) //POST //这个很重要,必须写 r.ParseForm() //第一种方式 id := r.Form.Get("id") filename := r.Form.Get("filename") fmt.Println(id, filename) // 1 test.zip //第二种方式 id2 := r.PostForm.Get("id") filename2 := r.PostForm.Get("filename") fmt.Println(id2, filename2, "===2====") // 1 test.zip //第三种方式,底层是r.Form id3 := r.FormValue("id") filename3 := r.FormValue("filename") fmt.Println(id3, filename3, "===3===") // 1 test.zip //第四种方式,底层是FormValue id4 := r.PostFormValue("id") filename4 := r.PostFormValue("filename") fmt.Println(id4, filename4, "=====4====") // 1 test.zip }
client :
//client package main import ( "fmt" "io/ioutil" "net/http" "net/url" ) func main() { httpPostSimple() } func httpPostSimple() { resp, err := http.PostForm("http://localhost:8080/send", url.Values{"value": {"postValue"}}) if err != nil { panic(err) } defer resp.Body.Close() resResult, err := ioutil.ReadAll(resp.Body) if err != nil { panic(err) } fmt.Println(string(resResult)) }
Server :
//Server package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/send", func(response http.ResponseWriter, request *http.Request) { request.ParseForm() result := request.Method + " " if request.Method == "POST" { result += request.PostFormValue("value") } else if request.Method == "GET" { result += request.FormValue("value") } fmt.Println(result) fmt.Println(request.Header) response.Write([]byte(result)) }) http.ListenAndServe(":8080", nil) }
参考:
https://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
client :
//json.zip 文件和该程序位于同一文件夹下 package main import ( "bytes" "fmt" "io" "io/ioutil" "mime/multipart" "net/http" "os" ) func postFile() error { //打开文件句柄操做 file, err := os.Open("json.zip") if err != nil { fmt.Println("error opening file") return err } defer file.Close() bodyBuf := &bytes.Buffer{} bodyWriter := multipart.NewWriter(bodyBuf) //关键的一步操做 fileWriter, err := bodyWriter.CreateFormFile("uploadfile", "json.zip") if err != nil { fmt.Println("error writing to buffer") return err } //iocopy _, err = io.Copy(fileWriter, file) if err != nil { return err } ////设置其余参数 //params := map[string]string{ // "user": "test", // "password": "123456", //} // ////这种设置值得仿佛 和下面再重新建立一个的同样 //for key, val := range params { // _ = bodyWriter.WriteField(key, val) //} //和上面那种效果同样 //创建第二个fields if fileWriter, err = bodyWriter.CreateFormField("user"); err != nil { fmt.Println(err, "----------4--------------") } if _, err = fileWriter.Write([]byte("test")); err != nil { fmt.Println(err, "----------5--------------") } //创建第三个fieds if fileWriter, err = bodyWriter.CreateFormField("password"); err != nil { fmt.Println(err, "----------4--------------") } if _, err = fileWriter.Write([]byte("123456")); err != nil { fmt.Println(err, "----------5--------------") } contentType := bodyWriter.FormDataContentType() bodyWriter.Close() resp, err := http.Post("http://127.0.0.1:9090/upload", contentType, bodyBuf) if err != nil { return err } defer resp.Body.Close() resp_body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } fmt.Println(resp.Status) fmt.Println(string(resp_body)) return nil } // sample usage func main() { postFile() }
bodyWriter.Close() 这里只能写在发送以前,不能使用defer 去关闭, 这个很关键
server :
package main import ( "fmt" "log" "net/http" ) // 处理/upload 逻辑 func upload(w http.ResponseWriter, r *http.Request) { fmt.Println("method:", r.Method) //POST //由于上传文件的类型是multipart/form-data 因此不能使用 r.ParseForm(), 这个只能得到普通post r.ParseMultipartForm(32 << 20) //上传最大文件限制32M user := r.Form.Get("user") password := r.Form.Get("password") file, handler, err := r.FormFile("uploadfile") if err != nil { fmt.Println(err, "--------1------------")//上传错误 } defer file.Close() fmt.Println(user, password, handler.Filename) //test 123456 json.zip } func main() { http.HandleFunc("/upload", upload) err := http.ListenAndServe("127.0.0.1:9090", nil) //设置监听的端口 if err != nil { log.Fatal("ListenAndServe: ", err) } }