赞
踩
微信小程序提供了以下几种网络请求API,分别是:
小程序要使用网络请求API需要事先设置服务器域名,小程序只可以向设定了域名的服务器发送网络请求。服务器域名请在【小程序后台】->【开发】->【开发设置】->【服务器域名】中进行配置。图4-1是服务器域名配置的一个例子,由于小程序中需要用到wx.request和wx.uploadFile接口,因此需要为request合法域名、uploadFile合法域名进行域名配置:
图 4-1 小程序域名设置
配置时需要注意:
"networkTimeout": {
"request": 10000,
"connectSocket": 10000,
"uploadFile": 10000,
"downloadFile": 10000
}
本节介绍wx.request接口和wx.uploadFile接口的使用,wx.connectSocket在以后的章节中再专门介绍。
在微信小程序中使用wx.request这个API来调用服务器接口来获取数据。wx.request接口的功能与浏览器的XMLHttpRequest接口功能类似,都是用于向Web服务器发送HTTP请求的,但二者在接口的访问方式上有所不同,wx.request接口参数说明见表:
参数名 | 类型 | 描述 |
---|---|---|
url | String | 开发者服务器接口地址 |
data | Object | 请求的参数 |
header | Object | 设置请求的 header,header 中不能设置 Referer,默认header[‘content-type’] = ‘application/json’ |
method | String | 有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT |
dataType | String | 回包的内容格式,如果设为json,会尝试对返回的数据做一次 JSON解析 |
success | Function | 收到开发者服务成功返回的回调函数,其参数是一个Object。 |
fail | Function | 接口调用失败的回调函数 |
complete | Function | 接口调用结束的回调函数(调用成功、失败都会执行) |
表 4-1 wx.request接口参数
小程序调用wx.request访问服务器接口的示例代码如下:
var url = 'http://127.0.0.1:8001/hello';
wx.request({
url: url,
success: function (res) {
if (res.statusCode === 200) {
console.log(res.data);
}
}
});
wx.request发送Get请求时,有两种方法把数据传递给服务器:通过URL上的参数以及通过data参数。以下的两个示例分别描述了数据的两种发送方式:
//通过url参数传递数据
wx.request({
url:'http://127.0.0.1:8001/hello?name=go',
success: function(res) {
console.log(res)
}
})
//通过data参数传递数据
wx.request({
url: 'http://127.0.0.1:8001/hello',
data: {name:'go'},
success: function(res) {
console.log(res)
}
})
wx.request的请求结果处理
在上面的代码中,小程序向地址为:http://127.0.0.1:8001/hello的服务器发送了一个HTTP的GET方法的请求,服务器处理请求并返回请求结果,小程序端收到请求结果会触发success回调,同时回调会带上一个Object信息,结构如下所示:
参数名 | 类型 | 描述 |
---|---|---|
data | Object/String | 开发者服务器返回的数据 |
statusCode | Number | 开发者服务器返回的 HTTP 状态码 |
header | Object | 开发者服务器返回的 HTTP Response Header |
表格 4-2 success回调参数
需要说明的是success回调参数的data字段类型是根据header[‘content-type’]决定的,默认header[‘content-type’]是’application/json’,在触发success回调前,小程序宿主环境会对data字段的值做JSON解析,如果解析成功,那么data字段的值会被设置成解析后的Object对象,其他情况data字段都是String类型。
wx.request发送POST请求
需要说明的是小程序中的URL是有长度限制的,其最大长度是1024个字节,同时URL上的参数还需要做一次urlEncode。向服务端发送的数据超过1024字节时,就要采用HTTP的POST方法发送数据,此时传递的数据就必须使用data参数。基于这个情况,一般建议传递数据时,都要使用data参数来传递。下面的代码是使用wx.request发送POST请求的用法:
wx.request({
url: 'http://127.0.0.1:8001/hello',
method: "POST",
header: { "Content-Type": "application/x-www-form-urlencoded" },
data: {
name: "go",
},
success: function (res) {
console.log(res)
}
});
客户端应用在提交一个POST请求时,需要设置请求头:Content-Type来告诉服务器消息主体的编码方式。服务器收到请求后根据请求头:Content-Type字段来获知消息主体的编码方式,并对主体进行解析。下面介绍几种常用Content-Type编码方式。
x-www-form-urlencoded是浏览器最常见的表单提交数据的方式,浏览器的Form表单,如果不设置enctype属性,默认采用application/x-www-form-urlencoded编码方式提交数据。提交的表单数据会转换为键值对并按照key1=val1&key2=val2的方式进行编码。当表单使用 application/x-www-form-urlencoded 时,需要对表单数据进行 urlencode编码。例如若要发送以下键值对数据:
name:Website
host:https://www.baidu.com
首先需要对数据进行urlencode编码,然后拼接为字符串。经过编码后的字符串格式为:
name=Website&host=https%3A%2F%2Fwww.baidu.com
小程序采用x-www-form-urlencoded编码方式向服务器发送数据的示例代码如下所示:
wx.request({
url: 'http://127.0.0.1:8001/hello?name=go&price=12',
method: "POST",
header: {"Content-Type": "application/x-www-form-urlencoded"},
data: {
name: "golan",
number: "12",
},
success: function (res) {
if (res.statusCode === 200) {
console.log(res.data);
}
}
});
图4-2是application/x-www-form-urlencoded的编码结果的截图:
图 4-2 application/x-www-form-urlencoded编码结果
将Content-Type设置为application/json来告诉服务端请求消息的主体是一个JSON字符串,期望服务器使用JSON数据格式来解析主体的数据。JSON格式的优点是它可以支持比键值对更为复杂的数据结构。
微信小程序的wx.request接口支持x-www-form-urlencoded和application/json编码方式。在使用wx.request接口时通过header[‘Content-Type’]来指定data数据的编码方式。若没有指定Content-Type,缺省的编码方式为:application/json。
示例代码:使用application/json编码方式发送数据:
wx.request({
url: 'http://127.0.0.1:8001/hello?name=go&price=12',
method: "POST",
header: {"Content-Type": "application/json"},
data: {
name: "golan",
number: "12",
},
success: function (res) {
if (res.statusCode === 200) {
console.log(res.data);
}
}
});
图4-3是application/json的编码结果截图:
图4-3 application/json的编码结果
在使用浏览器表单上传文件时,必须设置form的enctyped为multipart/form-data。multipart/form-data 顾名思义可以上传多个form-data并且用分隔符(boundary)进行分隔。multipart/form-data既可以上传文件,也可以上传键值对,不过多用于文件上传。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 –boundary–作为结束标志。下面是一个multipart/form-data编码结果的例子,例子中的boundary为”XXX”:
--XXX\n
Content-Disposition: form-data; name="name"\n
\n
golan\n
--XXX\n
Content-Disposition: form-data; name="number"\n
\n
12\n
--XXX--\n
在小程序中使用wx.uploadFile上传本地资源到服务器时客户端会发起一个HTTPS POST请求,其中 的content-type就是 multipart/form-data。
在一个小程序应用中的大多数页面会用到网络API,因此上最好把请求的URL地址的公共部分存储到小程序的全局变量中,例如:
App({
globalData: {
domain: "http://127.0.0.1:8001",
ssid:""
}
})
有了URL地址的全局变量,在调用wx.request就不需要重复输入URL根地址,特别是小程序上线时只需要将全局变量中的URL地址修改为正式的地址即可完成全部地址的修改。采用了URL地址全局变量后,wx.request写法如下:
var domain = getApp().globalData.domain;
wx.request({
url: domain+'/hello?name=go&price=12',
method: "POST",
header: {"Content-Type": "application/json"},
data: {
name: "golan",
number: "12",
},
success: function (res) {
if (res.statusCode === 200) {
console.log(res.data);
}
}
});
在开始编写处理HTTP请求的服务端代码之前先看看Go语言的encoding/json标准库。Go语言内建对JSON的支持,使用Go语言内置的encoding/json标准库,开发者可以轻松使用Go语言来生成和解析JSON格式的数据。
使用json.Marshal()函数可以对数据进行JSON格式的编码。json.Marshal()函数的声明如下:
func Marshal(v interface{}) ([]byte, error)
示例代码:json_encode.go
package main import ( "encoding/json" "fmt" ) type Product struct { Id int64 Name string Number int Price float64 } func main() { p := &Product{} p.Id = 1 p.Name = "ShangPin" p.Number = 200 p.Price = 4000.0 data, _ := json.Marshal(p) fmt.Println(string(data)) } // 结果 //{"Id":1,"Name":"ShangPin","Number":200,"Price":4000}
从上面的输出结果可以看到,json.Marsha()编码时缺省采用了struct的变量名称作为json的key进行输出。但有些时候直接使用struct的变量名作为key就不太合适,例如与我们进行网络交互的第三方约定了json输出格式。在Go语言中采用struct的Tag来控制字段的json编码方式。Tag就是标签意思,给结构体的每个字段打上一个标签,通过标签来告诉json编码器是否输出该字段,以及编码是的输出名称,例如:
type Product struct {
Id int64 `json:"-"` // 表示不编码是不输出该字段
Name string `json:"p_name"`
Number int `json:"p_number"`
Price float64 `json:"p_price"`
}
//json编码后的结果
//{"p_name":"ShangPin","p_number":200,"p_price":4000}
使用json.Unmarshal()函数将JSON格式的文本解码为Go语言的数据结构。json.Unmarshal()函数的原型如下:
func Unmarshal(data []byte, v interface{}) error
该函数的第一个参数是byte数组,即JSON格式的文本,第二个参数是interface{}类型的参数,通常为一个struct对象的指针,用于存放解码后的值。使用json.Unmarshal()函数进行解码的示例如下所示:
package main import ( "encoding/json" "fmt" "log" ) type Product struct { Id int64 `json:"-"` Name string `json:"p_name"` Number int `json:"p_number"` Price float64 `json:"p_price"` } func main() { var JSON = `{"p_name":"ShangPin","p_number":200,"p_price":4000}` var ent Product err := json.Unmarshal([]byte(JSON), &ent) if err != nil { log.Fatal("Error:", err) return } fmt.Println(ent) }
Go的HTTP请求的处理函数中的*http.Request参数代表了客户端的请求结构,http.Request提供了方法获取URL、请求主体中的数据。对于x-www-form-urlencoded和multipart/form-data编码格式的数据,调用http.Request的解析函数后数据存储到r.Form和r.PostForm中。具体规则如下:
1)URL中的参数解析后存储到r.Form中
2)请求主体中的参数解析后同时存储到r.Form和r.PostForm中
http.Request提供了r.FormValue函数和r.PostFormValue函数来获取客户端请求参数。r.PostFormValue与r.FormValue的区别是r.FormValue从r.Form中获取数据,r.PostFormValue从r.PostForm获取数据,因此调用r.PostFormValue不能获取到URL中的参数。下面代码是通过r.FormValue函数获取客户端参数的例子:
func hello(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
fmt.Fprintf(w, "Hello %s", name)
}
若客户端参数中存在同名参数,r.FormValue只会返回第一个参数,并且优先返回POST主体中的参数。您若需要获取全部同名参数,可以通过r.Form或r.PostForm来获取。r.Form和r.PostForm是url.Values字典类型,r.Form[“xxx”]取到的是一个数组类型的结果。http.request在解析参数的时候会将同名的参数都放进同一个数组中。需要说明的是,调用r.Form或r.PostForm来获取参数之前必须首先调用ParseForm()或ParseMultipartForm()(若Content-Type为multipart/form-data)函数。
func hello(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
names := r.Form["name"]
name_str := ""
for _, name := range names {
name_str += name + " "
}
fmt.Fprintf(w, "Hello %s", name_str)
}
对于图4-4中客户端的请求参数:
图4-4 客户端请求参数
执行hello()处理函数后,会有如下输出:
Hello golan go
application/json请求的处理
ParseForm或ParseMultipartForm只能处理编码格式为x-www-form-urlencoded和multipart/form-data的数据,这主要是因为r.Form和r.PostForm是url.Values字典类型,不适合存储JSON格式的数据。对于编码格式为application/json的数据的解析开发者要自己去完成解析。首先是从r.Body读取数据,然后使用json.Unmarshal()函数对读取的数据进行解码。以下代码是对客户端json编码格式的数据进行解析的示例代码:
func hello_json(w http.ResponseWriter, r *http.Request) {
bdata, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "", http.StatusBadRequest)
return
}
ent := make(map[string]interface{})
err = json.Unmarshal(bdata, &ent)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Println(ent)
}
图4-5是客户端的发送的application/json编码格式的请求数据:
图4-5 客户端请求数据
执行hello_json()处理函数后,会有如下输出:
map[name:golan number:12]
很多小程序应用都有上传图片的需求。在小程序中图片文件的上传涉及到两个接口的调用,首先调用wx.chooseImage接口从本地相册选择图片,或使用相机拍照,然后调用wx.uploadFile接口将本地资源上传到服务器。本节将介绍wx.chooseImage接口和wx.uploadFile接口的使用方法,以及实现一个Web接口用于接收小程序上传的文件,并保存在文件目录中。
wx.chooseImage接口从本地相册选择图片,或使用相机拍照,以下是wx.chooseImage接口的参数说明:
<view class="container">
<view style="padding:20rpx;width:100%;text-align:center" bindtap="onAddImg">
<image style="width:100rpx;height:100rpx;border:1px solid #999" src="{{img_src}}"></image>
</view>
<view style="padding:10rpx">
<button type="default">上传照片</button>
</view>
</view>
wx.chooseImage接口调用的示例代码:js文件
Page({ data: { img_src:'', }, onAddImg:function(e){ wx.chooseImage({ count:1, sizeType: ['compressed'], //original 原图,compressed 压缩图,默认二者都有 sourceType:['album','camera'],//album 从相册选图,camera 使用相机,默认二者都有 success: function(res) { //tempFilePath可以作为img标签的src属性显示图片 page.setData({ img_src:res.tempFilePaths }); } }) } })
在小程序中调用wx.uploadFile接口将本地资源上传到服务器。调用wx.uploadFile接口时客户端发起一个HTTPS的POST请求,其中 content-type 要指定为 multipart/form-data。以下是wx.uploadFile接口的参数说明:
function uploadImg(page, img_src) {
var domain = getApp().globalData.domain;
var url = domain + '/img_upload';
var ssid = getApp().globalData.ssid
wx.uploadFile({
url: url,
filePath: img_src,
name: 'img',
header: { "Content-Type": "multipart/form-data" },
formData: { ssid: ssid },
success: function (res) {
console.log(res);
}
});
}
小程序文件上传接口使用multipart/form-data编码格式对文件数据进行编码并发送给服务器。在服务端调用http.Request的解析函数后客户端的键值对数据会被存储到r.Form、r.PostForm以及r.MultipartForm.Value中,文件相关的数据被保存到r.MultipartForm.File中。Go提供了r.FormFile函数用于读取客户端上传的文件数据。
func HandlerImgUpload(w http.ResponseWriter, r *http.Request) { file_key := "img" //这里的名称一定要与wx.uploadFile的name一致 imgFile, imgHead, imgErr := r.FormFile(file_key) if imgErr != nil { return } defer imgFile.Close() //提取文件扩展名 imgFormat := strings.Split(imgHead.Filename, ".") ftype := imgFormat[len(imgFormat)-1] fileBytes, _ := ioutil.ReadAll(imgFile) //写文件到指定位置 fname := fmt.Sprintf("./%d.%s", time.Now().Unix(), ftype) err = ioutil.WriteFile(fname, fileBytes, 0666) if err != nil { return } w.Write([]byte(fname)) }
服务端的图片数据通常不用文件系统来保存,主要的原因是现在的Web服务器都是多台服务器为客户端提供访问服务,客户端的请求通过负载均衡服务器分发到您的业务服务器。若采用文件系统,有可能造成所需要获取的文件没有存放到本机。建议大家使用云平台提供的图片存储方案,例如阿里云的对象存储(Object Storage Service,简称OSS)、腾讯云的COS。使用这些云平台的图片存储技术可以大大减少开发工作量、系统维护工作量,另外在系统的稳定性、安全行以及访问性能方面成熟的云平台通常都好于自己的实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。