在React Native開發過程當中,幾乎全部的app都須要使用到Http請求,因此fetch的封裝必不可少,因爲不一樣app的請求參數,解析規則,token機制等徹底不同,因此在大多數App開發中,先後臺Http請求的實現都是開發者本身封裝的。react
封裝一個先後臺Http請求實現須要多久?git
可能有人回答是1小時,也有三、5小時甚至更長時間的,或者也有說先這樣封裝個大概,等到需求不知足的時候再改。github
花費1小時的時間不必定短,花費三、5的時間也不必定算長,具體要看先後臺交互的複雜程度與開發者對交互實現的封裝程度。npm
那這裏咱們就引出了一個問題了,咱們一般說的app的Http請求【封裝】,到底封裝的是什麼,咱們須要作哪些工做,能使用得app的接口請求更簡單,易用且有較高的靈活性?在我看來這個「封裝」主要分兩個部分:json
-
數據交換 層面的封裝,即:react-native
- 實現先後臺的互通,支持服務器要求的數據交換類型、格式等
- 調用者能夠自由設置請求的header、params等參數,程序根據不一樣的設置也能保證請求能正確的發送給服務端並返回相應的結果
- 支持超時、日誌打印等一些基本功能
-
業務邏輯 層面的封裝,即:api
- 入參:公共部分header、params的參數處理,避免在具體接口請求是傳入沒必要要與接口無關的參數
- 出參:對後臺返回的數據按約定好的規則作一層基礎解析處理,避免在具體接口數據解析的時候作一些無心義的操做
從投入的時間上來看: 第一部分基本上要花掉開發者80%以上的時間來封裝 第二部分須要消耗的時間可能不足20% 【以此推算,按上面1個小時的封裝時間,用在邏輯封裝部分的時間也就12分鐘左右😝】服務器
咱們再回頭看一下,第一部分的【數據交換】封裝是否涉及到具體業務邏輯呢?答案是:沒有。數據結構
既然沒有咱們爲何不把第一部分的封裝交給第三方框架呢,咱們只須要作第二部分的封裝多省事,有這樣第三方框架麼?app
答案是:有的,react-native-easy-app 就能夠實現【先後臺數據交換】層面的封裝,經過這個開源庫,咱們就只須要實現涉及【App業務邏輯】層面的封裝便可。
爲驗證 react-native-easy-app 的實用性,在這裏咱們先來構想一個業務邏輯層面封裝的需求:
-
請求接口的公共headers參數有:
- version、channelCode、model、platform (全部接口)
- accessToken、refreshToken、customerId (登陸後額外增長)
-
請求接口的公共params參數有:
- customerId (登陸後額外增長)
-
後臺返回的數據結構示例以下: { data: {}, successful:1, msg: 'request msg', code: 'xxx'}
-
請求狀態碼爲503的時候表示accessToken過時,accessToken過時的狀況下,須要從新獲取新的accessToken並刷新因accessToken過時致使請求失敗的接口
-
accessToken、refreshToken在登陸成功後的response的headers中返回。
對於以上業務邏輯層面的需求,看看經過 react-native-easy-app 咱們能夠怎麼作。
XHttpConfig().initHttpLogOn(true) .initHeaderSetFunc((headers) => { headers['model'] = 'xiao mi'; headers['version'] = '1.0.0'; headers['platform'] = Platform.OS; headers['channelCode'] = 'channelOfficial'; if (isLogin()) { headers['customerId'] = RNStorage.customerId; headers['accessToken'] = RNStorage.accessToken; headers['refreshToken'] = RNStorage.refreshToken; } }) .initParamSetFunc(params => { if (isLogin()) { params['customerId'] = RNStorage.customerId; } }) .initParseDataFunc((result, request, callback) => { let {success, json, message, status, response} = result; if (status === 503) {// accessToken過時標記 this.refreshToken(request, callback); } else { let {data, successful, msg, code} = json; callback(success && successful === 1, data || {}, msg || message, code, response); } });
accessToken從新請求的實現及對失敗接口的刷新:
refreshToken = (request, callback) => { if (global.hasQueryToken) { global.tokenExpiredList.push({request, callback}); } else { global.hasQueryToken = true; global.tokenExpiredList = [{request, callback}]; const refreshUrl = `${RNStorage.baseUrl}api/refreshToken?refreshToken=${RNStorage.refreshToken}`; fetch(refreshUrl).then(resp => { resp.json().then(({successful, data: {accessToken}}) => { if (successful === 1) {// 獲取到新的accessToken RNStorage.accessToken = accessToken; global.tokenExpiredList.map(({request, callback}) => { request.resendRequest(request, callback); }); global.tokenExpiredList = []; } else { console.log('Token 過時,退出登陸'); } }); }).catch(err => { console.log('Token 過時,退出登陸'); }).finally(() => { global.hasQueryToken = false; }); } };
就這樣對當前構想的app的邏輯層面的封裝就實現了**(實現上面的代碼約70行,也許要超過20分鐘 😆😝,但相較於之前從零開的封裝,是否是節約了大量的時間呢?)**是否是清晰明瞭。固然,這只是代碼片斷,沒有實際操做,就沒辦法證實上面的代碼實現是實際有效的。
爲了演示,先用 react native init HttpTestDemo
建立一個RN項目:示例項目:HttpTestDemo 修改並刪除沒必要要的佈局或資源,結果以下:
假定有三個接口,分別爲 api/login、api/userInfo 、api/refreshToken (爲了省事,接口都以json文件替代)
- api/login 有兩個必傳參數:[userName、userPass];請求內容類型爲:application/x-www-form-urlencoded;post請求
- api/userInfo 無參數;請求內容類型爲:application/json;get請求
- api/refreshToken 必須參數refreshToken;請求內容類型爲:application/json;get請求
https://react-native-easy-app.oss-cn-beijing.aliyuncs.com/api/login https://react-native-easy-app.oss-cn-beijing.aliyuncs.com/api/userInfo https://react-native-easy-app.oss-cn-beijing.aliyuncs.com/api/refreshToken
-
按 react-native-easy-app 的說明文檔,安裝庫:
npm install react-native-easy-app --save
-
定義一個持久化對象,用於保存accessToken,customerId等參數:
export const RNStorage = {// 持久化數據列表 customerId: undefined,//客戶ID accessToken: undefined,//OAuth2.0 accessToken refreshToken: undefined,//OAuth2.0 refreshToken baseUrl: undefined, userInfo: undefined, hasLogin: false, };
3.在頁面的構造方法時調用 RNStorage的初始化操做;初始化完成以後,調用Http請求RFHttpConfig的【業務邏輯】層初始化方法,這樣就完成了,如今就能夠調用接口了。
- 調用登陸接口:(因爲使用json文件的形式只能使用get請求)
import { RFHttp } from 'react-native-easy-app'; login = () => { let params = {userName: 'zhangsan', userPass: '123456a'}; RFHttp().url('api/login').param(params).formEncoded().get((success, json, message, status, resonse) => { if (success) { if (resonse.headers && resonse.headers.map) { RNStorage.accessToken = resonse.headers.map['x-oss-meta-accesstoken']; RNStorage.refreshToken = resonse.headers.map['x-oss-meta-refreshtoken']; } RNStorage.customerId = json.customerId; RNStorage.hasLogin = true; this.setState({data: JSON.stringify(json)}); } else { console.log('失敗', message); } }); };
調用接口,經過框架自帶的日誌功能,能夠看到,該拼的參數都拼接了,從header中也獲取到了token
- 調用獲取用戶我的信息接口:
import { RFHttp } from 'react-native-easy-app'; queryUserInfo = () => { RFHttp().url('api/userInfo').formJson().get((success, json, message) => { if (success) { RNStorage.userInfo = json; this.setState({data: JSON.stringify(json)}); } else { console.log('失敗', message); } }); };
調用接口,經過框架自帶的日誌功能,能夠看到accessToken、refreshToken也正確的拼接了。
因爲沒有合適的服務器,token過時的狀況就不演示了,只要請求refreshToken的接口正常請求就不會有問題。
至此一個完整的App 【業務邏輯】層面的封裝就徹底實現了,從Http請求的配置到,refreshToken的從新請求到刷新失敗接口,一共大概只用了70行代碼左右,是否是相較於以前從零開始的fetch封裝簡單容易多了,節約了大量的封裝時間呢?
擔憂框架的靈活性?請參考 react-native-easy-app 詳解與使用之(二) fetch 而且react-native-easy-app 開源庫並不僅有Http請求的封裝,還有更多功能,有興趣的同窗能夠查看此欄目的其它文章,你確定會有更多收穫。
當前示例項目連接:HttpTestDemo
若是有任何疑問,歡迎掃碼加入RN技術QQ交流羣