AJAX支持的 GOOGLE 地图 MASHUP 教程
时间:2007-06-12 作者: Peter Laird 浏览次数: <script language=”JavaScript” src=”/beadevcount.jsp?d_id=813556″ type=”text/JavaScript”></script> 5577 本文关键字: , mashups , ajax , json , javascript , web 2.0 , Dev Toolbox , Web Services , WebLogic Server , Peter Laird , Web服务 |
编辑注:本教程是
Dev2Dev TechDays 2007
的附赠品,
Dev2Dev TechDays 2007
今年的主题是“构建企业Mashup”。这次研讨会的议题是如何在企业内部有效地使用mashup,会上详细讨论了本教程阐述的技术。在世界各地都举办了此次研讨会,查看
TechDays Web站点
,了解在您附近的城市何时会举办此次研讨会。
摘要
Web 开发的新纪元达到了顶峰,这一阶段称为
Web 2.0
。这一时期迎来了新一批原型化Web 应用程序,包括blog、wiki和mashup。 Mashup是本教程论述的重点,您将了解到如何使用一组通用技术构建一个示例 mashup 。这组技术包括 JavaScript、Ajax、REST、JSON 和Google地图API。 作为 Web 开发人员,了解如何结合使用这些工具很重要。在本教程中,我将使用这些工具轻松构建终极的 Hello World mashup:一个Google地图mashup。
简介
所谓Mashup,就是在将一些数据源和服务组合到一起创建一种新功能或以某种方式添加新值的时候创建的。本教程将介绍如何创建一个Google地图mashup —— 将
Google 地图
提供的地图数据与您自己创建的方位数据服务相结合。
本教程分为三部分:
- Web 2.0工具箱
- 从一个REST 数据服务检索方位
- 用 Google 地图标绘地址
第一部分介绍了我在第二部分和第三部分构建 mashup 时所用的技术。如果想提前浏览我制作的 demo,请浏览Web上托管的
演示
。
让我们开始吧!
Web 2.0 工具箱
Web 开发人员很清楚技术总是在不断地进步,技能在几年之内就会变得过时。我对此并无异议:创新使得 Web 开发变得有趣。新主张、新工具和新技术让我们能够在更短的时间内构建更出色的系统。本节将介绍一些您可能用过也可能没有用过的技术。如果这些对于您来说是新技术,可以将本小节的内容看作快速入门,在此之后,我鼓励您继续深入学习。下文所述内容并不完整,在某些方面过于简略,这样做是为了传达重要的概念。
虽然我要讨论多种技术,但是本教程也需要从头说起。我假定您熟悉以下概念和Web 技术:
- HTML
- XML
- 浏览器和 Web 服务器的角色
- HTTP 请求/响应模型
- 现代编程语言,例如Java、JavaScript、PHP 和 C#
客户端编程
Web 2.0 应用程序的特征是高度的交互性——它像传统桌面应用程序那样进行响应。传统 Web 应用程序一成不变的外观和刷新整个页面的长时间中断都无法满足这一期望。 因此,Web 2.0 应用程序利用了客户端编程技术来帮助应用程序获得更快的响应速度。两种最流行的客户端技术是 JavaScript 和 Adobe Flex。 这两种技术都具备令人叹服的特性,在应用上都取得了巨大的成功。然而,为了缩小本教程的讨论范围,在构建 mashup 的时候,我仅考虑JavaScript 方法。
JavaScript
技术成熟。它是一种强大的客户端编程语言,已经流行数年。随着标准的不断改进,跨浏览器支持显著进步,使得它成为一种可行的方法。对于那些没有接触过这门语言的人来说,会发现它的基本代码构造和其他主流语言(例如 Java)类似。
JavaScript包含一种功能强大的事件机制,使得 JavaScript 能够响应浏览器中的用户交互。我将使用事件响应功能来构建下文的mashup。学习HTML的人已经见过以事件的形式表现的JavaScript 事件响应机制,例如下面代码中的 onclick 属性:
<οnclick="javascript:myEventHandler(); return true" href="myURL.html">My Link</a>
JavaScript 在浏览器中执行时的另一个重要功能就是能够操纵 HTML 文档对象模型(HTML Document Object Model,DOM)。该功能允许 JavaScript 代码在 HTML 页面加载后以编程方式改变页面内容。
DOM 操纵
是提高Web 2.0 应用程序交互性时使用的重要功能。在 Web 应用程序中,重置 HTML 中一个元素所包含的文本很常见,例如:
// find thetag with id 'greet_div' var div = document.getElementById('greet_div'); div.innerHTML = 'Hello ' + name;
最后,还需提到的JavaScript 功能就是向后端服务器发出带外 HTTP 请求。通过此功能, JavaScript 可以发出不会导致页面重载或改变浏览器地址栏的请求。该功能通常被称为Ajax,但是实现该功能的实际上是XMLHttpRequest,它是调用 HTTP 请求的JavaScript 类。HTTP 请求通常是异步的,这要求编程人员定义一个回调函数,在接收响应时调用。
var request = new XMLHttpRequest(); function invokeAjax() { request.open("GET", 'ajaxTarget.html', true); request.onreadystatechange = ajaxCallback; request.send(null); } function ajaxCallback() { // check if response is complete, then do stuff }
我先讨论在mashup 应用程序中使用到的 XMLHttpRequest 功能的一个主要限制,然后才能讨论该功能。
为了保护用户免受恶意代码编写者的攻击,所有浏览器都实现了一个安全功能。
Same Origin Policy
防止XMLHttpRequest以返回页面的服务器所在的网络域之外的服务器为目标发送请求。例如,如果用户浏览
http://www.bea.com/ajaxPage.html
,该页面上的 JavaScript 代码则不能将XMLHttpRequest 到
http://www.evil.com/stealCookies.html
。虽然该安全功能能够保护用户,但是它限制了 JavaScript ?客户端程序在mashup 应用程序中的作用,mashup 应用程序需要使用来自多个域的服务。然而,两种资源类型不受这一策略的限制:页面可以跨域下载图片和脚本。通过JavaScript 将参数追加到这些资源请求中,一些实现解决了这一限制。
有关 Ajax 和 XMLHttpRequest 的更多信息,请参阅
Ajax简介
(Dev2Dev中文版,2005)。
轻量级服务
调用远程服务的功能是企业分布式架构的基础,例如面向服务的架构(Service Oriented Architecture ,SOA)。Web 服务技术(例如 SOAP)被广泛应用于创建企业内的可重用服务。这些实现效果很好,但是在一些情况下SOAP 未免有些大材小用。特别在客户端是浏览器时,需要一个轻量级的解决方案。
一种构建称为 REST 的轻量级服务的方法开始流行,在Web 2.0 应用程序中这种方法应用得尤为普遍。 REST 提供了一种构建 HTTP 可寻址服务的干净模型,从浏览器可以很容易地调用该服务。对于REST 的完整学术定义不适合本教程,我总结了几个要点:
-
REST 服务表示为一个URL,通过基本的 HTTP 请求访问,例如
http://bea.com/content/getArticles?author=joe
。 - HTTP 动词很重要: GET 是读操作, POST 是创建,PUT 更新服务。
- 返回的有效负载通常是XML 或 JSON。
将 REST 的含义解释得更清楚可将会引起争议,因此以上概括已经足够。
还需要进一步解释最后一个要点。以上列出了两种流行格式作为返回的有效负载:XML 和 JSON 。似乎可以选择 XML,它在全世界应用广泛。而 REST 服务可以返回XML,客户端 JavaScript 代码需要遍历返回的 XML 的DOM ,来提取所需信息。这当然可以,但是对于浏览器中的客户端程序是JavaScript 的情况来说,还可以选择另一种格式。
JavaScript 对象标志
(
JavaScript Object Notation
,JSON)是一种 JavaScript 对象序列化格式,它减少了客户端的工作。客户端可以通过调用将返回的JSON 文本反序列化成一个本机 JavaScript 对象,然后可以使用JavaScript 语法操纵JavaScript 对象。这通常是个简单的方法,因此对于 Web 2.0 应用程序使用的 REST 服务来说,JSON 很流行。
下面是一个序列化格式的 JSON 对象示例:
{"location": {"id": "WashingtonDC", "city": "Washington DC", "venue": "Hilton Hotel, Tysons Corner", "address": "7920 Jones Branch Drive" } }
有关JSON的详细描述,请参见
JSON简介
(Dev2Dev中文版, 2007)。
Google 地图 API
Google地图和 Yahoo!地图(我们不会进一步讨论Yahoo!)均创建了可公开访问的JavaScript API,用于免费将地图嵌入随机的Web 站点。这形成了小规模的mashup Web 应用程序集,它们使用这些API来标绘地理位置,从芝加哥的
犯罪地点
到全世界的
AP 新闻发生地
。 这种流行的模式是查询针对一组地址的服务,然后使用Google Map API映射这些地址,这也是我构建mashup所使用的方法。您会看到,这种实现方法简单直白,但是有两件事需要注意。
首先,浏览器的跨域安全功能似乎不利于用JavaScript 在浏览器中实现Google 地图 mashup。这是由于用户必须导航到托管REST 服务的网络域,该服务会生成一个地址列表。在该网络域中,将不允许浏览器请求http://maps.google.com 。Google 在其JavaScript 库中实现了一种解决方案,重载对于google.com 脚本的资源请求,以便将数据注入到浏览器中。
第二件事不是技术问题,而是商业问题。Google 拥有映射数据和API ,保留定义
服务条款的权利
,Google 限制每个Web 站点的免费服务为每天50,000 请求。如果这是个问题,那么
付费服务
是没有限制的。为了实施这种使用策略,每个Web 站点都有一个惟一的API密钥,必须将其配置为可追踪站点的使用情况。获得该密钥是
免费的,而且简单容易
, 然后在HTML 页面的脚本源块中可以对它进行配置,如下所示(为简洁起见,截断了密钥):
<script src="http://maps.google.com/maps?key=ABQIAAAAT" type="text/javascript"> </script>
从 REST 数据服务检索地理位置
既然已经具备了开发所用的工具箱,那么就该开始实现Ajax 支持的Google 地图mashup 了。本节将逐步介绍如何构造一个REST 服务,该服务通过地址指示位置。在下一小节中,我们将把REST 服务连接到Google 地图来创建mashup。
关于 REST 服务,要说的第一点是它很简单。本教程的内容和演示可以作为构建更复杂的mashup 的起点。因此,其中的 Hello World mashup 尽可能简单而去掉复杂性。为此, REST 服务是一个静态服务:位置是硬编码到HTML 页面中的。尽管这看似不灵活,但用一个动态服务很容易就可以代替整个方法。精通JSP、PHP或 Ruby on Rails 的开发人员可以用从数据库、Web 服务或其他技术中寻找方法来代替静态的HTML 服务。
REST 服务是在示例的getD2DSites.html 中实现的。请看一下这个文件,您会发现它只是JSON 格式的地址对象的序列化JavaScript 数组:
{"locations": {"location":[ {"id": "WashingtonDC", "city": "Washington DC", "location": "Hilton Hotel, Tysons Corner", "address": "7920 Jones Branch Drive", "date": "May 2nd, 2007" }, {"id": "NYC", "city": "New York City", "location": "Grand Hyatt New York", "address": "109 East 42nd Street, NY 10017", "date": "May 3rd, 2007" }, etc...
客户端将使用XMLHttpRequest 从REST服务检索JSON 对象。一旦检索到该对象,JavaScript 代码将需要反序列化对象,然后遍历整个数组。看一下mapper.js,就可以看到 getLocationsAndMap 和 getLocationsAndMapCallback 函数完成了这一功能:
// Gets the current locations from the REST service // on the server and writes out the HTML that // contains links for the map function getLocationsAndMap() { if (receiveReq.readyState == 4 || receiveReq.readyState == 0) { // getD2DSites.html is a REST service // that returns the list of locations // as JSON receiveReq.open("GET", 'getD2DSites.html', true); receiveReq.onreadystatechange = getLocationsAndMapCallback; receiveReq.send(null); } // end if } // end function function getLocationsAndMapCallback() { // state == 4 is when the response is complete if (receiveReq.readyState == 4) { // Deserialize the JSON response (eval() command) // This creates an array of location objects. var response = eval("("+request.responseText+")"); // generate HTML listing the locations and update // the page DOM so the user will see the HTML var div = document.getElementById('loc_div'); div.innerHTML = '<p>Received ' + response.locations.location.length+' results.'; for(i=0;i < response.locations.location.length; i++) { var city = response.locations.location[i].city; var anchor = ''; // TODO: we will fix this later div.innerHTML += '<p><b>'+ city + '</b> ' + anchor + loc + '</a><br/>' + addr + '</p>'; } // end for loop } // end if (state == 4) } // end function
请注意, eval 调用将接收JSON 并对它进行计算,有效地构建一个可以导航的JavaScript数组。For 循环显示了如何在数组内遍历地理位置:
至此,您已经完成了这些工作:
- 创建一个静态的 REST 服务 HTML 文件
- 向HTML文件添加一个JSON 有效负载
- 编写代码通过eval()将JSON 重构为一个JavaScript 对象
- 编写代码来循环遍历地址数组,使用新的HTML操纵DOM
现在,让我们来看如何在Google 地图中显示这些位置。
使用 REST 和Google 地图 API 来组装Mashup
实现REST 服务后,就可以开始构建mashup了。图1显示了mashup 的完整格式。用户可以单击按钮来获取位置,然后单击纽约的链接。请注意Google 地图是如何以街道地址位置为中心进行显示的。
图 1. mashup应用程序实例
图 2 显示的是运行ajaxmashup.googlepages.com 时本教程的演示的架构。
图 2.演示程序的架构
第一步是定义ajaxGoogleMashup.html mashup 页面的结构。您需要三个主要结构元素:
- 允许用户调用您以上构建的REST 服务的按钮
- 保存REST 服务的占位符div 标记
- 地图的占位符div 标记
查看下面的 HTML 片断来了解这一结构:
<table> <tr> <td valign="top"> <div id="google_map_div" style="width: 500px; height: 300px"> </div> </td> <td valign="top"> <p id="getLocations_div" align="center"> <> <form id="getLocationsForm"> <input value="Get the Locations" type="submit" οnclick="javascript:getLocationsAndMap();return false" /> </form> </p> <p> <div id="locations_div"> <> </div> </p> </td> </tr> </table>
请注意,当用户按下按钮时,将触发一个onclick 事件。该事件连接到了前述getLocationsAndMap() 函数,该函数将调用针对REST 服务的XMLHttpRequest。您已经看到,getLocationsAndMapCallback() 函数之后会将服务响应从JSON 文本转换为注入到DOM 的HTML。
现在,您需要将地址组合到Google 地图中。首先,getLocationsAndMapCallback() 中的JavaScript 代码会编写HTML ,因此每个地址都会有一个调用JavaScript 函数的锚定标记。函数showAddress()和代码会传递所点击的位置地址。以上代码中getLocationsAndMapCallback()函数的 TODO注释替换为这行代码:
var anchor = '<a href="irrelevant" οnclick="javascript:showAddress(/''+ response.locations.location[i].address+'/');'+ ' return false">';
这会为每个地址创建一个锚定标记,单击地址时就会触发showAddress() 函数。
下一步,showAddress() 函数会连接到Google 地图 API,由它真正在地图中显示传递过来的地址。这是通过Google 提供的样板代码来完成的。请注意以下代码中的GMap2 和 geocoder 对象,它们都是作为Google地图API的一部分提供的。该代码将检索地图,然后更新HTML 文档的google_map_div div 标记。
function showAddress(address) { var map = new GMap2( document.getElementById("google_map_div")); var geocoder = new GClientGeocoder(); geocoder.getLatLng(address, function(point) { if (!point) { alert(address + " not found"); } else { map.setCenter(point, 13); var marker = new GMarker(point); map.addOverlay(marker); marker.openInfoWindowHtml(address); } } ); }
无论您相信与否,这样就完成了Ajax支持的Google 地图 mashup!在本教程中我还没有展示完整的文件,我展示的是重要部分。查看下载一节,获取完整代码。
Google 地图 Mashup预览
在此,直接运行示例代码将帮助您将所有的代码片断连接到一起。为了指导您如何使用代码,下面的列表突出了上文提到的重要代码片断,以及它们之间如何协作。
- 用户向浏览器请求ajaxGoogleMapsMashup.html,其结构如下 :
- 单击时调用getLocationsAndMap() 函数的 HTML 按钮
- id 为locations_div的空div 标记,地址将在此插入
- id 为google_map_div 的空 div 标记,Google 地图将被插入
- 用户单击按钮, getLocationsAndMap() 向REST 服务(getD2DSites.html)发出一个XMLHttpRequest 。
- REST 服务以JSON文本返回一个地址列表,该列表会在getLocationsAndMapCallback()中进行反序列化。
- getLocationsAndMapCallback()为在返回列表中查找到的每个地址插入 HTML,其中包含一个锚定标记。
- 用户单击一个地址的锚定标记,将触发对showAddress() 的调用,并传递位置的街道地址。
- showAddress() 从Google API调用 JavaScript 代码,它将正确的地图注入到google_map_div 元素中。
下载
可以下载本文介绍的代码:
dev2devMashupDeveleporDemo.zip
—— 代码,包含安装说明
结束语
如您所见,我将HTML、JavaScript、XMLHttpRequest、Google API和 JSON 组合在一起,创建了一个Hello World mashup 的实例。第一次这样使用这些技术,您可能会感到难以理解,但是会很快对于这些技术的组合感到满意。Web 2.0 是Web 应用程序开发的主流趋势,这种创建mashup的模式可以成功在Web 2.0项目中应用。