轉載
JSON傳值法則
- 只能使用String來傳遞,用LONG,BOOLEAN,INT等數據類型解析會有偏差,切記!!!!
基本設計思路
- 接口採用類RPC的風格,讓客戶端只有一個服務器入口地址,便於維護
- 服務端通過統一入口進來,根據請求的命令名載入不同的命令實現模塊,完成命令處理和返回。
- 支持chunk encoding
HTTP請求的寫法
- 請求支持POST方法。
- Content-Type必須設置爲application/x-www-form-urlencoded
- 請求數據格式json=<json數據>,數據是UTF-8編碼的
實際上就是標準的HTTP FORM POST寫法。
下面是一個簡單的例子:
POST /r/index.php HTTP/1.1 Host: www.gypsii.com.cn Connection: keep-alive Content-type: application/x-www-form-urlencoded Content-Length: 349 json={......}
標準寫法:
POST /r/index.php HTTP/1.1 Host: www.gypsii.com.cn Connection: keep-alive Content-type: application/x-www-form-urlencoded Content-Length: 349 key1=value1&key2=value2
通用數據報文格式
- 所有的鍵值,請求的命令都用小寫,單詞之間用"_"來分割。例如:place_detail
- cmd命令由"a-z","A-Z","_",其他字符爲非法字符,服務器不接受。這樣做是爲了避免HACK行爲。
REQUEST
最外層數據報文如下:
{ "cmd":<module_sub_module_..._command>, "id": <request id, optional>, "sid": <session ID, optional>, "cc": <cache timeout, optional>, "headers" <client headers>, "data": <interface specific data structure, optional> } *cmd: 必填項, 請求命令。 *id: 可選項,請求ID,如果請求中包含ID,則返回的RESPONSE中也會包含這個ID。這個是給客戶端更靈活的REQ/RSP處理。 *sid: 可選項,服務器SESSION ID。成功登錄後,服務器會返回一個session ID,此後的請求都應該加上這個session ID. *cc: 可選項,瀏覽器緩存時間設置,單位秒。如果不填,則服務器會在HTTP RESPONSE HEADER中加入默認的緩存設置,現在是1分鐘。如果設置爲0則表示瀏覽器不緩存。 *headers:必填項,客戶端信息,用來生成XMLRPC的請求頭。有關XMLRPC的請求頭信息,參見[[打包格式和升級UA規範|User Agent規範]] *data:請求的數據體,可選項 headers的結構: { "ua":<user agent>, "lang": <client language locale, en-us/zh-chs, etc>, } 例子: { "cmd": "places_detail", "id": "93dkkakxiu3kaaidi3", "headers" {"ua":"J2ME/2.0.0.0 (customerid=UNI_01, variant=UNI_01_Java_en-us_01, devicetype=s40_nontouch)", "lang":"en-us"}, "data": {"id":1111111111} }
注意:"module_sub_module_..._command"用來對服務器上的請求進行分類。邏輯如下:通過分隔符"_"將模塊,子模塊,和命令分開。最後一個是命令,前面的都是模塊名稱。在服務器上腳本文件的存放目錄和文件名就按照這個約定來存放。
例如: "cmd":"places_detail"表示在服務器的JSON根目錄下有如下目錄和文件來處理該命令/hb/req/places/detail.php
RESPONSE
最外層的數據報文如下:
{ "rsp": <1=SUCCESS/0=FAILUARE>, "cmd":<cmd in request> "data": <inteface specific data structure> "id": <corresponding request id, optional> "msg": <message will be prompted at client, optional>, } *id: 對應的請求ID,可選項,如果請求中包含ID,則返回的RESPONSE中也會包含這個ID。這個是給客戶端更靈活的REQ/RSP處理 *cmd:對應的請求命令 *data:該對象必須爲一個JSON對象表達。如果是數組,則必須爲:"data":{"array":[...]} *msg: 用來在客戶端顯示的消息,如果該字段不存在,或者爲內容爲空("" or "null"),則不顯示 例如: { "rsp": 1, "data": {"name": "...", "creation_time": "...", "thumbnail", "..."} }
支持帶進度顯示的文件上傳
由於上傳進度條是由服務器處理的,爲了支持負載均衡,服務器上傳服務器將會是單獨的服務器和地址,並共同COOKIE的處理來保證獲取進度條的正確性對於文件上傳請求和進度條請求,有如下特殊處理:
- 上傳服務器的地址是單獨的;
- 客戶端程序啓動後,異步先向該服務器發送一個進度條的測試請求,如果HTTP RESPONSE的頭裏面有Set-Cookie,則在之後上傳文件和請求進度的請求中將之前得到的cookie中的內容複製到HTTP REQUEST頭中的Cookie字段,這樣負載均衡器才能將上傳文件請求和進度條請求發送到同一臺WEB服務器上;
- 如果測試請求的RESPONSE中沒有Set-Cookie,則不能向服務器發送進度條請求,否則返回的結果很可能是錯的。
- 如果是CMWAP情況下,也不能向服務器發送進度條請求,因爲圖片是首先上傳到運營商的代理WAP網關,然後網關發送到我們的服務器,這樣,進度條請求的返回是不準確的。大部分上傳時間都耗在從客戶端到WAP網關那裏,但是由於服務器還沒有收到圖片,所以進度條請求返回的總是0.
模塊代碼的寫法
預置變量
每個JSON請求處理模塊被調用時,下面的變量已經可以使用
- $gypsii_user_id:當前用戶USER_ID,如果沒有找到,則爲;
- $gypsii_token:當前用戶的XMLRPC TOKEN,如果沒有找到,則爲;
- $json:該變量爲一個數組,表示客戶端傳入的JSON對象。
- $json_rsp:該變量爲一個數組,執行框架會最後將這個數組轉爲JSON格式輸出。在第一次訪問該變量時,該變量已經具有了一個默認元素"rsp"=>"1"
- $client_headers: 該變量爲一個數組,表示通過客戶端傳過來的headers生成的XMLRPC請求頭。在後續調用XMLRPC請求時會用到。
- $media: 如果上傳的是一個附帶二進制的請求,則該變量爲二維數組,保存需要上傳的二進制文件在服務器的路徑。
- type: MIME type image/jpeg, etc
- file: 上傳文件在服務器的絕對路徑,框架會在本次請求結束時會刪除掉上傳的臨時文件。
如何返回自定義錯誤
在/includes/config.inc.php中有一個環境變量GYPSII_PRODUCTION,當設置爲FALSE,表示爲開發模式,否則爲生產模式。
在程序中如果需要產生一個自定義錯誤,則調用如下:
output_json_error(你的資源串定義);
框架會自動捕獲錯誤,並返回一個JSON response到客戶端。
當GYPSII_PRODUCTION=FALSE時,會返回詳細的錯誤信息,例如產生錯誤的文件名,行號等等。
當GYPSII_PRODUCTION=TRUE是,則只返回提示消息
注意:模塊代碼內不能調用exit()
response的輸出是框架通過ob buffer最後統一發給客戶端的。所以如果中途退出,框架是不知道的,所以不會有任何內容返回給客戶端,客戶端就會獲得一個空白頁面。
如果模塊內部要要終止程序的執行,只要調用output_json_error("<錯誤提示信息>"),就會終止當前模塊的執行並返回一個包含該錯誤提示信息的JSON RESPONSE到客戶端。
如何定義自定義返回提示消息或者錯誤
如果需要返回自定義信息或者錯誤,要考慮到多語言化境。 /hb/req/res/<locale>/lang_res_req.inc.php是用來存放資源串的地方。由於JSON的資源串不會很多,因此就用一個文件來定義了。
獲取DEFAULTTOKEN
如果需要沒登錄前用一些需要TOKEN的接口,可以在JSON的data中加入"defaulttoken":"true"來獲取默認的TOKEN
例如 {
"cmd":<module_sub_module_..._command>, "id": <request id, optional>, "sid": <session ID, optional>, "cc": <cache timeout, optional>, "headers" <client headers>, "data":{"defaulttoken":"true",.....} <interface specific data structure, optional>
}
如何上傳附帶二進制內容的數據
爲了支持二進制內容上傳進度顯示,上傳的格式有特殊規定,參見add place
樣例代碼
[樣例代碼請點擊這裏 /backbone/trunk/source/hb/r/test/foo.php]
樣例模塊寫法
==Place== 這裏說明該模塊是幹什麼的 ===子模塊=== 如果有子模塊,這裏說明該子模塊是幹什麼的 ===detail=== place detail方法,用來獲取一個place的詳情 ====輸入報文說明==== 詳細說明 *有哪些參數,幹什麼的 *哪些參數是可選的 ====輸出報文說明==== 詳細說明 *有哪些參數,幹什麼的 *哪些參數是可選的
解析JSON對象樣例
由請求得到的JSON對象字符串如下 {"rsp":1, "data":{"token":"5d0e12ca502cc1bd4a356b986d70235e929adc3d", "DISPLAY_NAME":"Krelian", "LONGITUDE":"121.4325389", "STATUS":"呃~~~~~", "USER_ID":"1511237", "URL":"http://www.gypsii.com.cn/software/J2ME/gypsii/gypsii~non_touch~2.1.0.0~zh-chs.jad", "THUMBNAIL_URL":"http://www.gypsii.com.cn/icache/attachments/v237/E98CF9005FAF11DEB076B7CC7F68675D/account/icon_128.png?t=1256872626", "LATITUDE":"31.1904683125", "SIZE":"596067", "FROM_GYPSII":"TRUE", "REL_DATE":"2009-11-11", "VERSION":"2.1.0.0"} } 解析代碼如下: ==信息頭解析== String response = (JSONObject) json.getString("rsp"); JSON對象get能獲取不同的數據類型,可以直接轉變爲所需數據,如json.getBoolean、json.getDouble... ==信息數據解析== 把data轉換爲JSONObject JSONObject userinfo = (JSONObject)json.get("data"); 取值 userinfo.getString("DISPLAY_NAME") userinfo.getBoolean("FROM_GYPSII") userinfo.getDouble("LATITUDE") userinfo.getString("THUMBNAIL_URL") ... ==數據爲數組的格式解析== 獲取類似LIST數據時返回的是一個JSON數組,需要轉變爲JSON數組進行解析 數據格式如下: "data":{"array": [{"CREATION_TIME":"1258698303","ID":"54043195530598589","DISTANCE":"0","THUMBNAIL_URL":"http://www.gypsii.com.cn/icache/attachm ents/v937/A1A6A888D59911DEB42CDB0BC5DC35EC/2152637/5519462_thumb_48.jpg","NAME":"郝茨","USER_NAME":"楊靜"}, {"CREATION_TIME":"1258698278","ID":"54043195530598574","DISTANCE":"0","THUMBNAIL_URL":"http://www.gypsii.com.cn/icache/attachme nts/v277/ED38631AD59C11DEA3A7A453A9542C65/2152622/5519432_thumb_48.jpg","NAME":"nokla7210","USER_NAME":"大白"}, {"CREATION_TIME":"1258698112","ID":"54043195530598559","DISTANCE":"0","THUMBNAIL_URL":"http://www.gypsii.com.cn/icache/attachme nts/v237/DDA6726ED59911DE8584D4473DED2034/2152607/5519402_thumb_48.jpg","NAME":"韓添秀","USER_NAME":"愛"}, {"CREATION_TIME":"1258698087","ID":"54043195530598544","DISTANCE":"0","THUMBNAIL_URL":"http://www.gypsii.com.cn/icache/attachme nts/v872/27629780D58011DE8610D27AABCD66FA/2152592/5519372_thumb_48.jpg","NAME":"luoguzhi77","USER_NAME":"美麗的"}, {"CREATION_TIME":"1258698027","ID":"54043195530598529","DISTANCE":"0","THUMBNAIL_URL":"http://www.gypsii.com.cn/icache/attachme nts/v122/3EDAA192D57F11DEB6D5910E9F68860B/2152577/5519342_thumb_48.jpg","NAME":"yuduanchan","USER_NAME":"fj"}, ..... 解析方式爲 JSONArray placelist = (JSONArray)json.getJSONObject("data").getJSONArray("array"); 數據長度爲placelist.length() 獲取數據爲 placelist.getJSONObject(index).getString("ID")