UML软件工程组织

Ajax基础教程 - 4.8 访问Web服务
作者:(美)阿斯利森 舒塔   出处:csdn

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 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;

 

    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");

        }

    }

}

 

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);

 

    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="25">25</option>

        <option value="50">50</option>

    </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?"

                     + "appid=your_app_id" + "&type=all";

 

    protected void processRequest(HttpServletRequest request

                                               , HttpServletResponse response)

    throws ServletException, IOException {

 

        String url = YAHOO_SEARCH_URL + "&" + 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吗?答案很简单——可以。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请求。

 

上一页     首页    下一页

 

版权所有:UML软件工程组织