言調(diào)用短信API完整指南:從HTTP請(qǐng)求到簽名鑒權(quán)實(shí)戰(zhàn))
做易語(yǔ)言開(kāi)發(fā)的人遲早會(huì)遇到這個(gè)需求給程序加短信通知。注冊(cè)驗(yàn)證碼、訂單提醒、腳本執(zhí)行異常告警都需要在程序里直接調(diào)用短信接口。但“易語(yǔ)言調(diào)短信API”這個(gè)組合很容易讓新手卡殼——網(wǎng)上的示例要么是過(guò)時(shí)的控件調(diào)用要么是其他語(yǔ)言代碼讓你來(lái)回翻譯真正能在中文編程環(huán)境里完整跑通的Demo不多。這篇文章就把這套流程徹底拆開(kāi)從技術(shù)路線怎么選、簽名鑒權(quán)為什么繞不開(kāi)到可以直接復(fù)制的示例代碼、參數(shù)配置里的隱藏坑位、排錯(cuò)實(shí)戰(zhàn)一步步給你理清楚。如果你是第一次接觸短信API但已經(jīng)能把易語(yǔ)言的窗口程序跑起來(lái)那這篇就是為你寫(xiě)的??赐曛竽憧梢詫?duì)照自己的短信服務(wù)商改參數(shù)讓驗(yàn)證碼和通知真正發(fā)到用戶(hù)手機(jī)上。1. 為什么在易語(yǔ)言里接短信API先想清楚技術(shù)路線很多新手拿到需求后的第一反應(yīng)是去搜“易語(yǔ)言 短信接口”結(jié)果搜出來(lái)的方案五花八門(mén)有的要裝控件有的要引入額外DLL還有的干脆讓你用調(diào)試工具手工觸發(fā)。這些方案不是不能用但對(duì)長(zhǎng)期維護(hù)來(lái)說(shuō)都是隱患。先想清楚技術(shù)路線后面能省掉一大半麻煩。1.1 四種常見(jiàn)技術(shù)路線的對(duì)比我見(jiàn)過(guò)不少商用的易語(yǔ)言項(xiàng)目短信發(fā)送這塊基本跑不出下面四種路子技術(shù)路線實(shí)現(xiàn)方式優(yōu)點(diǎn)缺點(diǎn)直接調(diào)用HTTP接口用網(wǎng)頁(yè)訪問(wèn)對(duì)象或winhttp發(fā)HTTP請(qǐng)求無(wú)需額外DLL跨平臺(tái)兼容好任意服務(wù)商都支持需要自己處理簽名、解析返回?cái)?shù)據(jù)調(diào)用服務(wù)商提供的DLL在易語(yǔ)言里聲明DLL函數(shù)并調(diào)用封裝完整函數(shù)調(diào)用簡(jiǎn)單綁定Windows平臺(tái)DLL版本更新后要重新適配用中間層腳本中轉(zhuǎn)易語(yǔ)言請(qǐng)求本地或遠(yuǎn)程腳本腳本負(fù)責(zé)調(diào)API易語(yǔ)言側(cè)最簡(jiǎn)單邏輯集中在腳本里多一層網(wǎng)絡(luò)開(kāi)銷(xiāo)多一個(gè)故障點(diǎn)調(diào)試工具手工觸發(fā)通過(guò)按鍵模擬或命令行調(diào)用適合臨時(shí)測(cè)試完全不適合正式環(huán)境無(wú)法集成到業(yè)務(wù)流程我的建議非常明確優(yōu)先選擇直接調(diào)用HTTP接口。原因在于短信API本質(zhì)就是一個(gè)HTTP服務(wù)任何語(yǔ)言能做的事易語(yǔ)言用網(wǎng)頁(yè)訪問(wèn)對(duì)象一樣能做。這樣你的核心邏輯全部留在易語(yǔ)言項(xiàng)目里沒(méi)有DLL版本沖突也不依賴(lài)第三方腳本的存活狀態(tài)。1.2 為什么HTTP接口方式是首選這里要解釋一個(gè)容易被忽略的點(diǎn)短信服務(wù)商的API地址是標(biāo)準(zhǔn)的HTTP URL參數(shù)通過(guò)GET或POST傳遞返回結(jié)果是JSON或XML文本。易語(yǔ)言在這方面的能力被嚴(yán)重低估了——精易模塊里的“網(wǎng)頁(yè)_訪問(wèn)_對(duì)象”完全能滿(mǎn)足請(qǐng)求發(fā)送、協(xié)議頭設(shè)置、超時(shí)控制這些需求。另外從成本角度看絕大多數(shù)短信平臺(tái)都提供免費(fèi)測(cè)試簽名和測(cè)試模板你注冊(cè)之后拿到密鑰就能調(diào)。自己用DLL封裝反而會(huì)把簡(jiǎn)單事情搞復(fù)雜因?yàn)镈LL的調(diào)用約定、數(shù)據(jù)結(jié)構(gòu)、內(nèi)存釋放都要逐一適配。尤其是當(dāng)你后續(xù)想換短信服務(wù)商的時(shí)候HTTP接口只要改地址和參數(shù)即可DLL方案則要整個(gè)重寫(xiě)。接下來(lái)我們就按“HTTP接口”這條主線把底層的邏輯先講透再給完整代碼。2. 短信接口調(diào)用的底層邏輯搞懂這四個(gè)關(guān)鍵詞才算入門(mén)我見(jiàn)過(guò)太多人拿到示例代碼后一頓復(fù)制結(jié)果換了服務(wù)商就完全不知道怎么改。原因在于不理解HTTP請(qǐng)求的構(gòu)成、鑒權(quán)的意義、模板的機(jī)制和返回?cái)?shù)據(jù)的長(zhǎng)相。這四個(gè)關(guān)鍵詞弄明白任何平臺(tái)的短信API你都能快速上手。2.1 請(qǐng)求構(gòu)成URL、請(qǐng)求方法、協(xié)議頭一個(gè)短信HTTP請(qǐng)求本質(zhì)上就是發(fā)送一個(gè)URL請(qǐng)求。比如某平臺(tái)接口地址是http://api.example.com/sms/send這個(gè)地址后面跟的參數(shù)通常包括access_key_id你的密鑰ID服務(wù)商用它來(lái)識(shí)別哪個(gè)賬戶(hù)在調(diào)用phone目標(biāo)手機(jī)號(hào)sign_name簽名內(nèi)容就是短信開(kāi)頭“【某某】”里面的某某template_code模板編號(hào)服務(wù)商審核通過(guò)后分配template_param模板變量比如驗(yàn)證碼的值、用戶(hù)昵稱(chēng)timestamp當(dāng)前時(shí)間戳用于防止請(qǐng)求重放sign簽名值證明請(qǐng)求確實(shí)由你發(fā)出請(qǐng)求方法通常有GET和POST兩種。GET適合參數(shù)少、調(diào)試方便的場(chǎng)景POST適合參數(shù)多、內(nèi)容敏感的場(chǎng)景。易語(yǔ)言里網(wǎng)頁(yè)_訪問(wèn)_對(duì)象的第二參數(shù)設(shè)0就是GET設(shè)1就是POST。協(xié)議頭也很關(guān)鍵。服務(wù)商要求的內(nèi)容類(lèi)型通常是application/x-www-form-urlencoded或application/json; charsetutf-8。如果你用JSON格式提交必須在協(xié)議頭里明確寫(xiě)清楚否則服務(wù)商可能返回“參數(shù)格式錯(cuò)誤”。2.2 鑒權(quán)與簽名讓服務(wù)器相信是你發(fā)的你有沒(méi)有想過(guò)短信是能賺錢(qián)的東西如果接口不限量隨便打很容易被惡意外掛刷爆。所以服務(wù)商必須確認(rèn)“調(diào)用者是你”——這就靠簽名機(jī)制。簽名的大致邏輯是把所有請(qǐng)求參數(shù)和你的密鑰AccessSecret拼接成一個(gè)字符串然后用MD5或HMAC-SHA256算法生成一個(gè)摘要值。服務(wù)商收到請(qǐng)求后會(huì)用同樣的方式重新計(jì)算一次如果算出來(lái)的摘要和請(qǐng)求里的sign字段一致就認(rèn)為請(qǐng)求合法。舉例來(lái)說(shuō)明。假設(shè)參數(shù)是access_key_idLTAI123 phone13800138000 timestamp1700000000密鑰是mysecret。那么待簽名字符串就是按字典序拼接后的結(jié)果access_key_idLTAI123phone13800138000timestamp1700000000keymysecret然后對(duì)這個(gè)字符串取MD5得到一個(gè)小寫(xiě)32位字符串這就是sign。這里有個(gè)新手容易踩的大坑拼接時(shí)中文必須編碼后再參與簽名且要注意UTF-8與GBK的差異。短信簽名和模板變量里有中文時(shí)最好先轉(zhuǎn)成UTF-8字節(jié)集再取摘要否則本地算出來(lái)和服務(wù)器算出來(lái)的簽名永遠(yuǎn)對(duì)不上。2.3 三要素簽名內(nèi)容、模板、手機(jī)號(hào)短信平臺(tái)上有“簽名內(nèi)容、模板、手機(jī)號(hào)”三個(gè)概念必須區(qū)分清楚簽名內(nèi)容就是出現(xiàn)在短信開(kāi)頭的名稱(chēng)比如“【我的應(yīng)用】”里的“我的應(yīng)用”。它需要平臺(tái)審核一般1-2小時(shí)就能通過(guò)。模板短信正文的固定框架比如“您的驗(yàn)證碼是${code}5分鐘內(nèi)有效”。${code}就是變量調(diào)用時(shí)用template_param傳入。手機(jī)號(hào)接收方號(hào)碼國(guó)內(nèi)一般是11位帶不帶86前綴取決于平臺(tái)要求。很多人在參數(shù)配置階段反復(fù)報(bào)錯(cuò)就是沒(méi)搞懂“簽名內(nèi)容”和“模板”是兩回事。簽名是發(fā)送方標(biāo)識(shí)模板是正文結(jié)構(gòu)服務(wù)器會(huì)先去庫(kù)里匹配你提交的簽名和模板是否存在、是否通過(guò)審核。審核狀態(tài)不通過(guò)調(diào)多少次都是失敗。2.4 響應(yīng)結(jié)構(gòu)狀態(tài)碼、消息、請(qǐng)求ID所有短信API都會(huì)返回一個(gè)結(jié)構(gòu)化響應(yīng)。最常見(jiàn)的是JSON格式類(lèi)似{code: 200, msg: 發(fā)送成功, request_id: f0a1b2c3-d4e5-6789-abcd-ef0123456789}雖然不同平臺(tái)字段名稱(chēng)不同有叫status的有叫success的但本質(zhì)信息就三類(lèi)狀態(tài)碼成功或失敗的原因分類(lèi)消息描述給人看的具體說(shuō)明請(qǐng)求ID服務(wù)商給本次請(qǐng)求的唯一編號(hào)排查問(wèn)題時(shí)向客服提供這個(gè)ID可以快速定位特別提醒一點(diǎn)發(fā)起HTTP請(qǐng)求成功不代表短信發(fā)送成功。請(qǐng)求返回200HTTP層面的成功只是說(shuō)服務(wù)商接收到了你的請(qǐng)求真正的結(jié)果要看業(yè)務(wù)狀態(tài)碼。這個(gè)后面排錯(cuò)部分會(huì)展開(kāi)講。3. 完整Demo從空窗口到發(fā)出第一條短信基礎(chǔ)原理講完下面進(jìn)入實(shí)戰(zhàn)。這里我以某個(gè)主流短信平臺(tái)的接口格式為例參數(shù)含義是通用的你拿到自己平臺(tái)的文檔后只要替換地址和字段名即可。3.1 界面設(shè)計(jì)新建一個(gè)易語(yǔ)言窗口程序放以下組件編輯框_手機(jī)號(hào)用于輸入接收號(hào)碼編輯框_驗(yàn)證碼用于輸入驗(yàn)證碼內(nèi)容或任意模板變量按鈕_發(fā)送觸發(fā)發(fā)送動(dòng)作標(biāo)簽_提示顯示請(qǐng)求返回結(jié)果界面越簡(jiǎn)單越好核心邏輯在按鈕的事件里。3.2 HTTP訪問(wèn)能力準(zhǔn)備推薦下載最新版精易模塊把模塊引用到程序里。本篇代碼用到精易模塊的以下命令網(wǎng)頁(yè)_訪問(wèn)_對(duì)象發(fā)送HTTP請(qǐng)求編碼_URL編碼對(duì)中文參數(shù)做URL編碼時(shí)間_取Unix時(shí)間戳生成當(dāng)前時(shí)間戳編碼_Ansi到Utf8把中文簽名串轉(zhuǎn)成UTF-8字節(jié)集取數(shù)據(jù)摘要計(jì)算MD5值類(lèi)_json解析返回的JSON文本如果你的環(huán)境里沒(méi)有精易模塊也可以用易語(yǔ)言核心支持庫(kù)的“http讀文件”臨時(shí)頂一下但功能弱很多后續(xù)做POST、自定義頭會(huì)受限。正式項(xiàng)目還是建議用精易模塊。3.3 完整代碼下面是一份可用的參考代碼。注意訪問(wèn)密鑰ID、訪問(wèn)密鑰、短信簽名、模板編號(hào)這幾個(gè)變量請(qǐng)?zhí)鎿Q成你自己的.版本 2 .支持庫(kù) spec .程序集 窗口程序集_窗口1 .子程序 _按鈕_發(fā)送_被單擊 .局部變量 手機(jī)號(hào), 文本型 .局部變量 驗(yàn)證碼值, 文本型 .局部變量 模板參數(shù), 文本型 .局部變量 時(shí)間戳, 文本型 .局部變量 待簽名字符串, 文本型 .局部變量 簽名值, 文本型 .局部變量 請(qǐng)求地址, 文本型 .局部變量 返回文本, 文本型 讀取界面輸入 手機(jī)號(hào) 編輯框_手機(jī)號(hào).內(nèi)容 驗(yàn)證碼值 編輯框_驗(yàn)證碼.內(nèi)容 手機(jī)號(hào)基本校驗(yàn) 如果真 (取文本長(zhǎng)度 (手機(jī)號(hào)) ≠ 11) 標(biāo)簽_提示.標(biāo)題 “請(qǐng)輸入11位手機(jī)號(hào)” 返回 () 如果真結(jié)束 拼接模板變量JSON 模板參數(shù) “{\“code\”:\“” 驗(yàn)證碼值 “\”}” 生成時(shí)間戳 時(shí)間戳 時(shí)間_取Unix時(shí)間戳 () 拼接待簽名字符串注意參與簽名的參數(shù)順序和內(nèi)容 待簽名字符串 “access_key_id” “你的訪問(wèn)密鑰ID” 待簽名字符串 待簽名字符串 “phone” 手機(jī)號(hào) 待簽名字符串 待簽名字符串 “sign_name” “你的短信簽名” 待簽名字符串 待簽名字符串 “template_code” “你的模板編號(hào)” 待簽名字符串 待簽名字符串 “template_param” 模板參數(shù) 待簽名字符串 待簽名字符串 “×tamp” 時(shí)間戳 待簽名字符串 待簽名字符串 “key” “你的訪問(wèn)密鑰” 將待簽名字符串轉(zhuǎn)UTF-8再取MD5 簽名值 取數(shù)據(jù)摘要 (編碼_Ansi到Utf8 (到字節(jié)集 (待簽名字符串))) 拼請(qǐng)求地址中文簽名和模板參數(shù)要做URL編碼 請(qǐng)求地址 “http://api.example.com/sms/send” 請(qǐng)求地址 請(qǐng)求地址 “?access_key_id” “你的訪問(wèn)密鑰ID” 請(qǐng)求地址 請(qǐng)求地址 “phone” 手機(jī)號(hào) 請(qǐng)求地址 請(qǐng)求地址 “sign_name” 編碼_URL編碼 (“你的短信簽名”) 請(qǐng)求地址 請(qǐng)求地址 “template_code” “你的模板編號(hào)” 請(qǐng)求地址 請(qǐng)求地址 “template_param” 編碼_URL編碼 (模板參數(shù)) 請(qǐng)求地址 請(qǐng)求地址 “×tamp” 時(shí)間戳 請(qǐng)求地址 請(qǐng)求地址 “sign” 簽名值 發(fā)送GET請(qǐng)求超時(shí)10秒 返回文本 網(wǎng)頁(yè)_訪問(wèn)_對(duì)象 (請(qǐng)求地址, 0, , , , , 10000) 調(diào)試輸出原始返回 輸出調(diào)試文本 (返回文本) 調(diào)用解析子程序 解析返回結(jié)果 (返回文本)這里有個(gè)細(xì)節(jié)需要重點(diǎn)解釋為什么簽名拼接用的是中文原文而發(fā)送時(shí)卻要URL編碼因?yàn)楹灻?jì)算是服務(wù)商拿到“原始參數(shù)值”再計(jì)算的它并不知道你在客戶(hù)端做了編碼。而URL里又不能直接放中文所以發(fā)送前才做URL編碼。如果你在簽名時(shí)對(duì)中文先做了URL編碼那服務(wù)商同樣對(duì)原始值編碼后再計(jì)算兩邊就會(huì)不一致。保險(xiǎn)起見(jiàn)應(yīng)該以服務(wù)商文檔為準(zhǔn)本篇示例采用的是“簽名用原文、發(fā)送時(shí)編碼”的常見(jiàn)做法。3.4 JSON返回解析上一步代碼里的“解析返回結(jié)果”子程序可以用類(lèi)_json來(lái)完成。大致邏輯.子程序 解析返回結(jié)果 .參數(shù) 返回文本, 文本型 .局部變量 JSON, 類(lèi)_json .局部變量 狀態(tài)碼, 文本型 .局部變量 消息, 文本型 如果真 (返回文本 “”) 標(biāo)簽_提示.標(biāo)題 “網(wǎng)絡(luò)請(qǐng)求無(wú)返回” 返回 () 如果真結(jié)束 .如果真 (JSON.解析 (返回文本) 假) 標(biāo)簽_提示.標(biāo)題 “返回格式錯(cuò)誤” 返回 () .如果真結(jié)束 狀態(tài)碼 JSON.取屬性 (“code”).取數(shù)據(jù)文本 () 消息 JSON.取屬性 (“msg”).取數(shù)據(jù)文本 () 調(diào)試輸出 (“狀態(tài)碼”, 狀態(tài)碼) 調(diào)試輸出 (“消息”, 消息) 如果真 (狀態(tài)碼 “200”) 標(biāo)簽_提示.標(biāo)題 “發(fā)送成功” 返回 () 如果真結(jié)束 標(biāo)簽_提示.標(biāo)題 “發(fā)送失敗” 消息解析JSON這一步很多老項(xiàng)目會(huì)用手工字符串查找比如“尋找文本”那不是不好只是遇到返回結(jié)構(gòu)變動(dòng)時(shí)就容易出錯(cuò)。類(lèi)_json雖然也要先安裝模塊但它能按路徑取屬性代碼寫(xiě)起來(lái)更穩(wěn)。4. 參數(shù)配置里最容易踩的六個(gè)坑示例代碼跑通之后你會(huì)進(jìn)入一個(gè)更考驗(yàn)?zāi)托牡碾A段正式接入自己的參數(shù)。這個(gè)階段我總結(jié)了六個(gè)高頻坑每一個(gè)都是我實(shí)際見(jiàn)過(guò)或踩過(guò)的。4.1 中文不編碼接口各種報(bào)錯(cuò)短信簽名和模板變量里帶中文是必然的。如果你直接把中文拼在URL后面發(fā)過(guò)去輕則服務(wù)商返回“參數(shù)錯(cuò)誤”重則你的HTTP請(qǐng)求直接被網(wǎng)關(guān)攔截。解決方案就是示例代碼里的編碼_URL編碼()。但注意編碼前先要搞清楚參數(shù)在哪個(gè)環(huán)節(jié)被解碼否則可能出現(xiàn)“編碼一次多了百分號(hào)”或“編碼兩次全部亂掉”的詭異現(xiàn)象。排查方法很簡(jiǎn)單把最終請(qǐng)求地址復(fù)制到瀏覽器地址欄打開(kāi)看服務(wù)商返回的錯(cuò)誤信息。4.2 模板變量名對(duì)不上服務(wù)商的模板往往長(zhǎng)這樣您的驗(yàn)證碼為${code}5分鐘內(nèi)有效。那你的template_param就必須是{code:123456}很多新手會(huì)把變量名寫(xiě)成了“verifyCode”“authCode”之類(lèi)的跟模板里占位的${code}對(duì)不上結(jié)果返回錯(cuò)誤“變量值與模板不匹配”。還有一點(diǎn)JSON的鍵名一定要和模板占位符完全一致大小寫(xiě)也不能錯(cuò)。建議寫(xiě)代碼前先到平臺(tái)后臺(tái)復(fù)制一遍模板變量名。4.3 密鑰泄露與硬編碼問(wèn)題訪問(wèn)密鑰相當(dāng)于你短信賬戶(hù)的密碼。如果項(xiàng)目要發(fā)給別人密鑰寫(xiě)在源碼里被反編譯出來(lái)別人就能拿你的密鑰發(fā)短信費(fèi)用全算你頭上。雖然是示例代碼但正式項(xiàng)目里的密鑰建議盡量從配置文件讀取或者部署成加密后的常量。如果你做的是商用軟件更要仔細(xì)考慮這個(gè)點(diǎn)。4.4 發(fā)送頻率限制幾乎所有短信平臺(tái)都有頻率限制常見(jiàn)的規(guī)則包括同一手機(jī)號(hào)1分鐘內(nèi)最多1條、1天內(nèi)最多10條同一IP每秒最多1條。程序里如果寫(xiě)了循環(huán)又不做限速很容易觸發(fā)“觸發(fā)日發(fā)送量限制”或“觸發(fā)小時(shí)發(fā)送量限制”的錯(cuò)誤碼。我的習(xí)慣是發(fā)送前先檢查同號(hào)最近是否已發(fā)送過(guò)用簡(jiǎn)單的內(nèi)存表或?qū)憥?kù)記錄均可在高并發(fā)場(chǎng)景能幫你少踩很多坑。4.5 手機(jī)號(hào)格式校驗(yàn)這里不是指11位數(shù)字那么簡(jiǎn)單。國(guó)內(nèi)手機(jī)號(hào)第一位是1第二位通常是3、5、6、7、8、9。很多平臺(tái)接口層會(huì)做校驗(yàn)但也有平臺(tái)只按數(shù)字處理。你自己不做格式校驗(yàn)結(jié)果就是用戶(hù)填了個(gè)“12345”你發(fā)給平臺(tái)平臺(tái)返回“手機(jī)號(hào)無(wú)效”用戶(hù)體驗(yàn)極差。示例代碼里我做了長(zhǎng)度校驗(yàn)?zāi)氵€可以加上正則匹配。4.6 簽名和模板的審核狀態(tài)測(cè)試階段沒(méi)問(wèn)題換到生產(chǎn)環(huán)境反而失敗最常見(jiàn)的原因是測(cè)試簽名/測(cè)試模板和正式簽名/正式模板不是同一個(gè)。平臺(tái)允許你分別創(chuàng)建測(cè)試版和正式版測(cè)試版的發(fā)送量通常很小而且接收號(hào)碼有限制。所以正式項(xiàng)目上線前一定要檢查簽名內(nèi)容、模板編號(hào)是否已經(jīng)在平臺(tái)審核通過(guò)并且狀態(tài)是“可用”。5. 排錯(cuò)實(shí)戰(zhàn)收到錯(cuò)誤碼之后怎么辦短信接口報(bào)錯(cuò)很多新手的第一反應(yīng)是去百度錯(cuò)誤碼然后對(duì)著網(wǎng)上的舊答案瞎改。我的經(jīng)驗(yàn)是先不要管代碼先把請(qǐng)求本身搞清楚。5.1 先排查請(qǐng)求本身收到錯(cuò)誤后我最先做的是把發(fā)給服務(wù)商的請(qǐng)求原樣打印出來(lái)包括完整URL、協(xié)議頭、提交數(shù)據(jù)。為此我習(xí)慣在易語(yǔ)言里用一個(gè)日志子程序把每次請(qǐng)求的地址、生成的sign、返回文本追加寫(xiě)到文本文件里。這樣出了問(wèn)題看日志就能定位是“請(qǐng)求參數(shù)錯(cuò)了”還是“服務(wù)商拒絕了”。比如最典型的簽名錯(cuò)誤sign not match你去查代碼邏輯往往會(huì)糾結(jié)很久但看日志就明白了要么是待簽名字符串里包含的key不對(duì)要么是編碼方式變了。日志是排查這類(lèi)問(wèn)題最直接的鑰匙。5.2 常見(jiàn)錯(cuò)誤碼與處理下面這張表是我整理的高頻錯(cuò)誤碼不同平臺(tái)的具體code值可能不同但含義大同小異錯(cuò)誤碼/關(guān)鍵詞含義處理方式InvalidAccessKeyId密鑰ID不存在或已禁用檢查訪問(wèn)密鑰ID是否填對(duì)是否復(fù)制了多余空格SignatureNotMatch簽名不正確重新檢查待簽名字符串、密鑰、編碼方式InvalidPhoneNumber手機(jī)號(hào)格式錯(cuò)誤檢查號(hào)碼位數(shù)、是否含有非數(shù)字字符TemplateError模板不存在或未審核去后臺(tái)確認(rèn)模板編號(hào)確認(rèn)審核狀態(tài)TemplateMissingParam缺少模板變量對(duì)照模板占位符補(bǔ)全template_param觸發(fā)日發(fā)送量限制超出頻率限制降低發(fā)送頻率或更換號(hào)碼再試模板變量值非法變量?jī)?nèi)容不符合規(guī)范檢查是否含敏感詞變量類(lèi)型是否匹配簽名未審核簽名不可用到平臺(tái)查看簽名審核狀態(tài)如果你的返回里沒(méi)有明確錯(cuò)誤碼而是一條英文字符串通常也會(huì)有“msg”“message”字段給出說(shuō)明。把這些信息連同request_id一起保留下來(lái)才能給服務(wù)商客服提交有效工單。5.3 返回成功但收不到短信還有一種更隱蔽的情況接口返回“發(fā)送成功”但用戶(hù)就是收不到短信。這里的原因往往不在接口調(diào)用而在于短信鏈路本身。常見(jiàn)原因有測(cè)試模板限定了接收號(hào)碼只允許給申請(qǐng)時(shí)填寫(xiě)的測(cè)試號(hào)發(fā)送。發(fā)給其他號(hào)碼雖然返回成功但實(shí)際上不會(huì)真正下發(fā)。手機(jī)管家攔截。短信被手機(jī)系統(tǒng)自動(dòng)歸類(lèi)到騷擾短信或推廣短信里用戶(hù)在通知欄看不到。很多驗(yàn)證碼短信被攔截后連提醒都沒(méi)有你無(wú)法控制。平臺(tái)通道排隊(duì)。高峰期或有大量通知發(fā)送時(shí)服務(wù)商的消息隊(duì)列可能積壓導(dǎo)致延遲幾分鐘到達(dá)。短信內(nèi)容命中風(fēng)控規(guī)則。比如模板變量里包含了“鏈接”“銀行卡”等詞被策略攔截但API層仍然記錄為“發(fā)送成功”。遇到這種問(wèn)題不要反復(fù)重發(fā)。先看平臺(tái)控制臺(tái)里的發(fā)送記錄如果有逐條詳情會(huì)直觀得多??刂婆_(tái)顯示下發(fā)成功而用戶(hù)沒(méi)收到再引導(dǎo)用戶(hù)檢查攔截、信號(hào)等問(wèn)題。6. 進(jìn)階玩法批量發(fā)送、子線程與定時(shí)任務(wù)基礎(chǔ)功能穩(wěn)定之后你還可以在易語(yǔ)言里把短信能力做深一層。比如批量通知、定時(shí)提醒、多線程發(fā)送這些在實(shí)際軟件里都是高頻需求。6.1 批量發(fā)送的節(jié)奏控制批量發(fā)送用戶(hù)通知時(shí)切忌用一個(gè)for循環(huán)猛發(fā)。平臺(tái)有限頻規(guī)則短時(shí)間內(nèi)大量請(qǐng)求會(huì)返回“限流”而且被限流后往往要等一段時(shí)間才能恢復(fù)。我的做法是每發(fā)一條延時(shí)200-500毫秒并記錄這次發(fā)送是否成功。如果返回限流或異常就暫停1-2秒再繼續(xù)。用戶(hù)量特別大時(shí)可以按批次拆分比如每次100條批次之間間隔30秒。6.2 子線程發(fā)短信怎么安全操作UI控件易語(yǔ)言里耗時(shí)網(wǎng)絡(luò)操作如果放在窗口主線程里執(zhí)行窗口會(huì)卡住無(wú)響應(yīng)用戶(hù)會(huì)以為程序死了。正確做法是開(kāi)子線程發(fā)送。但易語(yǔ)言的子線程有講究子線程不能直接修改UI控件的屬性否則輕則顯示異常重則程序崩潰。結(jié)合上面的示例如果你在子線程里發(fā)完短信想更新“標(biāo)簽_提示”的標(biāo)題不能直接寫(xiě)標(biāo)簽_提示.標(biāo)題 “發(fā)送成功”而是應(yīng)該給主線程發(fā)一個(gè)消息讓窗口過(guò)程來(lái)處理。常用思路是窗口加一個(gè)“投遞消息”處理或者用精易模塊的線程池配合窗口句柄發(fā)一個(gè)自定義消息過(guò)去然后在窗口的事件里更新控件。這里簡(jiǎn)單給一個(gè)處理方向定義消息常量比如WM_MY_SMS_RESULT用PostMessageA (窗口句柄, 消息常量, 結(jié)果狀態(tài), 0)投遞到窗口。窗口的“自定義消息”事件中再根據(jù)結(jié)果狀態(tài)更新界面。這個(gè)模式能繞開(kāi)“子線程操作UI”的崩潰問(wèn)題也是易語(yǔ)言開(kāi)發(fā)里的標(biāo)準(zhǔn)解法。6.3 定時(shí)任務(wù)與重試機(jī)制做定時(shí)短信提醒比如每天9點(diǎn)提醒打卡可以在窗口里放一個(gè)時(shí)鐘控件周期設(shè)成60秒每次觸發(fā)時(shí)檢查任務(wù)列表到時(shí)間了就調(diào)用發(fā)送子程序。注意時(shí)鐘控件本身也是主線程的定時(shí)檢查邏輯要輕量不要在時(shí)鐘事件里直接做網(wǎng)絡(luò)請(qǐng)求而是啟動(dòng)一個(gè)子線程去發(fā)送。發(fā)送失敗的重試也很重要。我的建議是加一個(gè)“最大重試次數(shù)”和“重試間隔”。比如失敗后隔10秒重試一次最多重試3次。重試時(shí)要避免用同一時(shí)刻戳拼出的簽名因?yàn)椴糠制脚_(tái)會(huì)校驗(yàn)時(shí)間戳?xí)r效比如要求5分鐘內(nèi)有效重試前要重新取時(shí)間戳并重新簽名。還有一個(gè)小優(yōu)化把發(fā)送記錄寫(xiě)入本地SQLite或文本數(shù)據(jù)庫(kù)包括發(fā)送時(shí)間、手機(jī)號(hào)、模板、返回碼、請(qǐng)求ID。日后對(duì)賬、排查、統(tǒng)計(jì)都有據(jù)可查不用靠記憶。最后分享一個(gè)我自己的小習(xí)慣在正式接短信API之前我會(huì)先去平臺(tái)控制臺(tái)打開(kāi)“測(cè)試模式”用腳本批量生成20條測(cè)試參數(shù)把簽名的邊界情況全跑一遍——包括空手機(jī)號(hào)、11位手機(jī)號(hào)、帶86前綴的手機(jī)號(hào)、中文變量、超長(zhǎng)變量、特殊字符。這一套測(cè)下來(lái)能暴露的問(wèn)題基本都暴露完了。你把這些經(jīng)驗(yàn)沉淀成自己的測(cè)試用例以后換服務(wù)商、改模板、調(diào)密鑰都有了一套現(xiàn)成的回歸手段。短信功能本身不復(fù)雜復(fù)雜的是你愿不愿意把細(xì)節(jié)做扎實(shí)。