本文章是由機器翻譯。

技術最前線

在 ASP.NET MVC 4 中建立針對行動裝置最佳化的檢視

Dino Esposito

如果深入探討有關編寫行動裝置網站的常識性考慮因素,會發現其中有一種內在矛盾。一方面,客戶在其編寫應用程式和網站的方法中強烈要求(或樂於要求)移動優先。另一方面,同一些人又經常稱讚 CSS 媒體查詢和流體佈局。我所發現的矛盾在於經常利用 CSS 媒體查詢和流體佈局並未在其他內容之前優先處理移動方面,它不是一種移動優先的方法。在本文中,我將介紹如何使用伺服器端邏輯為給定設備呈現最佳的顯示效果,並介紹 ASP.NET MVC 4 的一種新功能,稱為顯示模式。

問題不在於 CSS 媒體查詢作為一種技術。問題甚至也不在於自我調整 Web 設計 (RWD) 作為 CSS 媒體查詢的支援方法 — 雖然不是這項技術啟發性的宗旨所在。那麼,怎樣將使用 CSS 媒體查詢和流體佈局變為一種「移動優先」的方法?在用於推廣此方法的口號中即可發現端倪:一個基本代碼可為多個視圖服務。在這個角度上,使用 CSS(一種用戶端技術)在視圖之間切換,而使用 JavaScript 在 CSS 無法勝任時進一步調整視圖。

在我看來,此方法中的基本做法是為所有設備提供相同內容,只調整頁面配置以適合螢幕大小。因此,可能無法向使用者提供最佳的體驗。我認為,您應在合理的情況下力求僅有一個基本代碼(Web API 的共同基礎),但一定要重點關注要支援的每類設備的具體使用方式。「移動」一詞如今已變得意義狹窄,被智慧手機、平板電腦、筆記本電腦和智慧電視等多種類型的設備取代,更不用說眼鏡顯示器和智慧手錶等可穿戴設備。

大約一年前,我在這個專欄中展示了一種在開發 ASP.NET MVC 網站時採用的伺服器端方法: to build ad hoc views for each class of supported devices (“Mobile Site Development: Markup,” 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 顯示由該頁面生成的輸出。


圖 2:顯示模式的預設清單

ASP.NET MVC 4 顯示模式遵循幾個約定。尤其是,每種顯示模式均與一個關鍵字關聯。該關鍵字用於構成相應視圖檔的名稱。預設顯示模式綁定到一個空字串。因此,由任何 ASP.NET MVC 4 應用程式正確地處理以下視圖檔,無需您進一步干預: index.cshtml and 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 所示。


圖 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。更重要的一點是,無需對程式設計風格作出任何更改,並且無需學習任何新技能,即可獲得這種效果。


圖 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。net.