平臺(tái)技術(shù)-API調(diào)用協(xié)議
開放平臺(tái)(TOP)的API是基于HTTP協(xié)議來調(diào)用的,開發(fā)者(ISV)可以直接使用TOP提供的官方SDK(支持多種語言,包含了請(qǐng)求的封裝,簽名加密,響應(yīng)解釋,性能優(yōu)化等)來調(diào)用,也可以根據(jù)TOP的協(xié)議來封裝HTTP請(qǐng)求進(jìn)行調(diào)用,以下主要是針對(duì)自行封裝HTTP請(qǐng)求進(jìn)行API調(diào)用的原理進(jìn)行詳細(xì)解說。
調(diào)用流程
根據(jù)TOP的協(xié)議:填充參數(shù) > 生成簽名 > 拼裝HTTP請(qǐng)求 > 發(fā)起HTTP請(qǐng)求> 得到HTTP響應(yīng) > 解釋json/xml結(jié)果,以下是大體的調(diào)用過程示意圖:
調(diào)用入口
調(diào)用API的服務(wù)URL地址,開放平臺(tái)目前提供了4個(gè)環(huán)境給ISV使用:沙箱測(cè)試環(huán)境,正式測(cè)試環(huán)境,正式環(huán)境,海外環(huán)境。
- 沙箱測(cè)試環(huán)境: ISV軟件的測(cè)試環(huán)境,應(yīng)用創(chuàng)建后即可使用。此環(huán)境提供簡(jiǎn)化版的淘寶網(wǎng),支持大部分場(chǎng)景的API調(diào)用,沙箱環(huán)境的權(quán)限和流量均無限制,可放開使用。
- 正式測(cè)試環(huán)境: ISV軟件上線之前的正式模擬環(huán)境,應(yīng)用創(chuàng)建成功后即可使用。此環(huán)境主要是針對(duì)部分無法在沙箱完成測(cè)試的場(chǎng)景使用,限制API調(diào)用為5000次/天,授權(quán)用戶數(shù)量為5個(gè),所能調(diào)用的API與應(yīng)用擁有的權(quán)限能力一致。
- 正式環(huán)境: ISV軟件上線之后使用的環(huán)境,此環(huán)境的入口與正式測(cè)試環(huán)境一致,只不過應(yīng)用上線之后,流量限制會(huì)進(jìn)行打開,具體流量限制與應(yīng)用所屬類目有關(guān),比如服務(wù)市場(chǎng)類的應(yīng)用,限制API調(diào)用為100萬次/天。
- 海外環(huán)境: 海外環(huán)境也屬于正式環(huán)境的一種,主要是給海外(歐美國家)ISV使用,對(duì)于海外的ISV,使用海外環(huán)境會(huì)比國內(nèi)環(huán)境的性能高一倍。
調(diào)用環(huán)境 | 服務(wù)地址(HTTP) | 服務(wù)地址(HTTPS) |
---|---|---|
沙箱環(huán)境 | http://gw.api.tbsandbox.com/router/rest | https://gw.api.tbsandbox.com/router/rest |
正式環(huán)境 | http://gw.api.taobao.com/router/rest | https://eco.taobao.com/router/rest |
海外環(huán)境 | http://api.taobao.com/router/rest | https://api.taobao.com/router/rest |
公共參數(shù)
調(diào)用任何一個(gè)API都必須傳入的參數(shù),目前支持的公共參數(shù)有:
參數(shù)名稱 | 參數(shù)類型 | 是否必須 | 參數(shù)描述 |
---|---|---|---|
method | String | 是 | API接口名稱。 |
app_key | String | 是 | TOP分配給應(yīng)用的AppKey。這里要注意正式環(huán)境和沙箱環(huán)境的AppKey是不同的(包括AppSecret),使用時(shí)要注意區(qū)分;進(jìn)入開放平臺(tái)控制臺(tái)“應(yīng)用管理-概覽” 和 “應(yīng)用管理-沙箱環(huán)境管理”可分別查看正式環(huán)境及沙箱環(huán)境的AppKey、AppSecret |
session | String | 否 | 用戶登錄授權(quán)成功后,TOP頒發(fā)給應(yīng)用的授權(quán)信息,詳細(xì)介紹請(qǐng)點(diǎn)擊這里。當(dāng)此API文檔的標(biāo)簽上注明:“需要授權(quán)”,則此參數(shù)必傳;“不需要授權(quán)”,則此參數(shù)不需要傳;“可選授權(quán)”,則此參數(shù)為可選。 |
timestamp | String | 是 | 時(shí)間戳,格式為yyyy-MM-dd HH:mm:ss,時(shí)區(qū)為GMT+8,例如:2016-01-01 12:00:00。淘寶API服務(wù)端允許客戶端請(qǐng)求最大時(shí)間誤差為10分鐘。 |
format | String | 否 | 響應(yīng)格式。默認(rèn)為xml格式,可選值:xml,json。 |
v | String | 是 | API協(xié)議版本,可選值:2.0。 |
partner_id | String | 否 | 合作伙伴身份標(biāo)識(shí)。 |
target_app_key | String | 否 | 被調(diào)用的目標(biāo)AppKey,僅當(dāng)被調(diào)用的API為第三方ISV提供時(shí)有效。 |
simplify | Boolean | 否 | 是否采用精簡(jiǎn)JSON返回格式,僅當(dāng)format=json時(shí)有效,默認(rèn)值為:false。 |
sign_method | String | 是 | 簽名的摘要算法,可選值為:hmac,md5。 |
sign | String | 是 | API輸入?yún)?shù)簽名結(jié)果,簽名算法參照下面的介紹。 |
業(yè)務(wù)參數(shù)
API調(diào)用除了必須包含公共參數(shù)外,如果API本身有業(yè)務(wù)級(jí)的參數(shù)也必須傳入,每個(gè)API的業(yè)務(wù)級(jí)參數(shù)請(qǐng)考API文檔說明。
簽名算法
為了防止API調(diào)用過程中被黑客惡意篡改,調(diào)用任何一個(gè)API都需要攜帶簽名,TOP服務(wù)端會(huì)根據(jù)請(qǐng)求參數(shù),對(duì)簽名進(jìn)行驗(yàn)證,簽名不合法的請(qǐng)求將會(huì)被拒絕。TOP目前支持的簽名算法有兩種:MD5(sign_method=md5),HMAC_MD5(sign_method=hmac),簽名大體過程如下:
- 對(duì)所有API請(qǐng)求參數(shù)(包括公共參數(shù)和業(yè)務(wù)參數(shù),但除去sign參數(shù)和byte[]類型的參數(shù)),根據(jù)參數(shù)名稱的ASCII碼表的順序排序。如:foo=1, bar=2, foo_bar=3, foobar=4排序后的順序是bar=2, foo=1, foo_bar=3, foobar=4。
- 將排序好的參數(shù)名和參數(shù)值拼裝在一起,根據(jù)上面的示例得到的結(jié)果為:bar2foo1foo_bar3foobar4。
- 把拼裝好的字符串采用utf-8編碼,使用簽名算法對(duì)編碼后的字節(jié)流進(jìn)行摘要。如果使用MD5算法,則需要在拼裝的字符串前后加上app的secret后,再進(jìn)行摘要,如:md5(secret+bar2foo1foo_bar3foobar4+secret);如果使用HMAC_MD5算法,則需要用app的secret初始化摘要算法后,再進(jìn)行摘要,如:hmac_md5(bar2foo1foo_bar3foobar4)。
- 將摘要得到的字節(jié)流結(jié)果使用十六進(jìn)制表示,如:hex(“helloworld”.getBytes(“utf-8”)) = “68656C6C6F776F726C64”
說明:MD5和HMAC_MD5都是128位長度的摘要算法,用16進(jìn)制表示,一個(gè)十六進(jìn)制的字符能表示4個(gè)位,所以簽名后的字符串長度固定為32個(gè)十六進(jìn)制字符。
JAVA簽名示例代碼
// 第一步:檢查參數(shù)是否已經(jīng)排序
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
// 第二步:把所有參數(shù)名和參數(shù)值串在一起
StringBuilder query = new StringBuilder();
if (Constants.SIGN_METHOD_MD5.equals(signMethod)) {
query.append(secret);
}
for (String key : keys) {
String value = params.get(key);
if (StringUtils.areNotEmpty(key, value)) {
query.append(key).append(value);
}
}
// 第三步:使用MD5/HMAC加密
byte[] bytes;
if (Constants.SIGN_METHOD_HMAC.equals(signMethod)) {
bytes = encryptHMAC(query.toString(), secret);
} else {
query.append(secret);
bytes = encryptMD5(query.toString());
}
// 第四步:把二進(jìn)制轉(zhuǎn)化為大寫的十六進(jìn)制
return byte2hex(bytes);
}
public static byte[] encryptHMAC(String data, String secret) throws IOException {
byte[] bytes = null;
try {
SecretKey secretKey = new SecretKeySpec(secret.getBytes(Constants.CHARSET_UTF8), "HmacMD5");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
bytes = mac.doFinal(data.getBytes(Constants.CHARSET_UTF8));
} catch (GeneralSecurityException gse) {
throw new IOException(gse.toString());
}
return bytes;
}
public static byte[] encryptMD5(String data) throws IOException {
return encryptMD5(data.getBytes(Constants.CHARSET_UTF8));
}
public static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toUpperCase());
}
return sign.toString();
}
C#簽名示例代碼
{
// 第一步:把字典按Key的字母順序排序
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters, StringComparer.Ordinal);
IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
// 第二步:把所有參數(shù)名和參數(shù)值串在一起
StringBuilder query = new StringBuilder();
if (Constants.SIGN_METHOD_MD5.Equals(signMethod))
{
query.Append(secret);
}
while (dem.MoveNext())
{
string key = dem.Current.Key;
string value = dem.Current.Value;
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
query.Append(key).Append(value);
}
}
// 第三步:使用MD5/HMAC加密
byte[] bytes;
if (Constants.SIGN_METHOD_HMAC.Equals(signMethod))
{
HMACMD5 hmac = new HMACMD5(Encoding.UTF8.GetBytes(secret));
bytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(query.ToString()));
}
else
{
query.Append(secret);
MD5 md5 = MD5.Create();
bytes = md5.ComputeHash(Encoding.UTF8.GetBytes(query.ToString()));
}
// 第四步:把二進(jìn)制轉(zhuǎn)化為大寫的十六進(jìn)制
StringBuilder result = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
result.Append(bytes[i].ToString("X2"));
}
return result.ToString();
}
其他語言簽名示例代碼請(qǐng)參見TOP官方SDK源代碼。
調(diào)用示例
以taobao.item.seller.get調(diào)用為例,具體步驟如下:
1. 設(shè)置參數(shù)值
公共參數(shù):
- method = “taobao.item.seller.get”
- app_key = “12345678”
- session = “test”
- timestamp = “2016-01-01 12:00:00”
- format = “json”
- v = “2.0”
- sign_method = “md5”
業(yè)務(wù)參數(shù):
- fields = “num_iid,title,nick,price,num”
- num_iid = 11223344
2. 按ASCII順序排序
- app_key = “12345678”
- fields = “num_iid,title,nick,price,num”
- format = “json”
- method = “taobao.item.seller.get”
- num_iid = 11223344
- session = “test”
- sign_method = “md5”
- timestamp = “2016-01-01 12:00:00”
- v = “2.0”
3. 拼接參數(shù)名與參數(shù)值
4. 生成簽名
假設(shè)app的secret為helloworld,則簽名結(jié)果為:hex(md5(helloworld+按順序拼接好的參數(shù)名與參數(shù)值+helloworld)) = “66987CB115214E59E6EC978214934FB8”
5. 組裝HTTP請(qǐng)求
將所有參數(shù)名和參數(shù)值采用utf-8進(jìn)行URL編碼(參數(shù)順序可隨意,但必須要包括簽名參數(shù)),然后通過GET或POST(含byte[]類型參數(shù))發(fā)起請(qǐng)求,如:
注意事項(xiàng)
- 所有的請(qǐng)求和響應(yīng)數(shù)據(jù)編碼皆為utf-8格式,URL里的所有參數(shù)名和參數(shù)值請(qǐng)做URL編碼。如果請(qǐng)求的Content-Type是application/x-www-form-urlencoded,則HTTP Body體里的所有參數(shù)值也做URL編碼;如果是multipart/form-data格式,每個(gè)表單字段的參數(shù)值無需編碼,但每個(gè)表單字段的charset部分需要指定為utf-8。
- 參數(shù)名與參數(shù)值拼裝起來的URL長度小于1024個(gè)字符時(shí),可以用GET發(fā)起請(qǐng)求;參數(shù)類型含byte[]類型或拼裝好的請(qǐng)求URL過長時(shí),必須用POST發(fā)起請(qǐng)求。所有API都可以用POST發(fā)起請(qǐng)求。
- 如需需要在沙箱環(huán)境測(cè)試,請(qǐng)?jiān)趹?yīng)用控制臺(tái)的沙箱管理頁面獲取沙箱環(huán)境對(duì)應(yīng)的app_key(一般為正式環(huán)境的app_key前面加上“10”)和app_secret,對(duì)應(yīng)的session值也用沙箱帳號(hào)登錄授權(quán)獲得,沙箱環(huán)境授權(quán)和正式環(huán)境授權(quán)類似,詳細(xì)可參考用戶授權(quán)介紹。
- 生成簽名(sign)僅對(duì)未使用TOP官方SDK進(jìn)行API調(diào)用時(shí)需要操作,如使用了TOP官方SDK,該步驟SDK會(huì)自動(dòng)完成。
FAQ
接口調(diào)用報(bào)簽名錯(cuò)誤“ Invalid signature”