领先技术

在 ASP.NET MVC 4 中创建为移动设备优化的视图

Dino Esposito

Dino Esposito如果深入探讨有关编写移动设备网站的常识性考虑因素,会发现其中有一种内在矛盾。一方面,客户在其编写应用程序和网站的方法中强烈要求(或乐于要求)移动优先。另一方面,同一些人又经常称赞 CSS 媒体查询和流体布局。我所发现的矛盾在于经常利用 CSS 媒体查询和流体布局并未在其他内容之前优先处理移动方面,它不是一种移动优先的方法。在本文中,我将介绍如何使用服务器端逻辑为给定设备呈现最佳的显示效果,并介绍 ASP.NET MVC 4 的一种新功能,称为显示模式。

问题不在于 CSS 媒体查询作为一种技术。问题甚至也不在于自适应 Web 设计 (RWD) 作为 CSS 媒体查询的支持方法 — 虽然不是这项技术启发性的宗旨所在。那么,怎样将使用 CSS 媒体查询和流体布局变为一种“移动优先”的方法?在用于推广此方法的口号中即可发现端倪: 一个基本代码可为多个视图服务。在这个角度上,使用 CSS(一种客户端技术)在视图之间切换,而使用 JavaScript 在 CSS 无法胜任时进一步调整视图。

在我看来,此方法中的基本做法是为所有设备提供相同内容,只调整页面布局以适合屏幕大小。因此,可能无法向用户提供最佳的体验。我认为,您应在合理的情况下力求仅有一个基本代码(Web API 的共同基础),但一定要重点关注要支持的每类设备的具体使用情况。“移动”一词如今已变得意义狭窄,被智能手机、平板电脑、笔记本电脑和智能电视等多种类型的设备取代,更不用说眼镜显示器和智能手表等可穿戴设备。

大约一年前,我在这个专栏中展示了一种在开发 ASP.NET MVC 网站时采用的服务器端方法: 用于为所支持的每类设备创建临时视图(“移动站点开发:标记”msdn.microsoft.com/magazine/jj133814)。我当时在 ASP.NET MVC 3 的环境中这样做。ASP.NET MVC 4 完全胜任这项任务,它具有前面提到的显示模式,可使用它轻松实现为给定设备提供最佳视图和内容的服务器端逻辑。为了切实有效,此方法要求您尽可能多地了解请求设备的功能。但是,除了有关屏幕大小和当前方向的基本信息外,在客户端上检测不到其他内容。然后,需要采用设备信息的服务器存储库。

在 ASP.NET MVC 4 中引入显示模式

在我开始深入探讨显示模式之前,请让我事先声明,这篇文章(以及显示模式技术本身)主要涉及生成一个独一无二的新站点,其中将同一 URL 动态绑定到不同的视图。如果您已有网站,要提供一个为某些(移动)设备优化的附属网站,那完全是另一个话题。您仍可将本专栏作为生成附属网站的指南,但与现有父网站统一 URL 需要使用其他工具。

在 ASP.NET MVC 4 中,显示模式是一项系统功能,该功能扩展视图引擎的传统行为,使后者可选取最适合请求设备的视图文件。在前面提到的 ASP.NET MVC 3 文章中,我为此使用了自定义视图引擎。在该解决方案中,我还只能使用 Razor 视图。通过显示模式,控制器方法仍将调用,比如说,一个名为 Index 的视图,如果已知请求设备为某种移动设备,则 ASP.NET MVC 运行时将改为选取一个名为 index.mobile.cshtml 的视图文件。

这是一个大好消息,因为这表示网站仍可只有一个基本代码。只需为要支持的每类设备添加额外的 CSHTML 视图文件。为了开始使用显示模式,我们来看图 1 中的代码示例。

图 1:支持的显示模式的标准列表

 

<h2>
  Display Modes currently active
  (@DisplayModeProvider.Instance.Modes.Count mode(s))
</h2>
<ul>
@{
  foreach(var d in DisplayModeProvider.Instance.Modes)
  {
    <li>@(String.IsNullOrEmpty(d.DisplayModeId)
      ?"default" :d.DisplayModeId)</li>
  }
}
</ul>

图 1 中代码中的页面显示支持的显示模式的标准列表。 图 2 显示由该页面生成的输出。

Default List of Display Modes
图 2:显示模式的默认列表

ASP.NET MVC 4 显示模式遵循几个约定。 尤其是,每种显示模式均与一个关键字关联。 该关键字用于构成相应视图文件的名称。 默认显示模式绑定到一个空字符串。 因此,由任何 ASP.NET MVC 4 应用程序正确地处理以下视图文件,无需您进一步干预: index.cshtml 和 index.mobile.cshtml。

若要查看演示,请将 index.cshtml 文件复制为一个名为 index.mobile.cshtml 的新文件,然后将其添加到项目中。 为了区分这两个文件,向移动文件添加以下内容:

<div style="border-bottom: solid 1px #000">Mobile view</div>

如果运行应用程序,并使用 Internet Explorer 或其他桌面浏览器测试它,则毫无变化。 尝试按 F12 以显示 Internet Explorer 开发人员工具,然后通过选择“工具”|“更改用户代理字符串”,设置移动用户代理 (UA),如图 3 所示。

Forcing a Mobile User Agent into Internet Explorer for Testing Purposes
图 3:强制 Internet Explorer 使用移动用户代理进行测试

我已配置了几种手机和平板电脑的 UA。 例如,可使用以下内容,其中将请求浏览器标识为 HTC Desire Android 智能手机:

Mozilla/5.0 (Linux; U; Android 2.1; xx-xx; HTC Desire Build/ERE27)
AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2

图 4 显示从 ASP.NET MVC 4 网站获得的内容。 从同一对控制器和操作方法得到的页面为 index.mobile.cshtml。 更重要的一点是,无需对编程风格作出任何更改,并且无需学习任何新技能,即可获得这种效果。

Switching to a Mobile View
图 4:切换到移动视图

基础知识以外

到目前为止,我们已讨论的内容只是在开发移动网站时可做并且应做的最基本工作。 需要解决重要的两点,才能将显示模式变为真实网站的解决方案。 一点是探索添加多个显示模式的方式。 另一点是探索注入某些临时逻辑以更可靠地检测设备的方式。

ASP.NET MVC 用于检测移动设备的内置逻辑并非都那么可靠。 该逻辑很可能适用于大多数智能手机,但不能用于老式手机。 以下列 UA 为例:

SAMSUNG-GT-S3370/S3370DDJD4 SHP/VPP/R5 Dolfin/1.5 Qtv/5.3
SMM-MMS/1.2.0 profile/MIDP-2.1 configuration/CLDC-1.1 OPN-N

此 UA 指的是一款老式手机(几年前比较流行),它运行专有操作系统和基于 WebKit 的自定义浏览器。 这款手机不支持 Wi-Fi 连接,但它呈现 HTML 的能力好得出人意料。 它的屏幕比大多数智能手机小,但支持触摸。 通过 ASP.NET MVC 中的基本显示模式支持,未将此手机识别为移动设备,而是提供完整版的页面。 这样有两个缺点。 首先,用户几乎无法查看内容,因为内容将被拉伸并围绕在屏幕四周。 其次,将下载许多内容,而由于这款手机不支持 Wi-Fi 连接,很可能通过 3G 连接下载所有这些内容,因此肯定过程缓慢,并且用户可能会花更多钱。

当我提到这一点时,有些人说他们的网站只是不支持这些类型的老式手机。 这当然合情合理,但在这种情况下向用户发出一条有礼貌的消息代替任由情况变得失控是否更好? 为了能够发一条“抱歉,无法在您的设备上查看本网站”等内容的消息,仍需要正确识别设备,并且了解它与,比如说 iPhone,有所不同。 此外,能否安全地忽略老式设备是一种商业(而非实施)上的决策。 不向前几代设备提供服务对于业务的影响程度可能超出想象。 那么,下面介绍如何将多个显示模式添加到一个网站,以正确地向多种类型的设备提供服务。

设备类型

无论设备是什么类型,新式网站均提供尽可能最佳的体验。 “尽可能最佳的体验”表示提供临时使用情况、特选数据和特定功能。 所得的标记必须特定于设备。 如果调整客户端上的设置,那么当依靠 CSS 媒体查询时,实际上获得页面的统一版本,然后只是调整这些页面,使其适合更小的屏幕。 这通常意味着将隐藏一些块、在垂直方向上盖住另一些块,还有可能请求一组较低的视觉效果。 页面的统一版本通常为桌面页面。 就我个人而言,我不愿意将此称为一种移动优先的方法。

说到“设备类型”,我无意区分 iPhone 设备与 Windows Phone 设备。 而是,旨在使用可向智能手机、平板电脑和笔记本电脑提供不同标记的逻辑。 因此,在 ASP.NET MVC 4 中,我要使用至少三种显示模式: 智能手机、平板电脑和默认模式(用于桌面浏览器)。 我将另外添加一个要在 App_Start 中调用的 DisplayConfig 类(见图 5)。

图 5:DisplayConfig 类

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

该类首先清空所提供的显示模式集合。 这样,它除去了默认模式。 接下来,代码用新创建的显示模式列表填充所提供的系统集合。 一个新的显示模式即为 DefaultDisplayMode 类的一个实例。 通过构造函数设置该模式的名称。 通过 ContextCondition 属性设置判断是否与给定 UA 匹配的逻辑。

ContextCondition 属性是一个接受 HttpContextBase 对象并返回布尔值的委托。 该委托的主体捕获当前请求的 HTTP 上下文以判断给定的显示模式是否合适。 在图 5 中,我使用了一些扩展方法以使代码具有较高的可读性。 图 6 列出了这些扩展方法。

图 6:使代码保持简明的扩展方法

public static class HttpRequestBaseExtensions
{
  public static Boolean IsDesktop(this HttpRequestBase request)
  {
    return true;
  }
  public static Boolean IsSmartphone(this HttpRequestBase request)
  {
    return IsSmartPhoneInternal(request.UserAgent);
  }
  public static Boolean IsTablet(this HttpRequestBase request)
  {
    return IsTabletInternal(request.UserAgent);
  }
  // More code here.
}

到目前为止讨论的所有代码仅仅是基础结构。 最后,要为每个显示模式编写一个方法。 每个方法均采用一个 UA,并返回一个布尔值答复。 下面是一个非常基本的例程,用于检查是否为平板电脑:

private static Boolean IsTabletInternal(String userAgent)
{
  var ua = userAgent.ToLower();
  return ua.Contains("ipad") || ua.Contains("gt-");
}

 

虽然此例程只能保证成功检测到 iPad 和 Galaxy Tab 设备,但您可了解有关应如何编写这些上下文条件例程的要点。至少,您可能要添加更多代码以检查是否为智能手机。若要检测平板电脑和智能手机,可利用任何开源或商业设备描述存储库 (DDR) 框架。我将在下一个专栏中更加详细地讨论这一点。

正式业务

移动网站的服务器端方法并非始终必要,但当网站背后经营一些业务时,该方法就是一件正经事。我不想为,比如,会议网站或任何种类的短期网站推荐服务器端方法。但是,以尽可能最多受众为目标的商业网站需要着重针对各种设备进行优化,而不只是简单地呈现适合移动环境的内容。

在客户端上,您受限于浏览器窗口大小和方向,并且无法检查操作系统或触摸功能,也无法检查是否具有更高级的功能,如设备是否支持无线、流媒体、内嵌图像、短信等。通过显示模式,在 ASP.NET MVC 4 中实现多视图方法变得尤为轻松。

在下一个专栏中,我将展开讨论,演示如何将 Facebook 使用的 DDR(无线通用资源文件 (WURFL))与 ASP.NET MVC 4 集成。

Dino Esposito 是《Architecting Mobile Solutions for the Enterprise》(Microsoft Press,2012 年)和《Programming ASP.NET MVC 3》(Microsoft Press,2011 年)的作者,同时也是《Microsoft .NET: Architecting Applications for the Enterprise》(Microsoft Press,2008 年)的合著者。 Esposito 定居于意大利,经常在世界各地的业内活动中发表演讲。 有关他的情况,请访问 Twitter twitter.com/despos

衷心感谢以下技术专家对本文的审阅: Mani Subramanian (Microsoft)
Mani Subramanian 过去 12 年来一直从事软件项目的开发和测试工作,研究领域偏向于 SOA、云计算和 core.net。