微信小程序开发实战4 与服务端进行交互

4.与服务端进行交互

微信小程序提供了以下几种网络请求API,分别是:

  • 普通HTTPS请求(wx.request)
  • 上传文件(wx.uploadFile)
  • 下载文件(wx.downloadFile)
  • WebSocket通信(wx.connectSocket)

小程序要使用网络请求API需要事先设置服务器域名,小程序只可以向设定了域名的服务器发送网络请求。服务器域名请在【小程序后台】->【开发】->【开发设置】->【服务器域名】中进行配置。图4-1是服务器域名配置的一个例子,由于小程序中需要用到wx.request和wx.uploadFile接口,因此需要为request合法域名、uploadFile合法域名进行域名配置:
图 4-1 小程序域名设置
图 4-1 小程序域名设置
配置时需要注意:

  • 域名不能使用IP地址(小程序的局域网 IP 除外)或 localhost;
  • 可以配置端口,如https://myserver.com:8080。
  • 如果不配置端口,例如https://myserver.com,那么请求的 URL中也不能包含端口。
  • 域名必须经过ICP备案。
    request默认的超时时间是60s,如果我们想自定义超时时间,我们可以在app.json中加入下面代码片段,分别设置request,socket,和上传文件及下载文件的超时时间
 "networkTimeout": {
    "request": 10000,
    "connectSocket": 10000,
    "uploadFile": 10000,
    "downloadFile": 10000
  }

本节介绍wx.request接口和wx.uploadFile接口的使用,wx.connectSocket在以后的章节中再专门介绍。

4.1发送HTTP请求

在微信小程序中使用wx.request这个API来调用服务器接口来获取数据。wx.request接口的功能与浏览器的XMLHttpRequest接口功能类似,都是用于向Web服务器发送HTTP请求的,但二者在接口的访问方式上有所不同,wx.request接口参数说明见表:

参数名类型描述
urlString开发者服务器接口地址
dataObject请求的参数
headerObject设置请求的 header,header 中不能设置 Referer,默认header[‘content-type’] = ‘application/json’
methodString有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
dataTypeString回包的内容格式,如果设为json,会尝试对返回的数据做一次 JSON解析
successFunction收到开发者服务成功返回的回调函数,其参数是一个Object。
failFunction接口调用失败的回调函数
completeFunction接口调用结束的回调函数(调用成功、失败都会执行)

表 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信息,结构如下所示:

参数名类型描述
dataObject/String开发者服务器返回的数据
statusCodeNumber开发者服务器返回的 HTTP 状态码
headerObject开发者服务器返回的 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)
    }
  });

4.2 Content-Type编码方式

客户端应用在提交一个POST请求时,需要设置请求头:Content-Type来告诉服务器消息主体的编码方式。服务器收到请求后根据请求头:Content-Type字段来获知消息主体的编码方式,并对主体进行解析。下面介绍几种常用Content-Type编码方式。

x-www-form-urlencoded

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编码结果

application/json

将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的编码结果

multipart/form-data

在使用浏览器表单上传文件时,必须设置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。

4.3服务器地址管理

在一个小程序应用中的大多数页面会用到网络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);
      }
    }
  });

4.4 JSON数据处理

在开始编写处理HTTP请求的服务端代码之前先看看Go语言的encoding/json标准库。Go语言内建对JSON的支持,使用Go语言内置的encoding/json标准库,开发者可以轻松使用Go语言来生成和解析JSON格式的数据。

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解码

使用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)
}

4.5处理HTTP请求

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]

4.6图片上传与接收

很多小程序应用都有上传图片的需求。在小程序中图片文件的上传涉及到两个接口的调用,首先调用wx.chooseImage接口从本地相册选择图片,或使用相机拍照,然后调用wx.uploadFile接口将本地资源上传到服务器。本节将介绍wx.chooseImage接口和wx.uploadFile接口的使用方法,以及实现一个Web接口用于接收小程序上传的文件,并保存在文件目录中。

图片选择接口

wx.chooseImage接口从本地相册选择图片,或使用相机拍照,以下是wx.chooseImage接口的参数说明:

  • count:最多可以选择的图片张数
  • sizeType:所选的图片的尺寸:[‘original’, ‘compressed’]
  • sourceType:选择图片的来源:[‘album’, ‘camera’]
  • success:接口调用成功的回调函数
  • fail:接口调用失败的回调函数
  • complete:接口调用结束的回调函数(调用成功、失败都会执行)
    wx.chooseImage接口调用的示例代码:WXML文件
<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接口的参数说明:

  • url:开发者服务器地址
  • filePath:要上传文件资源的路径
  • name:文件对应的 key,服务端可以通过这个 key 获取文件的内容
  • header:HTTP 请求 Header
  • formData:HTTP 请求中其他额外的form data
  • success:接口调用成功的回调函数
  • fail:接口调用失败的回调函数
  • complete:接口调用结束的回调函数(调用成功、失败都会执行)
    wx.uploadFile接口调用的示例代码:js文件
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。使用这些云平台的图片存储技术可以大大减少开发工作量、系统维护工作量,另外在系统的稳定性、安全行以及访问性能方面成熟的云平台通常都好于自己的实现。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go lang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值