精通 Grails: Grails 与移动 Web

目前,几乎所有的网站都宣称 “ 使用 [Browser X] 提供最佳视图”。现代的 Ajax 库,比如 Prototype、Dojo 和 YUI,有效地缩小了 Firefox、Internet Explorer 和 Safari 之间的差距。但是使用 Nokia、Motorola 或者 Apple 手机的人可能不会喜欢浏览器的独立性。即使是最新的呼吁 “支持完整 HTML ” 的移动浏览器也可以从简单更改网络内容中获益。本文将向您展示如何优化 Grails 应用程序,使其适用于移动浏览器。

移动 Web 的使用率正在增长

据 Internet World Stats 称,目前 Internet 用户为 14 亿 — 约为全球总人口的 20%(请参阅 参考资料)。在北美洲,有 3/4 的人都是使用移动互联网。

在全球 66 亿人口当中,就有一半的人拥有手机。在北美洲,手机与 Internet 的市场占有率几乎相等,但在其他的地方则有所不同。在香港,手机的市场占有率为 140%,而在欧盟的部分国家(立陶宛,意大利和卢森堡公国)其市场占有率已高达 150%。的确,在某些地方手机比人还要多。

IDG Communications 的 Colin Crawfordsays 说到了要点(参见 参考资料):“在接下来的几年里,手机会作为上网的主要设备而逐步取代个人电脑。现在,通过手机访问互联网的比例已达到 30%,而在某些国家(比如日本)这个比例已高达 70%”。

如果您还在疑虑为什么非要使网站便于移动浏览的话,看一看 移动 Web 的使用率正在增长 边栏里面的数字就会明白了。这份全球的统计数字确实让人过目难忘,但热衷移动 Web 源于我个人的兴趣。我在 2007 年春天买了一个 iPhone,那时它刚刚上市。从那以后,我就一直在寻找可以用它来浏览的网站。当然,我可以用它访问任何的网站(只要不是基于 Flash 或者 Java™ applet 的网站,因为它不支持)。问题是,适合在分辨率为 800x600 (或更高)的显示器上显示的内容,在 3.5 英寸的屏幕上显示效果就没有那么好了。

我经常访问的那些带有 UI 的网站,因为它们符合我的手机的特定限制。我的手机会用 m 代替普通站点中传统的 URL www,这就是一个很好的起点。http://m.cnn.com、http://m.yahoo.com 和 http://m.google.com 这样的页面就能在我的手机上显示。有些网站,比如 http://www.twitter.com,则会做出相应的调整,以输出合适的内容:在电脑上浏览,我可以获得全部功能;而在手机上浏览时,则删剪了一些内容,使它刚好适合我的屏幕。我将向您展示如何实现不改变 URL,但提供最佳 UI。

针对移动 Web 开发人员的技术

作为一个 Java 开发人员,我已经被 “只写一次,到处运行(Write Once,Run Anywhere)” 的承诺给宠坏了。我甚至从来没有考虑过优化 Java 应用程序使其适合某一特定的操作系统或硬件型号。但是如果是要开发移动 Web 的话,就应该熟悉三种支持不同型号移动设备的主要技术:

  • 无线标记语言(Wireless Markup Language 1.x,WML 1.x)
  • WML 2.x 或 Mobile Profile(XHTML-MP)
  • 针对 iPhone 的 HTML 标记

正如我展示的一样,您可以将 WML 和 XHTML-MP 标记与用 Grails 构建的 Groovy Server Pages(GSPs)结合起来,以生成便于移动的页面。此外,我还会向您展示如何修改 Grails 生成的 HTML,使页面在 iPhone 上显示得更好。

结合使用 WML 1.x 和 Grails

WML 是一种类似于 HTML 的标记语言,但它并不是真正的 HTML(WML 1.0 于 1998 年标准化。WML 1.3 为最新版本)。WML 并无法在 Web 浏览器中查看(至少不借助于仿真器是不行的),同样您也无法在 WML 浏览器中查看 HTML。手机供应商通常都会提供在后台实现 HTML 与 WML 之间相互转换的网关。

关于本系列

Grails 是一种新型 Web 开发框架,它将常见的 Spring 和 Hibernate 等 Java 技术与当前流行的实践(比如约定优于配置)相结合。在加入脚本语言的灵活性和动态性的同时,用 Groovy 编写的 Grails 可以无缝地集成遗留的 Java 代码。学习完 Grails 之后,您将改变看待 Web 开发的方式。

WML 通过无线访问协议(Wireless Access Protocol,WAP)传输,这和 HTML 通过 HTTP 传输很相似。WAPWML 在临时对话中通常可以相互转换:手机说明书总是会夸耀该手机带有 WAP 浏览器,或者支持 WML 1.x(参阅 参考资料 获得 WML 和 WAP 规范的官方链接)。

如果您针对的是 Research in Motion 的 BlackBerry 用户的话,就得提高关于 WML 的知识了。(BlackBerry 大约占了智能手机市场的 40%,iPhones 和 Windows® Mobile 居于第二和第三位)。虽然很多技术过硬的用户也可以下载真正的 Web 浏览器,比如 Opera Mini(参见 参考资料),但是 BlackBerry 智能手机还是配备了 WAP 浏览器。

继续设计旅行计划

如果您一直都在关注 精通 Grails 系列文章的话,那么您可以修改已经熟悉的旅行计划应用程序,使它便于在手机上浏览。在旅行计划应用程序的 web-app 目录中创建一个文件,命名为 testwml.gsp,并输入清单 1 中的静态 WML:


清单 1. 静态 WML

                
<% response.setContentType("text/vnd.wap.wml") %>
<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//PHONE.COM//DTD WML 1.1//EN"
   "http://www.phone.com/dtd/wml11.dtd" >

<wml>
  <card id="f1" title="Flight 1">
    <p mode="wrap">From: DEN</p>
    <p mode="wrap">To: ORD</p>
    <p mode="wrap">UAL 1234</p>
    <p mode="wrap">Jun 30, 10:30am</p>
    <p>
      <anchor>Next<go href="#f2"/></anchor>
    </p>  
  </card>

  <card id="f2" title="Flight 2">
    <p mode="wrap">From: ORD</p>
    <p mode="wrap">To: DEN</p>
    <p mode="wrap">UAL 9876</p>
    <p mode="wrap">Jul 02, 1:15pm</p>
    <p>
      <anchor>Previous<go href="#f1"/></anchor>
    </p>  
  </card>
</wml>

您用手机访问 http://www.davisworld.org/testwml.gsp 同样可以看到这个页面。您可能习惯于在 GSP 中查看 HTML。但在这里使用的是 WML。

当从 GSP 中发送出 WML 时,切记要将默认的 MIME 类型 text/html 替换为 text/vnd.wap.wml,如清单 1 中的第一行所示。如果直接提供静态 WML,那么只需赋予文件一个 WML 扩展名,而不是 GSP 扩展名。大多数 Web 服务器会在这之后自动返回一个正确的 MIME 类型,无需调用 response.setContentType。在 $TOMCAT_HOME/conf/web.xml 中,您会发现 MIME 针对 WML 文件的映射已经就位。如果您使用的是 Apache HTTPD 的话,那么您可以在 $APACHE_HOME/conf/mime.types 文件中找到类似的 WML 文件的映射。清单 2 向展示了 Tomcat 的 MIME 类型映射:


清单 2. 在 Tomcat 中设置 MIME 类型

                
<mime-mapping>
  <!-- WML Source -->
  <extension>wml</extension>
  <mime-type>text/vnd.wap.wml</mime-type>
</mime-mapping>

回过头来在看一下 清单 1,接下来需要注意的是 DOCTYPE。包含文档类型定义(Document Type Definition,DTD)语句可以帮助将 WML 文档识别为 testwml.gsp。

注意该文件并未打包在常见的 <html> 标记中。它的开头和结尾为 <wml>。您可能还注意到 清单 1 中缺少 <head><body> 部分。每一个 WML 页面为一个 card,拥有一个独立的 id 属性和便于用户使用的 title 属性。

通常情况下,在一个单一文件中可以下载多个页面/卡片。早期的手机的数据通道非常狭窄,而这种方法刚好就缓和了这些设备及其网络的局限性。一次下载得越多,手机与服务器之间的数据转就越少。因为一次只能查看一个页面,这样就可以有效地提前获取其余的页面。对于这种情况,导航只发生在客户端。

HTML 开发人员一定要熟悉 <p> 标记。WML <anchor> 标记在本质上与 HTML <a> 标记是类似的,即使它们在语法上有所不同(参阅 参考资料 了解更多关于 WML 的信息)。

下面是 WML 的一个小技巧。由于处理的是专用于手机的内容,因此可以创建一个超链接,用户一旦选定链接,就可以拨出电话。清单 3 的中例子可以拨出电话号码 303-555-1212 :


清单 3. WML 拨号链接

                
<do type="accept">
 <go href="wtai://wp/mc;3035551212"/>
</do> 

注意该链接使用的协议并非常见的 http:// — 而是 wtai://,这是无线电话应用程序界面(Wireless Telephony Applications Interface)的缩写。

WML 仿真器

要使这个页面在个人电脑上显示,则需要一个 WAP 仿真器(参阅 参考资料,查看文中提到的所有仿真器的链接)。访问 dotMobi 仿真器,它是一个 Java applet。输入 URL davisworld.org/testwml.gsp(注意 http:// 前缀已提供在输入框的左侧),您将看到类似于图 1 的内容:


图 1. 仿真 WAP 页面

注意 dotMobi 仿真器有两种不同的皮肤,它不仅代表着不同设备的外观和感觉,还代表设备的不同功能。如果对仿真某个设备感兴趣的话,它的硬件制造商通常会提供一个开发者网站,您可以从哪里下载安装所需的仿真器。

从 GSP 发送静态 WML

第一个 WML 例子为静态代码。清单 4 是一个使用常见的 <g:each><g:if> 标记的例子:


清单 4. 结合 GSP 和 WML

                
<% response.setContentType("text/vnd.wap.wml") %>
<%
def flightList = []
flightList << [iata1:"DEN", iata2:"ORD"]
flightList << [iata1:"ORD", iata2:"DEN"]
%>

<?xml version="1.0"?>
<!DOCTYPE wml PUBLIC "-//PHONE.COM//DTD WML 1.1//EN"
   "http://www.phone.com/dtd/wml11.dtd" >

<wml>
  <g:each in="${flightList}" var="${flight}" status="i">
    <card id="f${i}" title="Flight ${i}">
      <p mode="wrap">From: ${flight.iata1}</p>
      <p mode="wrap">To: ${flight.iata2}</p>      
      <g:if test="${flightList.size() > i+1}">
        <p>
          <anchor>Next<go href="#f${i}"/></anchor>
        </p>  
      </g:if>  
    </card>
  </g:each>  
</wml>

注意我仅仅模仿了 HashMap 中的一些机载数据,而不是设置完整的 MVC 基础设施。重要的是它能保证了 GSP 标记与 WML 的结合,就像我在 “精通 Grails: 用 Groovy 服务器页面(GSP)改变视图” 中处理 JavaScript 一样(可以在 http://davisworld.org/testwml2.gsp. 中查看到这个页面的示例)。

WML 1.x:一个时代的终结

虽然常有人断言 WML 的生命快到头了,但仅支持 WML 的手机仍在流通。没错!— WML 1.x 正在淡出江湖。越来越多的现代手机开始避免这种 “分离但平等的” WML 平台,转而使用真正的 Web 浏览器。正如下一节所演示的一样,要为 WML 2.x 设备或 iPhones 创建一个便于移动的浏览网站,只需在现存的 HTML 上做些变动,而不是将其转换成完全不同的标记语言。

结合使用 Grails 与 WML 2.X(或 XHTML-MP)

提到 WML 2.x,WML 更像是一个品牌的名称,而不是一个独立的标记语言(WML 1.x 才是)。事实上,WML 2.x 只不过是 XHTML 的一个方言:明确地说是 XHTML-MP。

XHTML-MP 严格要求创建格式良好的 XML。这就意味必须正确地关闭每一个容器标记(<p></p><li></li>),属性前后要用引号(<a href="http://somewhere.com">),并且元素名称只能用小写字母(<h1> 而不是 <H1>)。

XHTML-MP 是 XHTML-Basic 的一个超集。只要稍作调整,您的网站就遵循 XHTML-Basic 规范。XHTML-MP 不可以使用嵌套的表格或框架。它只支持 gif 和 png 图像格式。至于其他的最佳实践(比如指定图像大小和替换文本)则是 XHTML-Basic 的要求。很多常见的 HTML 标记(虽然不是全部)都可以找到。参阅 参考资料 获得可用于 XHTML-Basic 和 XHTML-MP 的标记的列表链接。

要优化网站使其适合较小屏幕,就必须减少针对每个请求发回的数据。Web 网页(包括 HTML、CSS 和图像)最好小于 20KB。并且要使用 Expires 或者 Cache-Control header 来主动缓冲文件。当为移动设备提供内容时,要将网页分割为 2 到 3 页。http://m.cnn.com 在这方面做的就很好,它可以将整篇文章分成 3 到 4 页显示,但也提供了 “整篇文章” 的链接,如果您不介意额外开销的话。

就像使用 WML 1.x 一样,必须在文件的开始包含正确的 DTD。同时还要修改 <html> 标记,使其包含 xmlns 属性,如清单 5 所示:


清单 5. 启动 XHTML-MP 文件

                
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"
"http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
...
</html> 

虽然很多移动设备也接受更加通用的 MIME 类型 application/xhtml+xml ,但您仍然要用适当的 MIME 类型 application/vnd.wap.xhtml+xml 将其发送。application/xhtml+xml 可以帮助您在标准桌面浏览器中调试代码。

查看 XHTML-MP

访问 http://m.yahoo.com 可以查看 XHTML-MP。(虽然它在 Web 浏览其中看起来很简朴,但在手机上的效果却很好)。选择 View > Source,就会在文档顶端看见 XHTML-MP DTD 了。

想要更深入地了解这样的网站在真机上的效果到底如何,您还需要找到另外一个仿真器。例如,Sandip Chitale 的博客提供了 Firefox 插件,它看来真的很象一个 iPhone(参阅 参考资料)。注意,这个仿真器要比真机大一些,但是它显示的网站效果跟在 iPhone 上显示的效果非常接近。(我将指出一些更精确的验证器)。图 2 展示了用 Chitale 的仿真器仿真的 m.yahoo.com:


图 2. 使用 iPhone 仿真器查看 Yahoo 移动 web

验证 XHTML-MP

有几个在线验证器可以确保您发出的 XHTML-MP 是格式良好的。您可以尝试一下 W3C mobileOK Basic Checker 或 ready.mobi 测试工具(参阅 参考资料)。二者都很好,但是 ready.mobi 的仿真器提供的信息要比 W3C 多得多。

例如,图 3 展示了 W3C 验证器所提供的关于 http://m.google.com 的信息:


图 3. W3C 验证器提供的有关 Google 的移动 web 的报告

图 4 为 ready.mobi 工具提供的关于 http://m.yahoo.com 的报告的前一部分:


图 4. ready.mobi 提供的关于 Yahoo 的移动 web 的报告

它显示了 Yahoo! 的 4/5。再往下拉一点点,您就可以看到很多不同的可视化器,使您可以看到网页的真正显示效果。图 5 展示了它在 Nokia N70 的效果:


图 5. 使用仿真器查看 Yahoo 的移动 web

在页面的底部,ready.mobi 验证器展示了一组详细的测试结果,每一个结果都带有这样的标记:通过(绿色)、失败(红色)或警告(黄色)。例如,即使 http://m.yahoo.com 好像在各种设备上都显示得不错,其 XHTML 也不是 100% 遵从的,如图 6 所示:


图 6. 查看 XHTML-MP 遵从性错误

再往下看,如图 7 所示,您会看到 Yahoo! 在图像上遗漏了一些 alt 属性,而且在某些情况下没有指定图像的大小:


图 7. 查看具体的错误

Grails 与 XHTML-MP

那么,Grails 已经可以直接用于开发移动 Web 了吗?图 8 展示了 ready.mobi 验证器提供的关于旅行计划应用程序的原始清单页面的信息:


图 8. Grails 没有直接遵从 XHTML-MP

那就是说,还需要做一些工作。首先,在 grails-app/controllers/AirportController.groovy 中创建一个 mlist 闭包。除了会返回 5 个(而不是 10 个)元素外,它与默认的闭包没什么不同。创建一个单独的闭包保留,您就可以原样保持 list.gsp,以进行比较,如清单 6 所示:


清单 6. AirportController 中的新闭包

                
def mlist = {
  if(!params.max) params.max = 5
  [airportList:Airport.list(params)]
}

现在将 grails-app/views/airport/list.gsp 复制到 mlist.gsp。(一会儿,我将提供一些策略,从而将移动用户无缝地重定向到正确的内容。这个强有力的方法目前还能满足需求)。

验证器指出网站未返回 XHTML-MP。编辑 mlist.gsp 使它在 <html> 标记中包含有必要的 DTD 和 xmlns 属性。您还需要禁用 meta 标记,因为它会自动将内容类型设置为 text/html。最后一步:将 grails-app/views/layout/main.gsp 中包含 CSS 的行复制到该文件中。(SiteMesh — Grails 使用的模板库 — 配置为只在默认情况下修饰 text/html 文件)。清单 7 展示了 mlist.gsp:


清单 7. 将 GSP 页面转变成 XHTML-MP

                
<% response.setContentType("application/xhtml+xml")%>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" 
        "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
  <head>
    <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
    <link rel="stylesheet" href="${createLinkTo(dir:&apos;css&apos;,file:&apos;main.css&apos;)}" />
    <meta name="layout" content="main" />
    <title>Airport List</title>
  </head>
  ...
</html>

在编辑文件时,也可以简化表布局。验证器指出 <thead><tbody> 标记有误,所以需要将其删除。由于手机屏幕的纵向空间要比横向空间大,所以清单 8 所示的布局看起来会好一些:


清单 8. 简化表

                
<table>
  <tr>
    <g:sortableColumn property="id" title="Id" />
    <g:sortableColumn property="name" title="Name" />
  </tr>
  <g:each in="${airportList}" status="i" var="airport">
  <tr class="${(i % 2) == 0 ? &apos;odd&apos; : &apos;even&apos;}">
    <td>
      <g:link action="show" 
              id="${airport.id}">${airport.id?.encodeAsHTML()}
      </g:link>
    </td>
    <td>${airport.iata?.encodeAsHTML()}<br/>
        ${airport.name?.encodeAsHTML()}
    </td>
  </tr>
  </g:each>
</table>

图 9 展示了新页面在 iPhone 仿真器中的显示效果:


图 9. 为 iPhone 定制的列表页面

图 10 展示了此时 ready.mobi 验证器所提供的信息:


图 10. 通过验证的列表页面

这就好多了!而且针对验证器的必要更改是很少的。回顾 “精通 Grails: 用 Groovy 服务器页面(GSP)改变视图”,可以通过输入 grails install-templates 来相应地更改默认模板。

为 iPhone 开发页面

iPhone 可能就是三种类型的设备中最容易支持的设备。它页面的开发与普通的 Web 页面开发完全相同。iPhone 上的 Safari 浏览器与桌面浏览器的代码库完全相同,因此,用户在两者中所看到的东西是一样的。但是您可以去掉一些显示提示,因为通过 iPhone 查看网站时它们会影响外观和感觉。

例如,一个 iPhone 的屏幕尺寸为 320x480,但有趣的是,浏览器设置的网页的默认宽度为 980 像素。这使文本在手机的景色模式下不能读取,并且在肖像模式下会变得很小。但是不用担心,使用一个简单的只有 iPhone 才能识别的 meta 标记,就可以将网页校正到 “正确的尺寸” 了:viewport 标记允许为移动 Safari 浏览器添加提示。清单 9 中的代码就在很大程度上提高了在 iPhone 上查看的网页的可读性。(不幸的是,基于 Firefox 的 iPhone 仿真器无法识别这个 meta 标记。只有在真正的 iPhone 上才可以看到它的实际运行)。


清单 9. 为 iPhone 设置 viewport

                
<meta name="viewport" content="initial-scale=1.0" />

inital-scale 的范围为 0 到 10,且支持分数值。也可以输入显式的 widthheight 值,上限为 10,000 像素(如清单 10 所示):


清单 10. 为 viewport 设置 widthheight

                
<meta name="viewport" content="width=600;height=400" />

iPhone 上的超链接

说到超链接,iPhone 提供了一些特殊的性能。如果使用 tel: 前缀代替 http://,单击链接就可以拨出一个电话号码,如清单 11 所示:


清单 11. iPhone 上可用于拨号的链接

                
<p>
telephone number: 
<a href="tel:303-555-1212">303-555-1212</a>
</p>

如果使用传统的 mailto: 前缀的话,单击链接就会运行邮件应用程序,如清单 12 所示:


清单 12. 邮件链接

                
<p>
mail: 
<a href="mailto:scott@aboutgroovy.com">Scott Davis</a>
</p>

如果您为 Google Map 提供一个链接,单击链接就会运行本地 Google Maps 应用程序,而不是将其转交到 Safari 中,如清单 13 所示:


清单 13. Google Map 链接

                
<p>
local google maps:
<a href="http://maps.google.com/maps?q=denver+international+airport">DEN</a>
</p>

输入一个起点和终点,单击链接,它就会为用户提供驾驶方向,如清单 14 所示:


清单 14. Google Map 驾驶方向

                
<p>
driving directions:
<a href="http://maps.google.com/maps?daddr=
    denver+airport&saddr=coors+field+denver,+co">Directions</a>
</p>

移动 Web 开发策略

您已经知道为三种基本设备创建内容都需要什么工具,现在的任务是如何根据需要使用它们。有三种基本的策略可供选择。

为移动内容创建独立的、专用的网站

正如前面看到的一样,m 是许多 Web 站点所采用的策略。Google、Yahoo! 以及 CNN,都设置了一个 m 域,它独立于主站点,用于提供移动内容。如果改动域名系统(Domain Name System,DNS)的话,可以创建一个类似 http://mysite.org/mobile 的 URL。您也可以注册一个移动内容专用的 .mobi 域。

监听用户代理

每一个 Web 浏览器在请求数据时都向服务器表明身份。可以利用这个信息来提供为设备定制的内容。(http://twitter.com 使用的就是这种技术)。

访问 http://davisworld.org/echo.gsp。清单 15 中的页面只用了个简单的循环,就回应了 HTTP 的请求:


清单 15. 显示 Request Header

                
<h2>Request Headers</h2>
<table border="1">
  <tr>
    <th>Header</th>
    <th>Value</th>
  </tr>
  
  <g:each in="${request.headerNames}" var="${name}">
    <tr>
      <td>${name}</td>
      <td>${request.getHeader(name)}</td>
    </tr>
  </g:each>
</table>

正如您在图 11 中所看到的,当我打开 http://davisworld.org/echo.gsp 时,Firefox 浏览器提供了足够身份提示:


图 11. 查看 HTTP header

根据图 11 中展示的 user-agent 字符串,就可以断定请求程序通过 Intel CPU 运行 Mac 系统。对于 OS(10.5)、HTML 呈现引擎(Gecko)、和真实浏览器(Firefox)的版本,您已经很熟悉了。清单 17 展示了其他常见的用户-代理字符串:


清单 17. 常见的用户-代理字符串

                
BlackBerry7520/4.0.0 Profile/MIDP-2.0 Configuration/CLDC-1.1 
    UP.Browser/5.0.3.3 UP.Link/5.1.2.12

Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) 
    AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3

Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)

通过捕获 request.getHeader("user-agent") 值为请求浏览器提供适当的内容。

发回浏览器接受的内容

第三种策略就是满足浏览器的所有请求。每一个请求都会包含一个 accept 值和一个 user-agent 值。Firefox 返回的 accept 值如下:

text/html,application/xhtml+xml,application/xml;

这告诉服务器 Firefox 偏好 text/html。如果服务器不包含 text/html 数据,那么它可以发送 application/xhtml+xml。如果两者都没有的话,服务器会在列表中逐个查找,直到找到一个可以返回的 MIME 类型。

WAP 1.x 浏览器需要 text/vnd.wap.wml,更现代的手机会需要 application/vnd.wap.xhtml+xml。只要多加注意,聪明的开发者便可以返回适当的数据。

当然,这三种策略并不是相互排斥的。您可以全部选用,确保您的网站已经准备好为全球 33 亿的手机用户提供服务了。


结束语

让 Grails 应用程序便于手机使用的方法有很多种,可以不做任何改动(对于 iPhone 而言)、做很小的改动(对于 XHTML-MP 设备而言)、或者全部重写(对于 WML 1.x 设备而言)。借助文中所介绍的这些仿真器和验证器,您一定可以实现顺利支持移动 Web。

在下一篇文章中,您将会学习如何处理 Grails 中的遗留数据库。您将学习 Mapping DSL、使用 Hibernate 注释和 HBM 文件。学会了这些技术,您就可以让 Grails 使用现有的表和字段名,即使它们不符合 Grails 标准命名规定。到时候就尽情享受精通 Grails 的带来的乐趣了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值