MVC最佳實踐

MVC執行流程

mvc.png 

Model的建議

Model是用於定義特定於域的對象。 這些定義應包括業務邏輯 (對象的行爲)、驗證邏輯、數據邏輯和會話邏輯 (跟蹤應用程序的用戶狀態)。javascript

作模型本身的項目使用分隔不一樣的程序集。

大型複雜模型的應用程序最好建立一個單獨的Model程序集,以減小耦合。 而後,能夠在 ASP.NET MVC 項目中引用Model的程序集。css

把全部的業務邏輯放在模型中。

若是你把全部的業務邏輯模型中, 你還得到如下好處:html

  • 減小重複的業務邏輯。
  • 視圖易於閱讀,不存在的業務邏輯。
  • 測試業務規則是獨立的。

例如:java

<% if (String.Compare((string)TempData["displayLastNameFirst"], "on") == 0)web

       { %>數據庫

        Welcome, <%= Model.lastName%>, <%= Model.firstName%>瀏覽器

    <% }緩存

       else安全

       { %>服務器

        Welcome, <%= Model.firstName%> <%= Model.lastName%>

<% } %>

若是不少地方須要這樣的邏輯,就是有在視圖中寫這樣的代碼,若是放在Model中:

public string combinedName

{

    get

    {

        return (displayLastNameFirst ? lastName + " " + firstName : firstName + " " + lastName);

    }

    private set

    {

        ;

    }

}

這將極大地簡化視圖,如圖所示:

<% Welcome, <%= Model.combinedName %> %>

把全部的驗證邏輯放在Model中。

全部輸入的驗證應該放在Model中,包括客戶機端驗證。

可使用 ModelState 來添加驗證檢查。 下面的示例顯示瞭如何將有明確的 ModelState 添加驗證檢查:

if (String.IsNullOrEmpty(userName))

{

 ModelState.AddModelError("username", Resources.SignUp.UserNameError);

}    

.net 框架提供的 System.ComponentModel.DataAnnotations 應該是驗證的首選的方法。好比:

public class User

{

   [Required(ErrorMessageResourceName = "nameRequired", ErrorMessageResourceType = typeof(Resources.User))]

   public String userName { get; set; }

  …

  }

定義數據訪問接口

接口用於公開數據訪問提供程序的方法, 這能加強 ASP.NET MVC 的鬆散耦合的組件的設計。

請考慮使用實體框架或 LINQ to SQL 建立到數據庫的調用wrappers。

View建議

HTML 放在ViewPartial Views(而不是在一個Controller)。

對於默認視圖引擎 ASP.NET 提供了幾種文件類型: 徹底 HTML 視圖 (.aspx),部分 HTML 視圖 (.ascx) 和母版頁(.master)。 母版頁使您可以指定一個總體佈局的視圖。 母版頁能夠嵌套幾回建立的可用的佈局類型層次結構。若是是Razor視圖沒有後綴名區別。

下面的示例顯示了調用局部視圖的方式:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

    Below is a list of items submitted by <b>

<%= Html.Encode(ViewData["name"]) %></b>.

    <p>

   

    ...

    <div id="items">

        <% Html.RenderPartial("ItemsByName");%>

    </div>   

</asp:content>

局部視圖 (ItemsByName.ascx) 以下所示:

<%@ Control Language="C#"  %>

        <% foreach (Seller.Controllers.Items item in (IEnumerable)ViewData.Model)

           { %>

            <tr>

                <td>

                    <%= Html.Encode(item.title)%>

                </td>

                <td>

                    <%= Html.Encode(item.price)%>

                </td>              

            </tr>       

        <% } %>

        </table>

      <% } %>

局部視圖是一個功能強大的可擴展性和重用機制。

訪問視圖使用ViewData

ASP.NET 提供瞭如下的機制訪問視圖模板中的數據:

  • ViewData.Model
  • ViewData 字典

推薦使用ViewModel,ViewModel是強類型,能保證類型安全。

使用自動生成客戶端驗證。

在模板中插入服務器端註釋。

下面的行演示了服務器端註釋:

< %--這是一個服務器端的模板評論 -- %>

不要視圖模板中使用 HTML 註釋,由於他們會呈現到 web 瀏覽器,可被潛在的惡意的用戶查看。

使用 HTMLHelper 擴展方法。

經常使用方法:

  • 表單生成 (BeginForm)
  • 輸入的字段生成複選 (框,隱藏,單選按鈕文本框)
  • 連接代 (ActionLink)
  • XSS 保護 (編碼)

Controller的建議

Action方法顯示指定view 名稱。

能夠直接return view();

若是這樣,MVC框架首先查找 /Views/Products/List.aspx若是不存在,查找 /Views/Products/List.ascx,若是還沒找到,查找 /Views/Shared/List.aspx 而後是/Views/Shared/List.ascx. 從上分析 /Views/Shared 這個目錄的文件能夠跨controller共享,爲避免混淆和性能考慮,推薦使用顯示指定view名稱的方式。

當提交表單時,使用post/redirect/get PRG)。

根據 HTTP POST 和 GET 動詞的定義:

  • HTTP GET 用於非更改您的模型 (冪等) 數據。
  • HTTP POST 用來更改您的模型的數據。

如圖在標準的回發中使用同一個 url create.aspx進行 GET 和 POST, 這是一個問題,當網站的用戶獲取窗體發佈到完成沒有耐心等待,若是他們點了瀏覽器的刷新按鈕,可能致使的重複數據。使用後Redirect 獲取模式能夠解決這個問題。

無標題1.png 

使用 HandleError或者在Web.config自定義錯誤處理

路由的建議

路由用於在 ASP.NET MVC url 直接映射到一個的控制器,而不是特定的文件。 這是以提升可讀性,能夠更好被搜索引擎收錄。

使用定製路由時使用從具體到通常路線。

定製路由聽從具體到通常的路線。

考慮下面的示例。 假設有一個以下列形式的 url 的產品目錄:

  • http://sellmyproducts/
  • http://sellmyproducts/Page#
  • http://sellmyproducts/category
  • http://sellmyproducts/category/Page#

提供如下列表方法的簽名 (ProductsController 類):

public ViewResult List(string category, int page)

定製路由:

routes.MapRoute(

    null,

    "",

    new { controller = "Products", action = "List", category = (string)null, page = 1 }

    );

 

routes.MapRoute(

    null,

    "Page{page}",

    new { controller = "Products", action = "List", category = (string)null },

    new { page = @"\d+" }

    );

 

routes.MapRoute(

    null,

    "{category}",

    new { controller = "Products", action = "List", page = 1}

    );

 

routes.MapRoute(

    null,

    "{category}/Page{page}",

    new { controller = "Products", action = "List"},

    new { page = @"\d+" }

    );

使用命名的路由機制,避免多義性的路由。

例如如下命名的路由:

routes.MapRoute(

    "Default",

    "",

    new { controller = "Products", action = "List", category = (string)null, page = 1 }

    );

 

routes.MapRoute(

    "PageRoute",

    "Page{page}",

    new { controller = "Products", action = "List", category = (string)null },

    new { page = @"\d+" }

);

使用這些路由定義,您能夠建立連接,將解析爲"PageRoute",以下所示:

<%= Html.RouteLink("Next", "PageRoute",

     new RouteValueDictionary( new { page = i + 1 } )); %>

可擴展性的建議

ASP.NET MVC 框架內有不少擴展點。 能夠替換的任何一個部分的列表,其中包括如下的核心組件:

  • 路由引擎 (MvcRouteHandler)
  • 控制器廠 (IControllerFactory)
  • 視圖引擎 (IViewEngine)

能夠經過在filters添加自定義行爲擴展框架,好比一些包含在框架的標準篩選器: OutputCache,HandleError,和Authorize。

使用filters添加行爲

這些篩選器能夠實現輕量級的請求處理管線的可擴展性。

例如假設要添加記錄爲每一個請求調試問題的 HTTP 標頭信息的功能。 下面的代碼定義了從 ActionFilterAttribute 類派生的類。

public class LogHeadersFilterAttribute : ActionFilterAttribute

{

   public override void OnActionExecuting(ActionExecutingContext filterContext)

   {

           foreach (string header in

filterContext.HttpContext.Request.Headers.AllKeys)

      {

            Debug.WriteLine("Header " + header);

            Debug.WriteLine("Value " +          

                      filterContext.HttpContext.Request.Headers.Get(header));

      }

            base.OnActionExecuting(filterContext);

   }

}

給定的操做方法中添加此篩選器,只要將 LogHeadersFilter 屬性放在要篩選的Action(或Controller) 的頂部。

可測試性建議

MVC 模式的主要優點之一是改進可測試性設計,解耦。

編寫單元測試。

ASP.NET MVC 提供了一個靈活的體系結構,容許簡單測試。能夠採用MS的單元測試工具或第三方工具來編寫單元測試。

關於單元測試 ASP.NET MVC 的應用程序的詳細信息,請參閱 MVC 應用程序中的單元測試

安全建議

安全是任何現代軟件開發項目的一個重要方面。 雖然沒有框架能夠提供完善的安全,可是仍是有不少方面你能夠幫助保護你的 ASP.NET MVC 應用程序。

防止常見的攻擊。

網站安全須要全部 web 開發人員的關注,常見攻擊方式:

  • 跨站點腳本 (XSS) 攻擊
  • SQL 注入
  • 跨站點請求僞造文件 (XSRF)

爲防止跨站點腳本 (XSS) 攻擊:

  • 禁用請求驗證經過使用 ValidateInput 屬性。 此屬性將拒絕錯誤的 HTML 輸入。
  • 將顯示的全部用戶輸入數據都添加 Html.Encode,不論是當即呈現的數據仍是從數據庫取出要顯示的數據。
  • cookie 設置 HttpOnly 標誌。 防止JavaScript閱讀和發送 cookie。

爲防止 SQL 注入:

  • 始終使用參數化的 SQL 查詢。
  • 不要將原始 SQL 傳遞到數據庫中。
  • 使用 (例如,能夠徹底消除的 SQL 語句在應用程序代碼須要的實體架構的對象關係映射 (ORM)。

爲防止跨站點請求僞造 (XSRF):

  • 在窗體中使用 Html.AntiForgeryToken 類可防止僞造跨站點請求。
  • 在須要保證的Action方法上增長ValidateAntiForgeryToken 屬性

進行身份驗證和受權來保護內容的用戶。

使用 < %: %> (.NET 4),以防止 XSS 攻擊。

前,.net 4.0 開發人員將必須那裏確保 HTML 編碼經過使用相似於下面的代碼:

<%= Html.Encode(ViewData["name"]) %>

此代碼被要防止 XSS 跨站點腳本攻擊。

若是使用的.net 4 不要使用上述語法。 請使用如下語法。

<%: Html.Encode(ViewData["name"] )%>

本地化和全球化建議

全球化是使一個產品多語言本地化在哪裏的適應特定語言和國家的全球產品過程的過程。 要開發一個 web 應用程序,它支持全球化和本地化,請記住至少一個規則。 在視圖中不使用硬編碼字符串。

不要使用 ASP.NET 特殊資源文件夾和資源文件。

添加 ASP.NET 項目文件夾全球化內容 (App_GlobalResources) 和給定的視圖 (App_LocalResources) 的本地化內容。 在每一個這些文件夾應添加一個資源 (.resx) 文件,應根據該控制器的名稱命名。若是控制器被命名爲 SubmissionPipeline,資源文件應被命名爲 SubmissionPipeline.resx。

visual Studio 將此文本映射類轉換爲全球的類,能夠調用使用下面的語法:

Resources.<resource_filename>.<string_name>

而後將訪問此類視圖中的資源:

<%= Resources.SubmissionPipeline.continueButton %>

獲得翻譯的資源文件時應使用如下格式命名每一個文件: <filename>. <language>.resx。

例如將命名資源文件的德語版的: SubmissionPipeline.de.resx。

性能的建議

可能會影響 web 站點性能的瓶頸包括:

  • 數據庫
    • 低效的查詢
    • 不正確地放置索引
    • 非規範化設計
  • 帶寬問題
    • 請求大小 (大圖像、.css、.js、.html、 等)。
    • 引用了其餘項目的多個腳本,CSS 或圖像文件的內容
    • 慢速鏈接
  • 處理能力
    • 服務器: 費時的操做
    • 客戶端: 糟糕的 javascript

考慮減小帶寬使用 AJAX 的部分頁更新。

使用Javascript  AJAX 異步來處理部分頁更新的請求來減輕涉及服務器處理壓力是的解決性能問題的一種方法。 ASP.NET MVC 有內置的 AJAX 支持,可減小處理服務器必須執行呈現的請求和減小的 HTML 片斷大小。

下面的示例說明了如何使用 AJAX 的部分頁更新:

1.     建立PartialView test.cshtml。

@model MVC.Test.Site.Models.UserView <div id="items">     username:     @if (Model != null)     {         @Model.UserName     }

</div>

2.     在Action.cshtml中添加

@{Html.RenderPartial("test", ViewData["user"]);} 

3.    Home/Login代碼

 public ActionResult Login(UserView user)   {             ViewData["user"] = user;             return PartialView("Action", user); } 

不要過分使用會話,而是使用 TempData存儲。

建立網站將對象添加到 會話 的對象,以便它們隨時可用時,看起來很好,可是將這些對象放在 Session 對象中的問題是它可能給服務器形成負擔,由於只是redirect的時候用到。 在重定向存儲這些臨時變量的正確方法是使用 TempData 詞典。

例如假設收到一個 POST 登陸時的表單數據。 初始化過程多是下面的操做方法:

[AcceptVerbs(HttpVerbs.Post)]

public ActionResult LogIn(Seller person)

{

    ...

    TempData["name"] = person.Name;

    return RedirectToAction("ItemUpload");

}

重定向到 ItemUpload 的操做方法以前將person.name放置在 TempData 詞典中。 ItemUpload 操做方法從 TempData 詞典中檢索,並放在本身的ViewData中,以便它能夠在視圖中引用。

public ActionResult ItemUpload()

{

    string name = TempData["name"] as string;

    ViewData["name"] = name;

 

    return View();

}

使用OutputCache

OutputCache 屬性用於不頻繁更新的數據 , 主要是用於首頁。 對於 HTML 和 JSON 數據類型,可使用這種技術。 當使用它,只指定緩存配置文件的名稱 ; 不指定任何其餘。使用 Web.config 文件配置output cache section。

例如 OutputCache 屬性附加到在如下代碼中的控制板操做方法。

[AcceptVerbs(HttpVerbs.Get), OutputCache(CacheProfile = "Dashboard")]

public ActionResult Dashboard(string userName, StoryListTab tab, OrderBy orderBy, int? page)

{

    ...

}

考慮使用異步控制器長時間運行的請求。

ASP.net 線程池的默認限制爲每一個 CPU 12 併發工做線程。 當請求過載處理這些請求的服務器的能力時,隊列是創建的請求。 例如任何請求,須要大量的時間等待外部資源如數據庫或較大的文件操做。 這些外部請求阻止他們佔用整個等待時間的線程。 當此隊列獲取太大 (5000 請求掛起) 時,在服務器啓動 503 (服務器太忙) 錯誤響應。

在 ASP.NET 4 的併發線程數設置默認狀況下,5000。 雖然可能增長默認限制有更好的方法,以減輕長時間運行的請求佔用了修改異步運行的長時間運行請求的線程。 ASP.NET MVC 使您能夠爲此目的實現異步控制器。 有關如何實現一個異步控制器的詳細信息,請參閱 使用 ASP.NET MVC 的一種異步控制器

相關文章
相關標籤/搜索