在开发的过程中,难免会调用其他的开发组的API,有时候这些API提供的是http接口或者是RPC接口,当服务是http接口时,如果需要在后端请求接口,那么则需要通过http client来发送请求
请求场景
- 上传文件的同时,还需要附带请求参数
- 发送JSON请求体
整体思路
- 将请求内容放入到一个输出流中
- 将输出流作为请求的参数
- 发送请求
请求参数+文件上传
- 准备一个buffer用于缓冲请求参数和文件
- 创建一个multipartWriter,用于写请求参数和文件
- 在writer中写请求参数和文件
- 发送请求
如果你想上传一个文件,附带一个对文件的描述,在HTML中它是这样的
<form
action="http://upload.com/post"
method="post"
enctype="multipart/form-data"
>
<input type="text" name="description"/>
<input type="file" name="fileName">
<input type="submit">
</form>
话不多说上代码
func uploadFile(fileName string, targetUrl string) error {
bodyBuf := &bytes.Buffer{}
bw := multipart.NewWriter(bodyBuf)
//模拟表单中的text字段
bw.WriteField("description", "一本好书")
//模拟表单中文件上传的字段
fileWriter, err := bw.CreateFormFile("fileName", fileName)
if err != nil {
fmt.Println("write to buffer error")
return err
}
//打开需要上传的文件
fh, err := os.Open(fileName)
if err != nil {
fmt.Println("open file error")
return err
}
defer fh.Close()
//将文件流copy到创建好的文件上传流中
_, err = io.Copy(fileWriter, fh)
if err != nil {
return err
}
//multipart/form-data
contentType := bw.FormDataContentType()
bw.Close()
//发送请求
resp, err := http.Post(targetUrl, contentType, bodyBuf)
if err != nil {
return err
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
fmt.Println(respBody)
return nil
}
发送JSON数据
- 创建JSON编码器
- 将结构体编码到字节流
- 将字节流作为请求参数
- 发送请求
//服务端
func Handeler(respWriter http.ResponseWriter, req *http.Request) {
if req.Method == "POST" {
msg := Msg{}
json.NewDecoder(req.Body).Decode(&msg)
fmt.Println("des:", msg.Description)
}
respWriter.Write([]byte("every thing is ok!"))
}
func main() {
http.HandleFunc("/test", Handeler)
http.ListenAndServe(":8080", nil)
}
//客户端
type Msg struct {
Description string `json:"description"`
}
func main() {
msg := Msg{"test"}
bf := &bytes.Buffer{}
//创建JSON编码器并编码struct结构体
err := json.NewEncoder(bf).Encode(msg)
if err != nil {
fmt.Println("encoding error")
os.Exit(-1)
}
//发送请求
req, err := http.NewRequest("POST", "http://localhost:8080/test", bf)
client := &http.Client{
Timeout: time.Millisecond * 100,
}
resp, err := client.Do(req)
fmt.Println(resp.Status)
resByte, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(resByte))
}
踩坑
- http.Response的Body需要用ioutil去读取
- 不是直接调用resp.Body.read()方法,这样做会读不到响应内容。Body.read()是当网络连接失败或者服务端终止了请求响应,此时会返回error