1. Web Service 接口
1.1 接口方式說明和長處
在筆者的開發生涯中,當作為接口提供商給第三方提供接口時,以及作為client去調用第三方提供的接口時,大部分時候都是使用 Web Service接口, Web Service作為接口使用廣泛的原因,與它的特點息息相關。
Web Service的主要目標是跨平台的可互操作性,為了實現這一目標, Web Service 全然基於 XML(可擴展標記語言)、 XSD( XML Schema)等獨立於平台、獨立於軟件供應商的標准,是創建可互操作的、分布式應用程序的新平台。因此使用 Web Service有很多長處:
1.1.1 跨防火牆的通信
假設應用程序有成千上萬的用戶,並且分布在世界各地,那么client和server之間的通信將是一個棘手的問題。由於client和server之間一般會有防火牆或者代理server。要調用 Web Service,能夠直接使用 SOAPclient,然后把它和應用程序連接起來。不僅縮短了開發周期,還降低了代碼復雜度,並可以增強應用程序的可維護性。
1.1.2 跨程序語言的應用程序集成
在企業的各種應用系統中,非常多系統不是使用同樣的語言編寫的,比如有的使用 Java,有的使用 php、 C#、 asp。當各種系統之間須要交互時,可使用各種語言都通用的 WSDL定義接口,對外將須要的接口暴露給指定的客戶。
XML Web services 提供了在松耦合環境中使用標准協議( HTTP、 XML、 SOAP 和 WSDL)交換消息的能力。消息能夠是結構化的、帶類型的,也能夠是松散定義的。
1.1.3 軟件和數據重用
Web Service在同意重用代碼的同一時候,能夠重用代碼背后的數據。使用 Web Service,再也不必像曾經那樣,要先從第三方購買、安裝軟件組件,再從應用程序中調用這些組件;僅僅須要直接調用遠端的 Web Service就能夠了。
還有一種軟件重用的情況是,把好幾個應用程序的功能集成起來,通過 Web Service “暴露 ”出來,就能夠很easy地把全部這些功能都集成到你的門戶網站中,為用戶提供一個統一的、友好的界面。
能夠在應用程序中使用第三方的 Web Service 提供的功能,也能夠把自己的應用程序功能通過 Web Service 提供給別人。兩種情況下,都能夠重用代碼和代碼背后的數據。
1.2 重要概念
1.2.1 何為Web Service ?
Web Service是構建互聯網分布式系統的基本部件,它是一個應用程序,它向外界暴露出一個可以通過 Web 進行調用的 API 。這就是說,別人可以用編程的方法通過 Web 來調用這個應用程序。
它通過標准通信協議,在互聯網上以服務的方式公布實用的程序模塊,眼下大部分是用 SOAP作為通信協議。
它提供一份具體的接口說明書,來幫助用戶構建應用程序,這個接口說明書叫 WSDL( Web服務描寫敘述語言, Web Service Description Language)。
通常已公布的 Web Service要注冊到管理server,便於使用者查詢和使用。這個是通過 UDDI( 統一描寫敘述、發現和集成, Universal Discovery Description and Integration)來完畢的。
1.2.2 何為 SOAP 協議?
SOAP定義 SOAP消息的 XML格式( XML格式),假設你用一對 SOAP標記( SOAP Elements)把 XML文檔括起來,那么這個就是一個 SOAP消息。
SOAP規范還定義了如何用 XML來描寫敘述程序數據,如何運行 RPC( 遠程過程調用, Remote Procedure Call)。大多數 SOAP解決方式都支持 RPC-style應用程序,由於非常多程序猿已對 DCOM或 CORBA熟悉。 它還支持 Document-style應用程序( SOAP消息僅僅包括 XML文本信息)。 Document-style應用程序有非常好的靈活性,所以非常多用 RPC非常難構建的 Web Service用這樣的方式構建。
最后 SOAP規范還定義了 HTTP消息是如何傳輸 SOAP消息的。這並不代表 SOAP僅僅能用 HTTP來作為傳輸協議, MSMQ、 SMTP、 TCP/IP都能夠做 SOAP的傳輸協議。
安全性對於應用程序來說是非常重要的。那么 SOAP的安全性怎樣呢?對於把 HTTP作為傳輸協議的 SOAP來說是沒有問題的,由於 HTTP協議已經有非常好的安全構架。那么用其它傳輸協議會出現安全問題嗎?這方面也已經有相關規范
( http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnglobspec/html/ws-security.asp )。
1.2.3 何為 WSDL ?
WSDL是一種 XML文檔,它定義 SOAP消息和這些消息是如何交換的。 IDL( Interface Description Language,接口描寫敘述語言)是用於 COM和 CORBA的, WSDL是用於 SOAP的。 WSDL是一種 XML文檔,所以能夠閱讀和編輯,但非常多時候是用工具來創建、由程序閱讀。
舉個實例,當讀者須要使用第三方的 Web Service構建應用程序。你能夠向接口提供商索取使用 WSDL文檔,在該文檔中具體的說明了各個方法的方法名、參數和參數類型等信息。在 Java等編程語言的 IDE(比如 My Eclipse)中,能夠依據 Web Servie生成相應的測試代碼,略微改動一下就可以。
1.2.4 何為 UDDI ?
UDDI能夠比喻成電話本,電話本里記錄的是電話信息,而 UDDI記錄的是 Web Service信息。能夠不把 Web Service注冊到 UDDI。但假設要讓全球的人知道這個 Web Service,不妨注冊到 UDDI。
UDDI文件夾說明文件也是一個 XML文檔,它包含三個部分。“白頁( White Paper)”說明提供 Web Service的公司(人)信息,比方說名稱、地址和聯系方式等等。“黃頁( Yellow Paper)”說明 UDDI文件夾的分類,比方說金融、服務和印刷等等。“綠頁( green Paper)”說明接口( Web Service 提供的)的具體信息。 UDDI提供多種查詢方式,來幫助你找到須要的 Web Service。假設你查詢與財務有關的 Web Service,那么 UDDI會提供具體的信息。
1 .2.5 何為 XML ?
XML( Extensible Markup Language)就可以擴展標記語言,它與 HTML一樣,都是 SGML(Standard Generalized Markup Language,標准通用標記語言 )。在 Web Service接口中, WSDL和 UDDI文件夾文件都是一種 XML文檔, XML攻克了數據表示的問題。
1.2.6 何為 XSD ?
XML攻克了數據表示的問題,但它未定義一套標准的數據類型,更沒有說怎么去擴展這套數據類型。比如,整型數究竟代表什么? 16位, 32位,還是 64位?
W3C制定的 XML Schema(XSD)就是專門解決問題的一套標准。它定義了一套標准的數據類型,並給出了一種語言來擴展這套數據類型。 Web Service就是用 XSD來作為其數據類型系統的。
1.3 開發 Web Service 接口和調用測試
在 Java IDE環境中開發 Web Service接口,以及怎樣調用第三方的 WSDL文檔怎樣進行接口測試的參考文章詳見:
1)《 使用XFire+Spring 構建 Web Service (一) ——helloWorld 篇 》:
http://www.blogjava.net/amigoxie/archive/2007/09/26/148207.html
2)《 使用XFire+Spring 構建 Web Service (二) 》:
http://www.blogjava.net/amigoxie/archive/2007/09/28/149074.html
3)《 依據wsdl 生成相應的 Java 代碼進行接口測試(一) 》:
http://www.blogjava.net/amigoxie/archive/2009/11/20/303038.html
1.4 開發舉例
1.4.1 作為提供商提供hello world的接口
參見:《 使用XFire+Spring 構建 Web Service (一) ——helloWorld 篇 》:
http://www.blogjava.net/amigoxie/archive/2007/09/26/148207.html
1.4.2 作為提供商提供用戶信息查詢接口
參見:《 使用XFire+Spring 構建 Web Service (二) 》:
http://www.blogjava.net/amigoxie/archive/2007/09/28/149074.html
2. js 接口
2.1 接口方式說明和優缺點
在開發的過程中,也遇到過須要調用第三方接口的情況,比如筆者在完畢的一個股票查詢的小 demo中,就須要調用新浪提供的股票查詢的 js接口。另外有一次,在系統中使用了第三方的 GIS系統,調用的也是 js接口。 由於調用 js接口的門檻非常低,所以有的接口供應商會提供多種調用接口的方式,比如 Web Servivce接口和 js接口等。
對於瀏覽器來說, script標簽的 src屬性所指向資源就跟 img標簽的 src屬性所指向的資源一樣,都是一個靜態資源,瀏覽器會在適當的時候自己主動去加 載這些資源,而不會出現所謂的跨域問題。這樣我們就能夠通過該屬性將要訪問的數據對象引用進當前頁面而繞過 js跨域問題。當然,前提是接口必須是返回一段 js腳本,如一個 json對象數組定義的腳本:
modlist = [
{"modname": "mod1", "usernum": 200, "url": "/widget/info/1"},
{"modname": "mod2", "usernum": 300, "url" : "/widget/info/2"},
…
];
但 script標簽也有一定的局限性,並不能解決全部 js跨域問題。 script標簽的 src屬性值不能動態改變以滿足在不同條件下獲取不同數據的需求, 更重要的是,不能通過這樣的方式正確訪問以 xml內容方式組織的數據。
2.2 開發舉例
2.2.1 新浪股票查詢的js接口
功能說明 : stockDetail.jsp依據傳入的 stockId參數,調用新浪股票查詢提供的 js接口返回股票結果信息,並解析返回結果,將股票信息在頁面展示出來。
stockDetail.jsp代碼參考例如以下:
<%
@ page language = " java " contentType = " text/html; charset=UTF-8 " pageEncoding = " UTF-8 "
%>
<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
>
<%
String stockId = request.getParameter( " stockId " );
if (stockId == null ) {
stockId = " 000001 " ;
}
%>
<
html
>
<
head
>
<
meta
http-equiv
="Content-Type"
content
="text/html; charset=UTF-8"
/>
<
title
>
股票查詢結果
</
title
>
<
link
href
="<%=request.getContextPath() %>/css/style.css"
type
="text/css"
rel
="stylesheet"
>
<
script
type
="text/javascript"
src
="http://hq.sinajs.cn/list=s_sh<%=stockId %>"
charset
="gb2312"
></
script
>
<
meta
http-equiv
="pragma"
content
="no-cache"
>
<
meta
http-equiv
="cache-control"
content
="no-cache"
>
<
meta
http-equiv
="expires"
content
="0"
>
</
head
>
<
body
onload
=""
>
<
div
class
="bodyDiv"
>
<
table
>
<
tr
>
<
td
colspan
="2"
valign
="bottom"
align
="left"
style
="width:176px; background: url(<%=request.getContextPath() %>/images/line2_bg.gif) repeat-x;border-bottom: 1px solid #b0bec7;"
height
="19"
>
<
span
class
="titleFont"
>
<
font
class
="newTitleFont"
><
b
>
股票查詢結果
</
b
></
font
>
</
span
>
</
td
>
</
tr
>
<
tr
>
<
td
>
指數名稱:
</
td
>
<
td
><
span
id
="stockName"
>
</
span
></
td
>
</
tr
>
<
tr
>
<
td
>
當前點數:
</
td
>
<
td
><
span
id
="currentPoint"
>
</
span
></
td
>
</
tr
>
<
tr
>
<
td
>
當前價格:
</
td
>
<
td
><
span
id
="currentPrice"
>
</
span
></
td
>
</
tr
>
<
tr
>
<
td
>
漲跌率:
</
td
>
<
td
><
span
id
="ratio"
>
</
span
></
td
>
</
tr
>
<
tr
>
<
td
>
成交額(w):
</
td
>
<
td
><
span
id
=turnVolume
>
</
span
></
td
>
</
tr
>
<
tr
>
<
td
colspan
="2"
valign
="bottom"
align
="right"
style
="width:176px; background: url(<%=request.getContextPath() %>/images/line2_bg.gif) repeat-x;border-bottom: 1px solid #b0bec7;"
height
="19"
>
<
span
class
="titleFont"
>
<
font
class
="newTitleFont"
><
b
>
1日K線
0返回
</
b
></
font
>
</
span
>
</
td
>
</
tr
>
</
table
>
</
div
>
<
script
language
="javascript"
>
<!--
// 查詢結果的格式為:指數名稱,當前點數,當前價格,漲跌率,成交量(手),成交額(萬元)
// 解析字符串
var stockValue = hq_str_s_sh <%= stockId %> ;
var stockArray = stockValue.split( " , " );
document.getElementById( " stockName " ).innerText = stockArray[ 0 ];
document.getElementById( " currentPoint " ).innerText = stockArray[ 1 ];
document.getElementById( " currentPrice " ).innerText = stockArray[ 2 ];
document.getElementById( " ratio " ).innerText = stockArray[ 3 ];
document.getElementById( " turnVolume " ).innerText = stockArray[ 5 ];
-->
</
script
>
</
body
>
</
html
>
帶上 6位 stockId參數(比如:值為 000002),實時的 A股(代號為 s_sh000002)查詢結果例如以下圖所看到的:
在文件頭部可看到例如以下一句引入了新浪提供的 js:
<
script
type
="text/javascript"
src
="http://hq.sinajs.cn/list=s_sh<%=stockId %>"
charset
="gb2312"
></
script
>
用例如以下語句獲得通過接口查詢到的數據:
var stockValue = hq_str_s_sh
<%
=
stockId
%>
;
2.2.2 對外提供js接口
對外提供js接口僅僅須要通過<script src="..." type="..."/>請求的地址返回的是JSON字符串就可以。
在本實例中,用到了筆者一篇 JSON文章的實例(《 JSON知識總結入門篇》: http://www.blogjava.net/amigoxie/archive/2010/09/25/332832.html ),在上面進行了小幅改動,簡便起見,沒有創建不論什么的Java類,提供的對外的js接口是直接通過json.txt,通過該文件返回一個JSON字符串,在實際的應用情況中,能夠是一個Ation等。
json.txt定義了 JSON格式的字符串,並定義放在 json這個變量中, jsInterface.html文件請求遠端的一個路徑,而后解析返回的 JSON串,並打印出來。 json.txt在遠端的一個server上,比如該文件訪問地址為: http://test.com/json.txt ,文件的內容例如以下:
var json={
"programmers": [
{ "firstName": "阿蜜果", "lastName":"McLaughlin", "email": "brett@newInstance.com" },
{ "firstName": "范范", "lastName":"Hunter", "email": "jason@servlets.com" },
{ "firstName": "高子", "lastName":"Harold", "email": "elharo@macfaq.com" }
],
"authors": [
{ "firstName": "安安", "lastName": "Asimov", "genre": "science fiction" },
{ "firstName": "Tad", "lastName": "Williams", "genre": "fantasy" },
{ "firstName": "Frank", "lastName": "Peretti", "genre": "christian fiction" }
],
"musicians": [
{ "firstName": "茂茂", "lastName": "Clapton", "instrument": "guitar" },
{ "firstName": "Sergei", "lastName": "Rachmaninoff", "instrument": "piano" }
]
}
在本地創建一個 jsInterface.html網頁,使用 <script type="text/javascript" src=”…”/>請求返回 json字符串的路徑信息,接着進行打印,該文件代碼例如以下:
<
html
>
<
head
>
<
title
>
JS Interface Test
</
title
>
<
meta
http-equiv
="Content-Type"
content
="text/html; charset=UTF-8"
>
<
script
type
="text/javascript"
src
="http://test.com/json.txt"
></
script
>
<
script
type
="text/javascript"
>
alert(json.programmers[ 0 ].firstName + ',' + json.programmers[ 0 ].lastName + ',' + json.programmers[ 0 ].email);
alert(json.programmers[ 1 ].firstName + ',' + json.programmers[ 1 ].lastName + ',' + json.programmers[ 1 ].email);
alert(json.programmers[ 2 ].firstName + ',' + json.programmers[ 2 ].lastName + ',' + json.programmers[ 2 ].email);
alert(json.authors[ 0 ].firstName + ',' + json.authors[ 0 ].lastName + ',' + json.authors[ 0 ].genre);
alert(json.authors[ 1 ].firstName + ',' + json.authors[ 1 ].lastName + ',' + json.authors[ 1 ].genre);
alert(json.authors[ 2 ].firstName + ',' + json.authors[ 2 ].lastName + ',' + json.authors[ 2 ].genre);
alert(json.musicians[ 0 ].firstName + ',' + json.musicians[ 0 ].lastName + ',' + json.musicians[ 0 ].instrument);
alert(json.musicians[ 1 ].firstName + ',' + json.musicians[ 1 ].lastName + ',' + json.musicians[ 1 ].instrument);
</
script
>
</
head
>
<
body
>
</
body
>
</
html
>
執行后可看到執行結果與《 JSON 知識總結入門篇》第一個實例的執行結果一致。
3. http 接口
3.1 接口方式說明和優缺點
須要為第三方提供一個接口,本來打算繼續使用 Web Service接口,結果那邊的開發者說,他們沒有使用過 Web Service接口(是做 IPTV的一個公司),希望我們可以提供 http方式的接口。
另外我們一般在提供 Web Sservice接口的同一時候,也對外提供 http接口。
3.2 開發實例
3.2.1 向http接口發送消息的使用小程序
本實例對自己提供請求信息為xml格式的http接口,將xml格式的請求信息發給http接口的地址后,將調用接口的返回消息簡單的顯示在頁面,為了簡便起見,筆者沒有對js代碼進行包裝。
該html文件代碼例如以下:
<!
DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
>
<
html
>
<
head
>
<
meta
http-equiv
="Content-Type"
content
="text/html; charset=GB2312"
>
<
title
>
http interface test
</
title
>
<
meta
http-equiv
="pragma"
content
="no-cache"
>
<
meta
http-equiv
="cache-control"
content
="no-cache"
>
<
meta
http-equiv
="expires"
content
="0"
>
<
script
>
// XMLHttpRequest
var http_request = false ;
function send_request(method, url, content, responseType, callback)
{
http_request = false ;
// XMLHttpRequest
if (window.XMLHttpRequest)
{
// Mozilla
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType)
{
// MIME
http_request.overrideMimeType( " text/xml " );
}
} else if (window.ActiveXObject)
{
// IE
try
{
http_request = new ActiveXObject( " Msxml2.XMLHTTP " );
} catch (e)
{
try
{
http_request = new ActiveXObject( " Microsoft.XMLHTTP " );
}
catch (e)
{}
}
}
if ( ! http_request)
{
window.alert( " XMLHttpRequest create Error. " );
return false ;
}
if (responseType.toLowerCase() == " text " || responseType.toLowerCase() == " xml " )
{
http_request.onreadystatechange = callback;
} else
{
window.alert( " error responseType. " );
return false ;
}
if (method.toLowerCase() == " get " )
{
http_request.open(method, url, true );
} else if (method.toLowerCase() == " post " )
{
http_request.open(method, url, true );
http_request.setRequestHeader( " Content-Type " , " text/xml " );
} else
{
window.alert( " http method error. " );
return false ;
}
http_request.send(content);
}
function submitInfo()
&n