go client http post upload上传及 server 参数获取
2017-09-06 16:56
465 查看
摘要: 在写上传 客户端 http api 碰到的问题总结
可以看两篇博客 :
Go发起Http请求及获取相关参数
golang web开发获取get、post、cookie参数
tcp socket 通讯需要自己封装协议下篇总结.
GET方式提交的数据最多只能是1024字节,理论上POST没有限制
如:
参考:浅谈HTTP中Get与Post的区别
如果是多个普通参数,使用
所以在传数据的时候可以使用 url.Values{} (
必须要设定Content-Type为
如果是多个普通参数,使用
这里可以设置连接后读取超市等,这个时候需要用到
使用代理或指定出口ip
参考:GO HTTP client客户端使用
Form:存储了post、put和get参数,在使用之前需要调用ParseForm方法。
PostForm:存储了post、put参数,在使用之前需要调用ParseForm方法。
MultipartForm:存储了包含了文件上传的表单的post参数,在使用前需要调用ParseMultipartForm方法。
r表示*http.Request类型,w表示http.ResponseWriter类型
go中参数传递为值传递,因为会在多个地方使用到 request 中传递的参数,其底层是struct 所以使用*Request。而ResponseWriter 是一个 interface{} 所以无所谓指针不指针,只要传递进去符合接口的类型就可以了。
详细例子查看示例 get 请求参数
这种取法在通常情况下都没有问题,但是如果是如下请求则无法取到需要的值:
因为r.Form包含了get和post参数,并且以post参数为先,上例post参数和get参数都有id,所以应当会取到post参数2。虽然这种情况并不多见,但是从严谨的角度来看程序上还是应当处理这种情况。立马补上改进代码:
详细例子查看示例 普通 post
因为需要上传文件,所以表单enctype要设置成multipart/form-data。此时无法通过PostFormValue来获取值,因为golang库里还未实现这个方法
server :
server :
Server :
https://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/
client :
bodyWriter.Close() 这里只能写在发送之前,不能使用defer 去关闭, 这个很关键
server :
go http 服务器编程
Go发起Http请求及获取相关参数
http://stackoverflow.com/questions/20205796/golang-post-data-using-the-content-type-multipart-form-data
go upload 遇到的问题
首先需要知道client是如何通过 http 协议 实现信息和参数的传递,以及server是怎么接受参数.可以看两篇博客 :
Go发起Http请求及获取相关参数
golang web开发获取get、post、cookie参数
客户端 发送请求方式
client 发送请求主要使用的是 net/http 包中提供的方法来实现tcp socket 通讯需要自己封装协议下篇总结.
http get 请求
GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),?分割URL和传输数据,参数之间以
&相连.
GET方式提交的数据最多只能是1024字节,理论上POST没有限制
如:
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的区别
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)) }
普通 http post 请求
http.Post 方式
使用这个方法的话,第二个参数要设置成application/x-www-form-urlencoded,否则post参数无法传递。
如果是多个普通参数,使用
"&"进行连接, 拼成字符串. 如
strings.NewReader("name=cjb&age=12&sex=man")
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.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)) }
更复杂情况 http.Client Do()
有时需要在请求的时候设置头参数、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, }
参考:GO HTTP client客户端使用
只发送head
要发起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请求
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,来处理参数Form:存储了post、put和get参数,在使用之前需要调用ParseForm方法。
PostForm:存储了post、put参数,在使用之前需要调用ParseForm方法。
MultipartForm:存储了包含了文件上传的表单的post参数,在使用前需要调用ParseMultipartForm方法。
r表示*http.Request类型,w表示http.ResponseWriter类型
go中参数传递为值传递,因为会在多个地方使用到 request 中传递的参数,其底层是struct 所以使用*Request。而ResponseWriter 是一个 interface{} 所以无所谓指针不指针,只要传递进去符合接口的类型就可以了。
get 参数获取
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]) }
普通的post表单请求
Content-Type = application/x-www-form-urlencodedr.ParseForm() //第一种方式 id := r.Form.Get("id") //第二种方式 id2 := r.PostForm.Get("id") //第三种方式,底层是r.Form id3 := r.FormValue("id") //第四种方式,底层是FormValue id4 := r.PostFormValue("id")
详细例子查看示例 普通 post
有文件上传 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 获取
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、过期时间、值等数据。
示例
get 请求参数
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 }
普通 post 请求
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 }
普通post 使用 PostForm
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) } }
参考例子网站
golang web开发获取get、post、cookie参数go http 服务器编程
Go发起Http请求及获取相关参数
http://stackoverflow.com/questions/20205796/golang-post-data-using-the-content-type-multipart-form-data
相关文章推荐
- HttpUploadFile模拟前台POST上传图片和后台获取上传图片并上传至服务器
- Method 为POST跨域时Client端请求及server端获取client请求的参数
- android向服务器上传multipart/form-data文件(upload using multipart post using httpclient in android)
- httpclient post 上传文件及参数包括中文乱码问题方案
- HttpClient通过post上传文件和提交参数
- HttpClient通过post上传文件和提交参数
- 利用HttpClient的POST方式发起带参数的请求时,点击注册按钮无反应状态分析
- 使用httpClient MultipartRequestEntity文件上传解析文件和普通表单参数
- Java:HttpClient篇,HttpClient4.2在Java中的几则应用:Get、Post参数、Session(会话)保持、Proxy(代理服务器)设置,多线程设置...
- httpclientpost网络获取数据
- node 上传文件 http client to post file
- httpclient小例子:获取手机号地址、IP地址获取、POST方式注册用户(乐蜂网)
- Http multipart/form-data多参数Post方式上传数据
- tp5的Request::instance()获取post、get、参数、表单上传的文件
- android----HttpClient的get,post和图片上传服务器
- HttpClient post方式,添加请求参数
- php通过$_SERVER['HTTP_USER_AGENT']获取浏览器相关参数
- 用HttpListener做web服务器,简单解析post方式过来的参数、上传的文件
- [c#][Windows 10 | Windows 10 mobile | Windows Phone] HttpClient 实现form-data POST上传请求
- httpclient不带参数获取值