json 基本原理

轉載

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")


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章