我們知道,很多時候為了方便,直接在小程序前端直接調用第三方提供商的接口獲取數據,然后顯示在小程序上,這種方式也是我們常規的使用接口方式,不過這種方式有個弊端,就是一旦第三方對小程序禁止,那么就無法再獲取數據了,類似於豆瓣的電影數據接口,在前幾個月還是可以正常訪問的,后來不知基於什么原因,就禁止了小程序端的接口調用了,為了解決這個問題,我們可以使用接口中轉的代理方式,通過調用自身的API接口獲取數據,自身的API對第三方接口進行封裝即可。本篇隨筆以豆瓣接口為例,實現接口的中轉處理,從而確保小程序前端數據獲取的正常。
1、小程序豆瓣接口調用異常
我在較早期的隨筆中《微信小程序豆瓣電影項目的改造過程經驗分享》介紹一個利用豆瓣接口獲取其電影資料的小程序,小程序的界面如下所示。
不過由於小程序被豆瓣端進行調用接口,那么再運行小程序就有接口錯誤了,如下所示。
而我們一般都已經在小程序的設置上添加了豆瓣的域名了
不過由於豆瓣官方對小程序端的限制,那么我們就無法進行正常的接口數據獲取了。
2、豆瓣電影數據接口
我們可以從豆瓣的接口文檔中了解到,豆瓣提供了不少電影相關的數據接口,這個是我們用來練手或者加工小程序的很好數據來源,它的接口如下所示。
豆瓣電影接口的API地址如下所示:https://developers.douban.com/wiki/?title=movie_v2
3、前端接口的修改
既然小程序段無法再通過接口方式獲取電影數據,但測試電腦端是沒問題的,那么我們可以利用自己的API接口對豆瓣接口進行封裝,從而實現接口數據的代理接口處理,為了方便,我們還是盡可能把代理接口弄得通用一些,以便其他接口也可以通過中轉方式獲取,提高中轉接口的通用性。
我們先來看看小程序端的JS端對接口的調用代碼,如下是獲取電影數據的其中一個接口,如下所示。
//通用的熱映、待映的獲取方式 fetchFilms: function(page, url, city, start, count) { return new Promise((resolve, reject) => { var that = page; var json = {city: city, start: start, count: count }; var type = "json";//特殊設置,默認是application/json if (that.data.hasMore) { app.utils.get(url, json, type).then(res => { if(res.subjects.length === 0){ that.setData({ hasMore: false, }) }else{ that.setData({ films: that.data.films.concat(res.subjects), start: that.data.start + res.subjects.length, showLoading: false }) } wx.stopPullDownRefresh(); resolve(res); }) } }) },
這里面核心是通過調用 app.utils.get(url, json, type) 的方式來處理對API的GET方式調用,我們再來看看這個Get方式的調用
//發布的接口 module.exports = { Promise,makeArray,getUserInfo, get:requestGet,post:requestPost,request,decodeHtml, setStorage,getStorage,getLocation,getCityName, getDate,getTime,formatTime,getDateDiff,getWeek,get2,post2 }
這個 util.js 里面公布的get接口就是 requestGet函數,我們用了別名的方式,它的詳細代碼如下所示
function requestGet(url,data,type){ return request(url,'GET',data,type) } function requestPost(url,data,type){ return request(url,'POST',data,type) } //封裝Request請求方法 function request(url, method, data = {}, type='application/json'){ wx.showNavigationBarLoading(); return new Promise((resove,reject) => { wx.request({ url: url, data: data, header: {'Content-Type': type}, method: method.toUpperCase(), // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT success: function(res){ wx.hideNavigationBarLoading() resove(res.data) }, fail: function(msg) { console.log('reqest error',msg) wx.hideNavigationBarLoading() reject('fail') } }) }) }
通過以上的分析,如果我們需要避免修改太多地方,那么我們把 app.utils.get(url, json, type) 里面的 get 修改為 get2 的方式就好了,其他參數變化等中轉信息,封裝在 get2 函數內部就好,這樣我們只需修改一個函數名稱就是了。
中轉接口后,處理的方式有所不同,我們需要把整個帶有參數的地址作為一個完整的URL傳遞給服務器接口(需要URL編碼,否則無法獲得參數),而且由於通用接口無法返回JSON格式,我們還需要返回的字符串內容進行JSON格式的轉換。
//使用內部接口進行中轉 var wrapper_get_url = 'http://localhost:27206/api/third/httpwrapper/httpget'; function get2(url, data = {}, type='application/json') { var newdata = {}; var newUrl = wrapper_get_url + "?url=" + encodeURIComponent(url + "?" + json2Form(data)); console.log(newUrl); return new Promise((resolve, reject) => { wx.request({ url: newUrl, data: newdata, headers: {'Content-Type': type}, success: function(res) { //將中轉接口返回的字符串轉換為JSON對象 var res = convertJson(res.data); resolve(res) }, fail: function(res) { //將中轉接口返回的字符串轉換為JSON對象 var res = convertJson(res.data); reject(res) } }) }) }
上面的代碼我們使用自己的本地接口進行測試,其中需要提交的URL是我們服務器的URL,原先的URL+數據組合為一個新的參數傳遞給服務器使用。
另外,由於返回的中轉接口數據為字符串信息,非JSON格式,那么還需要將中轉接口返回的字符串轉換為JSON對象。
//轉義字符串為JSON對象 function convertJson(res) { //將中轉獲取到的字符串轉換為JSON對象 var jsonStr = JSON.stringify(res); var jsonStrSym = jsonStr.replace('/', '\\'); var jsondata = JSON.parse(JSON.parse(jsonStr)); console.log(jsondata); return jsondata; };
以上就是完整的前端代碼了,通過上面的處理,我們可以正常的獲得接口的數據,並可以正常的展示了,正式發布的時候,我們修改用上自己的服務器域名地址即可發布使用了。
4、后端的代理接口代碼封裝
前面介紹了小程序的前端代碼修改,只需要引入一個新的函數就可以實現數據的中轉獲取了,后端我們自然配合這個接口實現數據的獲取即可。
我們在后端增加一個控制器,增加對應的接口定義,如下代碼所示。
namespace WebAPI.Areas.Third.Controllers { /// <summary> /// 對第三方的Http接口數據進行包裝,獲取數據,避免小程序並禁止獲取數據的問題 /// </summary> public class HttpWrapperController : BaseApiController { /// <summary> /// get方式獲取數據 /// </summary> /// <param name="url">接口URL</param> /// <returns></returns> [HttpGet] public string HttpGet(string url) { HttpHelper helper = new HttpHelper(); var html = helper.GetHtml(url, "", false); return html; }
是不是比較簡單,這樣就可以實現通用的接口數據中轉了。
最后測試得到的正常數據展示界面,如下所示。