4.8 访问Web服务
多年以来一直存在一个软件工程问题:从一台机器调用另一台机器上的服务或方法,即使这些机器使用完全不同的硬件或软件。对于这个问题,最近提出的解决方案是Web服务。几年前,Web服务大受吹捧,它的头上围绕着耀眼的光环,有些人认为Web服务就是分布式软件开发的“圣杯”。后来,它的光芒逐渐黯淡下来,Web服务最终找到了自己合适的位置,它是支持异构计算机系统相互操作的一种有用的工具。
Web服务通常用作为计算机系统之间的通信管道,这与CORBA(公共对象请求代理体系结构)、RMI(远程方法调用)或DCOM(分布式组件对象模型)很相似。区别在于,Web服务独立于具体的开发商,可以采用大量编程工具和平台来实现。为了支持更高层次的互操作性,Web服务是基于文本的协议,通常在HTTP之上实现。由于Web服务是基于文本的协议,所以几乎总能使用某种XML。
最著名的 Web服务实现是SOAP(简单对象访问协议)。SOAP是由W3C管理的规约,它是XML协议,对于如何调用远程过程给出了定义。
WSDL(Web服务描述语言)文档也是XML文档,描述了如何创建Web服务的客户。通过提供WSDL文档,Web服务提供者就能很轻松地为可能的客户创建客户端代码。WSDL和SOAP通常一同使用,不过不一定非得这样,因为这两个规约是分开维护的。
尽管人们在简化SOAP实现上做出了很大努力,但SOAP还是一个很难使用的技术,因此很受“排挤”,只有在跨平台互操作性确实是一个很重要的需求时才会使用SOAP。实现Web服务还有一种更简单的方法,称为REST(代表状态传输),它在开发人员中享有越来越高的知名度,这些开发人员一方面希望得到SOAP好处的80%,另一方面只希望付出SOAP代价的20%。
Yahoo!选择REST作为其公共Web服务的协议。Yahoo!认为基于REST的服务很容易理解,而且很推崇REST的“平易近人”,因为当前大多数编程语言都可以访问REST。实际上,Yahoo!相信,与SOAP相比,REST的门槛更低,使用也更容易。
通过使用REST,建立请求时可以先指定一个服务入口URL,再向查询串追加搜索参数。服务将结果返回为XML文档。这个模式听上去是不是很熟悉?你说对了,它与本书中你见过的Ajax例子是一样的。
XMLHttpRequest
对象非常适合作为基于
REST
的
Web
服务的客户。使用
XMLHttpRequest对象,可以向Web服务异步地发出请求,并解析得到的XML响应。对于Yahoo! Web服务,XMLHttpRequest对象可以向Yahoo!发出请求,搜索指定的项。一旦Yahoo!返回响应,则使用JavaScript DOM方法解析响应,并向页面动态地提供结果数据。
代码清单4-15展示了如何使用Ajax技术访问Yahoo! Web服务,并向页面提供结果。页面上的文本字段允许用户指定搜索项。用户可以使用选择框来指定需要显示多少个结果。点击Submit(提交)按钮就能启动搜索。
不过,先等等!第2章我们曾经说过,XMLHttpRequest对象只能访问发起文档(即调用脚本)所在域中的资源。如果试图访问其他域的资源,可能因为浏览器的安全限制而失败。怎么解决呢?
解决办法有好几个。在第2章已经了解到,浏览器实现安全沙箱的方式各有不同。IE会询问用户是否允许访问另一个域中的资源。Firefox则会报告错误,自动失败,虽然可以用专用于Firefox的JavaScript代码避免这种行为。
还有一个选择,这也是本例中要采用的方法,就是建立Yahoo!的网关,它与XMLHttp-
Request脚本在同一个域中。由网关接收来自XMLHttpRequest对象的请求,并把它转发到Yahoo! Web服务。Yahoo!做出响应返回结果时,网关再把结果路由传送到浏览器。通过使用这种方法,就能避免使用浏览器特定的JavaScript。另外,这种方法也更加健壮,因为你还可以扩展网关,让它支持其他的Web服务提供者。
代码清单
4-15
yahooSearch.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Yahoo! Search Web Services</title>
<script type="text/javascript">
var xmlHttp;
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
else if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
}
}
function doSearch() {
var searchString = document.getElementById("searchString").value;
if(searchString == ""){
return;
}
var url = "YahooSearchGateway?" + createQueryString()
+ "&ts=" + new Date().getTime();
createXMLHttpRequest();
xmlHttp.onreadystatechange = handleStateChange;
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
}
function createQueryString() {
var searchString = document.getElementById("searchString").value;
searchString = escape(searchString);
var maxResultsCount = document.getElementById("maxResultCount").value;
//query,results都是固定的,并且有一定的要求,还有其他参数的,具体看我最后给的链接
var queryString = "query=" + searchString + "&results=" + maxResultsCount;
return queryString;
}
function handleStateChange() {
if(xmlHttp.readyState == 4) {
if(xmlHttp.status == 200) {
parseSearchResults();
}
else {
alert("Error accessing Yahoo! search");
}
}
//可以添加 加载结果完成 前的等待页面
//else{
// 等待页面的DIV,完成后把这个DIV清除
//}
}
function parseSearchResults() {
var resultsDiv = document.getElementById("results");
while(resultsDiv.childNodes.length > 0) {
resultsDiv.removeChild(resultsDiv.childNodes[0]);
}
var allResults = xmlHttp.responseXML.getElementsByTagName("Result");
var result = null;
for(var i = 0; i < allResults.length; i++) {
result = allResults[i];
parseResult(result);
}
}
function parseResult(result) {
var resultDiv = document.createElement("div");
var title = document.createElement("h3");
title.appendChild(document.createTextNode(
getChildElementText(result, "Title")));
resultDiv.appendChild(title);
//
var summary = document.createTextNode(getChildElementText(result, "Summary"));
//resultDiv.appendChild(summary); //Summary不存在了
resultDiv.appendChild(document.createElement("br"));
var clickHere = document.createElement("a");
clickHere.setAttribute("href", getChildElementText(result, "ClickUrl"));
clickHere.appendChild(document.createTextNode
(getChildElementText(result, "Url")));
resultDiv.appendChild(clickHere);
document.getElementById("results").appendChild(resultDiv);
}
function getChildElementText(parentNode, childTagName) {
var childTag = parentNode.getElementsByTagName(childTagName);
return childTag[0].firstChild.nodeValue;
}
</script>
</head>
<body>
<h1>Web Search Using Yahoo! Search Web Services</h1>
<form action="#">
Search String: <input type="text" id="searchString"/>
<br/><br/>
Max Number of Results:
<select id="maxResultCount">
<option value="1">1</option>
<option value="10">10</option>
<option value="15">15</option>
<option value="20">20</option>
<!-- 显示的记录数默认是10 ,最大是20而已-->
</select>
<br/><br/>
<input type="button" value="Submit" onclick="doSearch();"/>
</form>
<h2>Results:</h2>
<div id="results"/>
</body>
</html>
点击页面上的Submit(提交)按钮将调用
doSearch函数。
这个函数使用
createQuery- String函数来创建目标URL,
createQueryString函数负责把搜索项和显示的最大结果数(即最多显示多少个结果)放在查询串中。需要注意,参数名(
query和
results)都是Yahoo! Search API定义的。
createQueryString函数创建的查询串发送给Yahoo! Search网关。在这个例子中,网关实现为名为
YahooSearchGatewayServlet的Java servlet(见代码清单4-16)。这个servlet的目的很简单,就是转发对Yahoo! Search URL的所有请求,并把结果传给浏览器。当然,这个网关也可以用其他语言(而不是Java)来实现。这个网关很简单,在XMLHttpRequest对象需要访问其他域中的资源时,这个网关确实能解决问题。
代码清单
4-16
YahooSearchGatewayServlet.java
package ajaxbook.chap4;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.servlet.*;
import javax.servlet.http.*;
public class YahooSearchGatewayServlet extends HttpServlet {
private static final String YAHOO_SEARCH_URL =
//" http://api.search.yahoo.com/WebSearchService/V1/webSearch? "
" http://local.yahooapis.com/LocalSearchService/V3/localSearch ?" //上面的已经不能用了,这是新的
+ "appid=your_app_id " // + "&type=all"; appid是必需的,type没有了
+ "&zip=94306"; //没这个查询没结果,我都不是很清楚, 邮政编码
protected void processRequest(HttpServletRequest request
, HttpServletResponse response)
throws ServletException, IOException {
String url = YAHOO_SEARCH_URL + "&" + request.getQueryString ();//request.getQueryString()返回请求
//页面?后面的部分
HttpURLConnection con = (HttpURLConnection)new URL(url).openConnection();
con.setDoInput(true);
con.setDoOutput(true);
con.setRequestMethod("GET");
//Send back the response to the browser
response.setStatus(con.getResponseCode());
response.setContentType("text/xml");
BufferedReader reader =
new BufferedReader(new InputStreamReader(con.getInputStream()));
String input = null;
OutputStream responseOutput = response.getOutputStream();
while((input = reader.readLine()) != null) {
responseOutput.write(input.getBytes());
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
}
Yahoo! Search把结果返回给网关,而且网关将结果再转发给浏览器后,会调用
parse-
SearchResults函数。这个函数从XMLHttpRequest对象获取得到的XML文档,并查找所有标记名为
Result的元素。
各
Result元素传给
parseResult函数。这个函数使用Result元素的子元素
Title、
Summary、
ClickUrl和
Url创建内容,并增加到页面。
可以看到,与基于REST的Web服务结合使用时,Ajax技术相当强大。如果想在你自己的域中访问Web服务,用JavaScript就可以完成。否则,当访问其他域的资源时,就要创建外部资源的某种网关,这样就能避免浏览器安全沙箱问题。
图4-15显示了结合使用Yahoo! Search Web服务和Ajax的搜索结果。
图4-15
结合使用Yahoo! Search Web服务和Ajax的搜索结果
可以结合使用Ajax和SOAP吗?答案很简单——可以。
Ajax
技术和基于
SOAP
的
Web
服务可以一同使用,不过,与使用基于
REST
的
Web
服务相比,这需要做更多的工作。
REST和SOAP都把响应返回为XML文档。二者之间最显著的差异是,REST将请求作为带查询串参数的简单URL发送,而SOAP请求是具体的XML文档,通常通过
POST而不是
GET发送。
结合使用SOAP和Ajax时,要求以某种方式创建SOAP请求的XML,这可能并不容易。一种做法是使用串连接来创建请求XML。尽管概念上讲很简单,但这种方法有些混乱,而且很容易出错,如很容易这儿忘了双引号,那儿忘了加号。
还有一种选择是使用一个XMLHttpRequest请求从网站加载静态XML文档,文档是SOAP请求的模板。一旦加载了模板,就可以使用JavaScript DOM方法来修改模板,使之满足特定的请求。请求准备好后,再用第二个XMLHttpRequest请求发送新创建的SOAP请求。
responseXML:
<?
xml version="1.0"
?>
-
<
ResultSet
xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
xmlns
="urn:yahoo:lcl"
xsi:schemaLocation
="urn:yahoo:lcl http://api.local.yahoo.com/LocalSearchService/V3/LocalSearchResponse.xsd"
totalResultsAvailable
="19"
totalResultsReturned
="1"
firstResultPosition
="1"
>
<
ResultSetMapUrl
>
http://maps.yahoo.com/broadband/?q1=Palo+Alto%2C+CA+94306
&tt
=ajax
&tp
=1
</
ResultSetMapUrl
>
-
<
Result
id
="32213349"
>
<
Title
>
Ajax Custom Plastics
</
Title
>
<
Address
>
34585 7th St
</
Address
>
<
City
>
Union City
</
City
>
<
State
>
CA
</
State
>
<
Phone
>
(510) 476-8000
</
Phone
>
<
Latitude
>
37.591159
</
Latitude
>
<
Longitude
>
-122.007798
</
Longitude
>
-
<
Rating
>
<
AverageRating
>
NaN
</
AverageRating
>
<
TotalRatings
>
0
</
TotalRatings
>
<
TotalReviews
>
0
</
TotalReviews
>
<
LastReviewDate
/>
<
LastReviewIntro
/>
</
Rating
>
<
Distance
>
12.31
</
Distance
>
<
Url
>
http://local.yahoo.com/details?id=32213349
&stx
=ajax
&csz
=Union+City+CA
&ed
=A_zEvq160Sy3zcQJxpKUsvvQnvLHOILp6hc8bPPfRwDnzV3Br.iK1ClzZB_v.Jo3ASrvVg--
</
Url
>
<
ClickUrl
>
http://local.yahoo.com/details?id=32213349
&stx
=ajax
&csz
=Union+City+CA
&ed
=A_zEvq160Sy3zcQJxpKUsvvQnvLHOILp6hc8bPPfRwDnzV3Br.iK1ClzZB_v.Jo3ASrvVg--
</
ClickUrl
>
<
MapUrl
>
http://maps.yahoo.com/maps_result?name=Ajax+Custom+Plastics
&desc
=5104768000
&csz
=Union+City+CA
&qty
=9
&cs
=9
&ed
=A_zEvq160Sy3zcQJxpKUsvvQnvLHOILp6hc8bPPfRwDnzV3Br.iK1ClzZB_v.Jo3ASrvVg--
&gid1
=32213349
</
MapUrl
>
<
BusinessUrl
>
http://www.ajaxmfg.com/
</
BusinessUrl
>
<
BusinessClickUrl
>
http://www.ajaxmfg.com/
</
BusinessClickUrl
>
-
<
Categories
>
<
Category
id
="96931027"
>
Plastic Products
</
Category
>
</
Categories
>
</
Result
>
</
ResultSet
>
-
<!--
ws05.search.scd.yahoo.com compressed/chunked Fri Aug 24 09:08:31 PDT 2007
-->
点击看看:http://local.yahooapis.com/LocalSearchService/V3/localSearch?appid=my_test_id&query=ajax&results=1&zip=94306
Yahoo! Search Web Services官方说明文档:http://developer.yahoo.com/search/local/V3/localSearch.html
红色部分为lin49940修改
原书链接:http://book.csdn.net/bookfiles/11/100117041.shtml