领先技术

在 ASP.NET MVC 4 中创建为移动设备优化的视图,第 2 部分: 使用 WURFL

Dino Esposito

Dino Esposito对十年前浏览器大战的久远记忆至今仍使开发人员心有余悸。 jQuery 之所以获得普遍成功的一个原因是,它能够隐藏跨浏览器的文档对象模型 (DOM) 和 JavaScript 实现方面存在的细微差异。 在人们看来,开发人员目前可能会重点关注整体功能,而忽略特定的浏览器功能。 真是这样吗? 对于桌面浏览器来说是这样 - 而对于移动浏览器来说,情况会好一点。

与桌面浏览器在十年前构成的领域相比,移动浏览器构成的领域分化程度更高。 需要考虑的各种桌面浏览器仅有数十款;而各种移动浏览器则数以千计。 设备外形因素可能会朝着两个方向发展 - 一个方向是变得更小,就像智能手机和迷你平板电脑一样;另一个方向是变得更大,如智能电视。 当前和将来获得成功的关键在于,让每类设备的用户都获得适当的体验。 为此,您需要依次检测设备及其功能。

历史悠久但优秀可靠的浏览器功能数据库

自 1.0 版开始,ASP.NET 便已提供浏览器功能数据库。 可以查询 HttpRequest 对象公开的 Browser 属性来了解所请求浏览器的一小部分功能。 浏览器功能基础架构的原理相当简单、有效。 在生成 HttpRequest 对象时,ASP.NET 运行时将获取用户代理字符串,并通过 ASP.NET 浏览器数据库运行该字符串。 通过将用户代理字符串用作关键字,它将检索已知功能的列表,并将这些值存储到开发人员通过 Request.Browser 访问的 HttpBrowserCapabilities 对象中。 在我看来,这个功能是 ASP.NET 独有的,任何其他 Web 开发平台都没有与之类似的功能。

在您深入了解浏览器功能的基础结构时,会清楚地意识到此框架存在相关的缺陷: 为了保持有效性,存储浏览器功能的数据库必须随着新的设备和浏览器版本上市不断更新。

Microsoft 设计浏览器功能框架的初衷是为了帮助将一些桌面浏览器区分开来。 移动设备的出现彻底改变了问题的严重程度。 Microsoft 曾在一段时间内为移动设备浏览器文件 (MDBF) 项目 (mdbf.codeplex.com) 提供支持,旨在为单个移动设备和浏览器创建丰富的功能定义数据库。 当时的想法是:人们只需将利用 ASP.NET 获取的标准 .browser 文件替换为 MDBF 数据库,即可通过 Request.Browser 访问详细的浏览器和移动设备信息。 不过,该项目在几年前被终止, 但这个基本想法依然相当有效,并且它可能是为若干类设备提供高度优化的定制内容的最有效方式。 我将探讨如何使用 ASP.NET MVC 4 做到这一点。 在接下来的示例中,有关设备的任何信息均由 wurfl.sourceforge.net 上的无线通用资源文件 (WURFL) 提供。 此外,WURFL 是指许多大型组织(包括 Google 和 Facebook)所使用的设备描述存储库 (DDR)。 您可以阅读我之前撰写的一系列有关移动站点开发的专栏文章,了解有关 WURFL 和 DDR 的详细信息,首先可以读一下“移动站点开发: 标记”(msdn.microsoft.com/magazine/jj133814)。

ASP.NET MVC 4 的移动外观

在 ASP.NET MVC 4 中,通过从 global.asax 中调用图 1 中的代码,您便为呈现同一 Razor 视图的最多三种不同的表示形式做好了准备。

图 1 如何使 ASP.NET MVC 4 支持同一 Razor 视图的最多三种显示模式的说明

public class DisplayConfig
{
  public static void RegisterDisplayModes(
    IList<IDisplayMode> displayModes)
  {
    var modeSmartphone = new DefaultDisplayMode("smart")
    {
      ContextCondition = (c => c.Request.IsSmartphone())
   };
   var modeTablet = new DefaultDisplayMode("tablet")
   {
     ContextCondition = (c => c.Request.IsTablet())
   };
   var modeDesktop = new DefaultDisplayMode("")
   {
     ContextCondition = (c => c.Request.IsDesktop())
   };
   displayModes.Clear();
   displayModes.Add(modeSmartphone);
   displayModes.Add(modeTablet);
   displayModes.Add(modeDesktop);
  }
}

RegisterDisplayModes 指示 ASP.NET MVC 4 运行时考虑每个视图的三种不同显示模式。 这意味着,每当控制器调用视图时,视图的实际名称(例如“index”)将由显示模式提供程序检查,并且这些名称在确认为智能手机或平板电脑定义的上下文条件后将更改为 index.smart 或 index.tablet。 显示模式对象插入到提供程序中采用的顺序是关键。 实际上,搜索会在找到第一个匹配项后停止。 例如,假设 HTTP 请求生成了下列代码:

public ActionResult Index(){  return View();}

在解析视图名称时,ASP.NET MVC 4 将查看显示模式集合,并首先检查智能手机的显示模式。 智能手机的上下文条件将返回 true 或 false,具体取决于 IsSmartphone 的实现。 如果为 true,则将选择后缀为“smart”的视图(如果有)。 否则,将继续搜索下一个可用的显示模式。

与决定用于确定哪些请求属于哪些显示模式的条件一样,显示模式的数目也完全由您确定。

要将请求映射到显示模式,您需要执行一些设备检测工作。 不过,您将不会获得每个可能设备的视图,而是获得您打算在应用程序中支持的每种类别的设备的视图。

使设备检测智能化

归根结底,设备检测将与探查用户代理字符串有关。 用户代理字符串不是一门精密科学,它可能需要大量分析和标准化工作易于掌握和映射到一系列功能。 此类数据库的维护成本很高,因为您需要查看每个新的浏览器版本和设备版本以及由 OEM 进行的 OS 自定义设置。 此外,对于标识的每个唯一设备,您应了解其功能并将这些功能有效地存储到数据库中。

有几家公司在这个行业内很活跃,它们根据不同的模型来销售其产品,其中包括相当便宜的基于云的解决方案。 用于设备检测的最常见框架为前面提及的 WURFL - 一个具有各种语言版本的跨平台库,根据 Affero 通用公共许可协议 (AGPL) 的规定,还是一种开放源。 对于 ASP.NET MVC 4 开发人员,WURFL 还以 NuGet 包的形式提供。 在 Microsoft .NET Framework 领域,51Degrees 数据库 (51degrees.mobi) 提供了另一种选择。 在下一节中,我将说明如何在 ASP.NET MVC 4 应用程序中使用设备检测框架路由显示模式。

将 WURFL 添加到显示模式

图 1 中的代码基本集中在 Context­Condition 委托上:

Boolean ContextCondition(HttpContextBase)

签名的作用很容易理解: 它传递 HTTP 上下文并需要布尔值答复。 委托中的逻辑应使用 HTTP 上下文中的任何信息,并确定是否可按照显示模式的预期为请求提供一个视图。 作为示例,我将从上一个专栏“在 ASP.NET MVC 4 中创建为移动设备优化的视图”(msdn.microsoft.com/magazine/dn296507) 的结束处说起。 图 1 中使用的 IsTablet 方法是已添加到 HttpRequestBase 类的扩展方法。 以下是其代码:

public static class HttpRequestBaseExtensions
{
  public static Boolean IsTablet(this HttpRequestBase request)
  {
    var ua = userAgent.ToLower();
    return ua.Contains("ipad") || ua.Contains("gt-");
  }
}

移动设备的飞速发展使得我们更加难以找到并保持平板电脑或智能手机的静态定义。 几乎很难依据某项物理特性(例如,设备是否支持 Flash 视频 (.flv) 流或内联图像)来判定设备是否为平板电脑或智能手机。 此类设备更像是虚拟功能的集合,其定义完全由开发团队确定。 通常,虚拟功能以多个物理功能的逻辑组合形式实现。

要将 WURFL 添加到 ASP.NET MVC 4 代码中,只需调用 NuGet 并获得正式的 WURFL API 即可。 软件包会将 WURFL 数据库(一个压缩文件)安装到 App_Data 文件夹中。 数据库是一个相对较新的设备信息快照;若要每周获取更新,您需要从 ScientiaMobile (scientiamobile.com) 处购买商业许可证。

在 WURFL 安装就绪后,将以下行添加到 Application_Start:

WURFLManagerBuilder.Build(new ApplicationConfigurer());

这会将数据库加载到内存中,并确保缓存所有数据以便以可能的最快速度访问。 要运行 WURFL 查询,您需要以下必须在 HTML 视图的每个请求中运行的代码:

var userAgent = ...; // Typically read from Request object
var device = WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);

同样,代码也是具有自我描述性。 WURFL 框架获取用户代理并返回(以毫秒计)其了解的所有设备相关信息。 此类信息以名称/值集合的形式表示,其中功能的名称及其返回的值都为字符串。 您负责将字符串转换为强类型数据(例如,整数或布尔值)。 WURFL 为每个设备提供了 500 多项功能,并且已将这些功能划分为多个类别。 显然,您并非对所有功能都感兴趣。 我认为,更实际的情况是您对其中十分之一的功能不感兴趣,这也和现已终止的 MDBF 项目所支持的功能数一致。 不管怎样,该数字大大超出了您使用 CSS3 媒体查询可检查的功能数。 媒体查询共有五个属性,其中仅一两个属性(宽度和方向)常被使用。 以下是使用 WURFL 可靠检查平板电脑的方式:

public static class HttpRequestBaseExtensions
{
  public static Boolean IsTablet(this HttpRequestBase request)
  {
    var device =
       WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
   return device.IsTablet();
  }
}

IsTablet 是供我刚创建的 WURFL IDevice 类型用来克服 WURFL 的弱类型化性质的扩展方法:

public static Boolean IsTablet(this IDevice device){
  return device.GetCapability("is_tablet").ToBool();
}

请注意,ToBool 是另一个扩展方法,它仅封装对 Boolean.TryParse 的调用。

使代码保持清晰而简洁的扩展方法的实用性在检测智能手机的示例代码中表现得更加明显,如图 2 所示。 对于智能手机来说,可能不存在所有人都接受的通用定义。 通过组合多个 WURFL 属性,您可以创建自己的智能手机定义,如图 2 所示。

图 2 将智能手机定义为虚拟功能

public static class HttpRequestBaseExtensions
{
  public static Boolean IsSmartphone(this HttpRequestBase request)
  {
    var device =
      WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
    return device.IsWireless() && !device.IsTablet() &&
      device.IsTouch() &&
      device.Width() > 320 &&
      (device.HasOs("android", new Version(2, 2)) ||
      device.HasOs("iphone os", new Version(3, 2)) ||
      device.HasOs("windows phone os", new Version(7, 1)) ||
      device.HasOs("rim os", new Version(6, 0)));
  }
}

图 2 中的代码将智能手机定义为一种具有以下特点的无线设备:不是平板电脑、支持触控、至少为 320 像素宽且运行任何指定的 OSes。 对设备变量使用的所有方法都是基于 WURFL 本机功能构建的扩展方法。

服务器端检测和客户端响应式设计

在网站中使用高级 HTML5 和 CSS3 功能的目的是导致业内某些领域关注客户端功能检测的原因。 但是,这与使站点适合移动特性没有多大关系。 客户端功能检测仅适用于浏览器允许检测的内容 - 充其量包括通过媒体查询获得访问权限的五个属性及可以编程方式测试的任何内容。 若要将 Android 设备与 iPhone 区分开来,您仍需要用户代理探查 - 不用说,运行 Android 2.2 无法提供有关实际设备功能的较多信息。

如果您确实需要为设备(小型和大型)提供临时标记,则服务器端设备和功能检测是唯一选择。 可使用 WURFL 轻松快速地做到这一点。

服务器端检测和客户端响应式设计并不是非此即彼的选择。 服务器端检测仅与标识设备和相关显示模式有关。 您提供的标记可轻松包含媒体查询、流式布局以及任何其他帮助优化结果的内容。 如果您喜欢使用首字母缩写词,则可使用 RESS(代表响应式设计 + 服务器端组件)来概括上述内容。 服务器端检测只是在生成定制视图的过程中增加一个额外的抽象层次。

Dino Esposito是“Architecting Mobile Solutions for the Enterprise”(为企业构建移动解决方案)(Microsoft Press,2012 年)和 Microsoft Press 即将出版的“Programming ASP.NET MVC 5”(ASP.NET MVC 5 编程)的作者。 作为 JetBrains 的 .NET 和 Android 平台的技术推广人员,Esposito 经常在全球行业活动上发表演讲,并且在 software2cents.wordpress.com 上以及 Twitter.com/despos 上的推文中分享其对于软件的愿景。

衷心感谢以下技术专家对本文的审阅: Mani Subramanian (Microsoft)