電子樞紐全稱國家交通運輸物流公共信息平台,主要提供物流及生產企業進行物流相關數據交換的標准和API,詳細介紹可參考其官網www.logink.org,本文假定閱讀者對該平台已有了解,並已成功申請了相應的帳號和數據交換服務。
信用中心是電子樞紐眾多數據服務中的一個,提供物流參與者信用信息的上傳和查詢,包括運輸車輛、從業人員等。官方的示例和介紹大多以Java為主,.net的非常少,希望本文可以幫助.net開發人員快速掌握數據交換方式。
電子樞紐的數據服務分為兩種,一種稱為數據交換,另一種稱為服務調用。
數據交換是一種類似電子郵件的行為,可以把電子樞紐看作是一個郵件服務器,發送和接收數據就與收發電子郵件的方式類同。
服務調用就是常規的HTTP請求,主要用於向電子樞紐查詢信息,例如對於信用中心來說,可以查詢到車輛的運政信息、誠信記錄等。
本文主要介紹的是服務調用方式,要調用電子樞紐提供的服務,首先必須確保已經開通了相關服務,開通後還需要獲得服務的ID,這些任務都可以從電子樞紐的用戶管理中心完成。
在調用服務前,必須獲得用戶驗證的令牌(Access Token),這項工作可通過統一認證服務完成,獲得令牌後,方可憑令牌調用相關服務。
電子樞紐的數據接口都是以Web Service提供的,所以在開始編碼之前,可以先引入相關的服務,在VS裡直接添加服務引用即可,相關的服務地址可以在官網的開放接入中心找到。
值得注意的是,雖然電子樞紐提供的大部分Web Service都可以在VS中直接引入,但還有個別不能采用這種“添加服務引用”的方式,目前已知的就是信用中心服務。對於這種情況,需要在VS中以Web引用的方式來添加,具體方法如下。
右鍵點擊解決方案浏覽器中項目或引用(Reference)的節點,在菜單中選擇“添加服務引用”(Add Service Reference),在彈出的對話框裡點擊“高級”(Advanced),再在彈出的服務引用設置對話框裡點擊“添加Web引用”(Add Web Reference)即可調出添加Web引用的對話框。
至於為什麼要這樣,由於我對SOAP和WCF沒有太多了解,實在沒法回答這個問題,希望這方面的大神可以給出解答。
引用添加完以後,VS會為我們自動生成好相應的類型,直接使用就可以。
首先是獲取令牌,統一驗證服務的客戶端類型是AuthenServiceClient,實例化以後調用他的authenticate方法,方法簽名如下:
authenticate(string applicant, string userid, string password, string resource)
關於這個方法的具體說明,可參考官方說明。
返回值的tokenValid屬性指示是否驗證成功,如果為true,可通過token屬性獲取令牌的值。示例代碼如下:
1 private bool Authenticate(LoginkUser user, string resId, out string token) 2 { 3 AuthenServiceClient clnt = new AuthenServiceClient(); 4 5 var result = clnt.authenticate(user.ExchangeCode, user.ExchangeCode, user.Password, resId); 6 7 if (result.tokenValied) 8 token = result.token; 9 else 10 token = null; 11 12 clnt.Close(); 13 14 return token != null; 15 }
得到令牌以後,就可以直接調用信用中心的查詢服務了,VS生成的信用中心服務的客戶端類型為LoginkServiceService,實例化後調用它的InterfaceName方法。
genericResult InterfaceName(authentication Authentication, publicInformation PublicInformation, string BusinessInformation)
這個方法沒有找到官方的文檔,由於各個參數比較復雜,就不一一介紹了(其實我自己也沒搞明白),直接照抄下面的示例就可以了。
1 private string CallCreditService(LoginkUser user, string token, string action, string request) 2 { 3 var css = _settings.CreditService; 4 5 Logink.Services.Credit.security security = new Logink.Services.Credit.security(); 6 security.LogisticsExchangeCode = user.ExchangeCode; 7 security.UserTokenID = token; 8 9 Logink.Services.Credit.authentication authentication = new Logink.Services.Credit.authentication(); 10 authentication.UserName = user.ExchangeCode; 11 authentication.UserPassword = user.Password; 12 authentication.ServiceId = css.ResourceId; 13 authentication.UserId = user.ExchangeCode; 14 15 Logink.Services.Credit.publicInformation publicInformation = new Logink.Services.Credit.publicInformation(); 16 publicInformation.ServiceType = "3"; 17 publicInformation.ActionType = action; 18 19 Logink.Services.Credit.LoginkServiceService service = new Logink.Services.Credit.LoginkServiceService(); 20 service.Url = css.Url; 21 service.Security = security; 22 23 var result = service.InterfaceName(authentication, publicInformation, request); 24 25 if (result.ResultCode) 26 { 27 return result.BusinessInformation; 28 } 29 else 30 throw new ExchangeException(result.ExceptionInformationCode, result.ExceptionInformation); 31 }
示例中這個CallCreditService方法已經封裝了調用信用中心服務的各種參數,其中action參數表示業務類型,官網有介紹,下面再詳細說下request參數。
request參數實際上是個經過Base64編碼的XML字符串,XML的內容就是各個傳入參數的值,需要注意的是,傳入的這個XML串並不是完整的文檔,而是根節點以下的內容,千萬不要把根節點也傳上來,如果你使用XmlDocument來處理傳入參數,可以使用根節點的InnerXml屬性。
Base64編碼相對來說就比較簡單了,System.Convert類型直接支持轉換為Base64,默認情況下電子樞紐使用的是UTF8編碼,在編/解碼時不要搞錯,否則會查不到數據或出現亂碼。
1 private string XmlToBase64(string xml) 2 { 3 if (string.IsNullOrEmpty(xml)) 4 return xml; 5 6 return Convert.ToBase64String(Encoding.Utf8.GetBytes(xml)); 7 }
最後,需要處理的是InterfaceName的返回值,返回值的ResultCode指示是否調用成功,如果不成功可通過ExceptionInformationCode屬性獲取錯誤代碼,否則可以通過BusinessInformation屬性獲取返回的文本。
返回的文本同樣是一個Base64編碼的XML字符串,轉換成明文以後就可以直接使用了,不過對於明文的處理是一件比較頭痛的事,經過多次試驗最終摸索出以下的規律。
文本為空串:可能是沒有查詢到相關的內容。
文本不是XML:盡管ResultCode為true,但仍舊可能是出現了錯誤,文本的內容就是出錯信息。
對於這些情況,我們的程序都應該進行相應的處理。
1 public XmlDocument QueryCredit(QueryParameters parameters) 2 { 3 if (parameters == null) 4 throw new ArgumentNullException(nameof(parameters)); 5 6 AccessToken token; 7 string data; 8 bool forceRenewToken = false; 9 10 retry: 11 12 // 通過統一驗證服務獲取用戶的訪問令牌 13 token = GetToken(_user, _settings.CreditService.ResourceId, forceRenewToken); 14 15 // 將傳入參數序列化為XML 16 data = parameters.GetXml(); 17 // 對XML轉換為BASE64編碼 18 data = XmlToBase64(data); 19 20 try 21 { 22 // 調用信用中心的Web Service 23 data = CallCreditService(_user, token.Value, parameters.ActionName, data); 24 } 25 catch(ExchangeException ee) 26 { 27 // 判斷是否需要更新令牌 28 if (ee.IsTokenInvalid && !forceRenewToken) 29 { 30 forceRenewToken = true; 31 goto retry; 32 } 33 else 34 throw; 35 } 36 37 if (!string.IsNullOrEmpty(data)) 38 { 39 // 返回的內容已做了BASE64編碼處理,將它轉換為XML。 40 data = Base64ToXml(data); 41 42 // 部分情況下平台返回的可能是一串錯誤信息而非XML,因此對不是"<"開頭的直接按錯誤信息處理。 43 if (data[0] == '<') 44 { 45 XmlDocument doc = new XmlDocument(); 46 doc.LoadXml(data); 47 48 return doc; 49 } 50 else 51 throw new ExchangeException(data); 52 } 53 else 54 { 55 // 如果返回的內容為空,可能是沒有查到相關數據。 56 throw new ExchangeException("110008", "暫時沒有查到相應數據"); 57 } 58 59 }
到些為止,調用信用中心服務所需的代碼都已經大致做了介紹,把它們串起來以後就形成了一個完整的程序,全部源代碼請點擊此處。由於我的電腦上裝的是VS2015,低版本的VS可能沒辦法打開,實在抱歉。
幾個必須加入的QQ群
213604083:平台接入群,這是必加的一個群,有很多平台的管理人員在裡面,一些基礎的問題可以問問他們,當然還有一點很重要的是群裡有開發所需要的許多資源。
383412768:信用接入群,如果要使用信用中心服務,這就是你要找的組織。
363016382:園區通接入群,如果你是為了接入而接入(你懂的),那這裡是也是必須的。
601484722:充裝數據接入群。