MVC5知識點記錄

 

IIS/ASP.NET管道

原理永遠是重中之重,因此在開篇的地方,先了解一下地址欄輸入網址回車以後的故事。javascript

IIS/ASP.NET管道的流轉過程

針對IIS7而言css

不一樣IIS版本處理請求也不同

IIS5

IIS 5.x 運行在進程InetInfo.exe中,進程寄宿一個World Wide Web Publishing Service(W3SVC)服務。html

W3SVC主要負責HTTP請求的監聽、激活管理工做進程、加載配置等。前端

當檢測到HTTP請求,IIS根據擴展名判斷請求是靜態仍是動態。java

靜態資源,IIS直接響應內容。jquery

動態資源,根據擴展名從IIS的處理程序映射中找到ISAPI動態連接庫(Dynamic Link Library,DLL)git

ISAPI,是一套本地的Win32 API,是IIS和其餘動態Web應用或平臺之間的紐帶。web

ISAPI支持ISAPI擴展和ISAPI篩選,擴展處理HTTP請求的接口。篩選能夠進行查看,修改,轉發,拒絕。ajax

當收到ASP.NET資源請求,ISAPI會建立ASP.NET工做進程aspnet.exe.正則表達式

IIS進程與工做進程之間經過命名管道(Named Pipes)進行通訊。

工做進程初始化時,CLR會加載以構建一個託管的環境。對於某個Web應用的初次請求,CLR會爲其建立一個AppDomain應用程序域。

寄存在IIS5.x的全部Web應用都運行在同一個進程的不一樣AppDomain裏面。

IIS6

IIS6針對IIS5有如下兩點修改。

1. 將ISAPI動態連接庫直接加載到工做進程中。

2. IIS6引用應用程序池的機制,能夠爲一個或多個Web應用建立一個應用程序池。以免全部應用都在一個進程裏

能夠爲一個或多個Web應用建立應用程序池,每一個應用程序池對應一個獨立的工做進程,運行在不一樣應用程序池中的Web引用基於進程級別的隔離機制。

IIS6建立了一個HTTP.SYS的監聽器,以驅動程序的形式運行在Windows內核模式下,是TCP/IP網絡子系統的一部分。

HTTP.SYS好處以下

持續監聽:因爲是網絡驅動程序,始終處於運行狀態,對用戶的HTTP請求可以及時做出反應。

更好的穩定性:HTTP.SYS運行在操做系統內核模式下,並不執行任何用戶代碼,自己不會受到Web應用,工做進程,IIS進程的影響。

數據緩存:某個資源被頻繁請求,HTTP.SYS會把響應的內容進行緩存。

W3SVC在IIS6中,從進程InetInfo.exe轉移到SvcHost.exe中了。

當HTTP.SYS監聽到HTTP請求,將其分發給W3SVC進行解析,獲取目標應用,得到目標應用運行的程序池或工做進程。

工做進程初始化過程當中,ISAPI動態庫被加載,負責進行CLR的加載、應用程序域的建立和Web初始化等工做。

IIS7

引入了Windows進程激活服務(Windows Process Activation Service,WAS)分流W3SVC承載的部分功能。

W3SVC有三大功能

1.HTTP請求接受:接受HTTP.SYS監聽到的HTTP請求

2.配置管理:從元數據庫中加載配置信息對相關組件進行配置

3.進程管理:建立、回收、監控工做進程

其中後兩個功能實現到WAS中了。提供了非HTTP協議的支持,經過監聽適配器接口,抽象出針對不一樣協議的監聽器。

對應3中監聽器,他們以Windows服務的形式進行工做。

NetTcpPortSharing:爲WCF提供TCP端口共享,即同一個監聽端口被多個進程共享

NetTcpActivator:爲WAS提供基於TCP的激活請求,包含TCP監聽器和對應的監聽適配器

NetPipeActivator:爲WAS提供命名管道的激活請求

NetMsmqActivator:爲WAS提供基於MSMQ的激活請求

不管是從W3SVC接受到的請求,仍是監聽器接受的請求,最終都會流轉到WAS中。

IIS7分爲兩種模式,經典模式跟IIS6相同,集成模式是一種統一的處理管道。

將ASP.NET請求管道和IIS請求管道組合在一塊兒。能夠提供更好的性能,能夠完成配置和管理的模塊化。繼承模式的映射都在web.config中進行處理。

ASP.NET集成

IIS與ASP.NET是兩個相互獨立的管道,各自具備本身的一套HTTP處理機制。兩個管道經過ISAPI連通。

IIS是第一道屏障,進行必要的前期處理。IIS經過ISAPI將請求分發給ASP.NET管道。

ASP.NET在自身管道範圍內完成對HTTP請求的處理,結束後再返回IIS。IIS在進行後期處理。

IIS運行在非託管的環境中,ASP.NET管道則是託管。託管環境指CLR搭建的環境,非託管環境指能夠由Windows直接調用。

ASP.NET管道

HTTP.SYS接收到HTTP請求是對web應用的第一次訪問,加載CLR後IIS會經過AppDomainFactory建立一個AppDomaiin。

ISApiRuntime被加載,會接管該HTTP請求。

建立一個IsapiWorkerRequest對象封裝當前的HTTP請求,將此對象傳遞給CLR的HttpRuntime(當前應用程序的 ASP.NET 運行時服務)

此時,HTTP請求正式進入ASP.NET管道。HttpRuntime會根據對象建立HTTPContext( HTTP 請求的全部 HTTP 特定的信息)俗稱 請求上下文

HttpContext建立後,HttpRuntime會利用HttpApplicationFacotry建立HttpApplication(ASP.NET 應用程序內全部應用程序對象公用的方法、屬性和事件)

HttpApplication初始化過程當中,ASP.NET會根據配置文件加載並初始化HttpModule(HTTP模塊初始化和處置事件)

HttpApplication會在處理HTTP請求的不一樣階段觸發不一樣的事件,HttpModule經過註冊事件, 將操做注入HTTP請求處理流程中。

HttpApplication

(定義對 ASP.NET 應用程序內全部應用程序對象公用的方法、屬性和事件。 此類是用戶在 Global.asax 文件中定義的應用程序的基類)

整個ASP.NET基礎架構的核心,負責處理分發給它的HTTP請求。

第一個請求抵達後,會建立多個HttpApplication對象,存放於對象池中。

在HTTPAppliccation處理請求的不一樣階段會觸發不一樣的事件。

對於ASP.NET應用來講,HttpApplication派生於Global.asax文件,能夠經過此文件,對請求處理行爲進行制定。

Global.asax 採用方法名匹配,按照「Application_{事件名}」進行事件註冊。

其中事件名內容,就是Application中包含的事件。例如:

protected void Application_BeginRequest(Object sender, EventArgs e) 
{ 
   Application["StartTime"] = System.DateTime.Now; 
} 

其中會有幾個是針對整個應用程序而言,而不是針對請求。

  1. Application_Init:在每個HttpApplication實例初始化的時候執行 
  2. Application_Disposed:在每個HttpApplication實例被銷燬以前執行
  3. Application_Error:全部沒有處理的錯誤都會致使這個方法的執行
  4. Application_Start:在程序初始化的時候執行。在Web應用程序的生命週期裏就執行一次,這裏只能放一些公用的信息,好比HttpApplicationState
  5. Application_End:應用程序結束時,在最後一個HttpApplication銷燬以後執行。對應Application_Start,在整個生命週期裏面也是隻執行一次
  6. Session_Start:會話開始時執行
  7. Session_End:會話結束或過時時執行

請求事件執行順序以下:

  1. BeginRequest:做爲執行的 HTTP 管道鏈中的第一個事件發生,當 ASP.NET 的請求作出響應
  2. AuthenticateRequest:當安全模塊已創建的用戶標識時出現
  3. PostAuthenticateRequest:當安全模塊已創建的用戶標識時出現
  4. AuthorizeRequest:安全模塊已驗證用戶身份驗證時發生
  5. PostAuthorizeRequest:當前請求的用戶已被受權時發生
  6. ResolveRequestCache:當 ASP.NET 完成受權事件以便從緩存中,跳過的事件處理程序 (例如,一個頁面或 XML Web 服務) 執行的請求提供服務的緩存模塊時發生。
  7. PostResolveRequestCache:ASP.NET 將繞過當前事件處理程序的執行,並容許緩存模塊以處理從緩存請求時發生
  8. PostMapRequestHandler:當 ASP.NET 已映射到相應的事件處理程序的當前請求時出現
  9. AcquireRequestState:當 ASP.NET 獲取與當前的請求相關聯的當前狀態 (例如,會話狀態)
  10. PostAcquireRequestState:獲取與當前的請求相關聯的請求狀態 (例如,會話狀態) 時發生
  11. PreRequestHandlerExecute:ASP.NET 開始執行事件處理程序 (例如,一個頁面或 XML Web 服務) 以前發生
  12. PostRequestHandlerExecute當 ASP.NET 事件處理程序 (例如,一個頁面或 XML Web 服務) 完成執行時發生
  13. ReleaseRequestStateASP.NET 完成執行全部請求事件處理程序後發生。 此事件會致使狀態模塊保存當前的狀態數據
  14. PostReleaseRequestState:當 ASP.NET 已完成執行全部請求事件處理程序和存儲數據的請求狀態時發生
  15. UpdateRequestCache:當 ASP.NET 完成執行事件處理程序,以便讓緩存模塊存儲將用於爲從緩存中的後續請求提供服務的響應時發生
  16. PostUpdateRequestCache:當 ASP.NET 完成更新的緩存模塊和存儲用於爲從緩存中的後續請求提供服務的響應時發生
  17. LogRequest:ASP.NET 執行當前請求的任何日誌記錄以前發生
  18. PostLogRequest:當 ASP.NET 已完成處理的事件處理程序時發生 LogRequest 事件
  19. EndRequest:做爲執行的 HTTP 管道鏈中的最後一個事件發生,當 ASP.NET 的請求作出響應

IHttpModule

(提供模塊初始化和處置事件以實現類)

當請求轉入ASP.NET管道時,最終負責處理該請求的是HttpHandler對象。在此以前會先加載HttpModule對象。

ASP.NET提供的不少基礎功能都是經過HttpModule實現的。例如

OutputCacheModule:輸出緩存功能

SessionStateModule:無狀態的HTTP協議上實現基於會話的狀態保持

WindowsAuthenticationModule + FormsAuthenticationModule + PassportAuthenticatinonModule:實現了三種典型的身份認證

UrlAuthorizationModule _ FileAuthorizationModule:基於URI和ACL文件的受權

IHttpHandler

(定義 ASP.NET 以異步方式處理使用自定義 HTTP 處理程序的 HTTP Web 請求而實現的協定)

對不一樣資源類型的請求,ASP.NET會加載不一樣的Handler來處理,好比aspx與asmx對應的Handler是不一樣的。

HttpHandler 能夠配置到Web.config中。例子爲svc資源的配置。

<system.web>
    <httpHandlers>
      <add path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHander" validate="false" />
    </httpHandlers>
    <compilation debug="true" targetFramework="4.6" />
    <httpRuntime targetFramework="4.6" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/login.aspx" timeout="2000"></forms>
    </authentication>
  </system.web>
HttpHandler

自治視圖

將全部與UI相關的操做糅合在一塊兒,包括UI界面的呈現、交互的捕捉與相應、業務執行對數據的存儲。這種模式稱爲:自治視圖。

Web Form和Windows Form,都採用事件驅動的開發方式,全部與UI相關的邏輯均可以定義在後臺代碼中。

缺點以下

1. 重用性差:業務邏輯與UI無關,應當最大程度的被重用。可是定義在自治視圖中的UI處理邏輯,徹底不能重用。

2. 穩定性差:業務邏輯具備最強的穩定性,UI邏輯其次,界面呈現最差。若是將他們融合在一塊兒,則最差穩定性決定了總體穩定性。

3. 可測試性差:任何涉及UI的組件都不易測試,用機器來模擬人對組件實施自動化測試不是一件容易的事。

MVP

MVP是一種普遍使用的UI架構模式,適用於基於事件驅動的應用框架。例如:web Form, win Form。

Model View Presenter,天然代替了MVC中的Model View Controller。

MVP三要素之間交互體如今兩個方面:

Presenter與Model:Presenter對Model是單向調用。很簡單

View與Presenter:採用怎樣的交互方式是整個MVP的核心。分爲兩種模式:PV(Passive View)和SV(Supervising Controller)

PV

View難以測試的解決辦法,就是讓他無須測試。條件就是讓View儘量不涉及UI處理邏輯,這就是PV模式的目的。

PV是一個被動的View,定義其中的針對UI元素的操做不是由View自身主動來控制,而是被動的交給Presenter來操控。

例:須要作一個列表,一個下拉框,一個查詢。

首先,咱們定義Model(Employee),而後由於須要添加數據因此,咱們還要定義一個Model存儲庫(ModelRepository)

public class Employee
    {
        public string Id { get; private set; }
        public string Name { get; private set; }
        public string Gender { get; private set; }
        public DateTime BirthDate { get; private set; }
        public string Department { get; private set; }
        public Employee(string id, string name, string gender, DateTime birthDate, string department)
        {
            this.Id = id;
            this.Name = name;
            this.Gender = gender;
            this.BirthDate = birthDate;
            this.Department = department;
        }
    }

    public class EmployeeRepository
    {
        private static IList<Employee> employees;
        static EmployeeRepository()
        {
            employees = new List<Employee>();
            employees.Add(new Employee("001", "張三", "", new DateTime(1981, 8, 24), "銷售部"));
            employees.Add(new Employee("002", "李四", "", new DateTime(1982, 7, 10), "人事部"));
            employees.Add(new Employee("003", "王五", "", new DateTime(1983, 9, 21), "人事部"));
        }
        public IEnumerable<Employee> GetEmployees(string department = "")
        {
            if (string.IsNullOrEmpty(department))
            {
                return employees;
            }
            return employees.Where(e => e.Department.Equals(department)).ToArray(); ;
        }
    }
模型層

Repository,只是爲了省略業務邏輯層。採用靜態加載模擬數據。

而後,咱們定義頁面的視圖接口,用來將操做暴露給Presenter。這裏不會將控件直接暴露給Presenter,由於這樣耦合性會變得很大。

咱們暴露一個獲取,兩個設置,還有一個點擊事件。

 public interface IEmployeePVView
    {
        IEnumerable<string> Departments { set; }
        string SelectedDepartment { get; }
        IEnumerable<Employee> Employees { set; }
        event EventHandler DepartmentSelected;
    }
視圖

接下來,咱們定義Presenter,由於是PV模式,因此咱們在這裏操做暴露的UI元素。不讓UI層,進行任何邏輯。

 public class EmployeePVPresenter {
        public IEmployeePVView View { get; private set; }
        public EmployeeRepository Repository { get; private set; }
        public EmployeePVPresenter(IEmployeePVView view) {
            this.View = view;
            this.Repository = new EmployeeRepository();
            this.View.DepartmentSelected += OnDepartmentSelected;
        }

        public void Initialize() {
            IEnumerable<Employee> employees = this.Repository.GetEmployees();
            this.View.Employees = employees;
            string[] departments = new string[] { "", "採購部", "人事部", "銷售部" };
            this.View.Departments = departments;
        }

        private void OnDepartmentSelected(object sender, EventArgs e)
        {
            string department = View.SelectedDepartment;
            var employees = this.Repository.GetEmployees(department);
            this.View.Employees = employees;
        }
    }
Presenter

最後,咱們展現這個頁面

 public partial class WebForm2 : System.Web.UI.Page, IEmployeePVView
    {
        public IEnumerable<string> Departments
        {
            set
            {
                this.DropDownListDepartments.DataSource = value;
                this.DropDownListDepartments.DataBind();
            }
        }

        public IEnumerable<Employee> Employees
        {
            set
            {
                this.GridViewEmployee.DataSource = value;
                this.GridViewEmployee.DataBind();
            }
        }

        public string SelectedDepartment
        {
            get
            {
                return this.DropDownListDepartments.SelectedValue;
            }
        }

        public EmployeePVPresenter Presenter { get; private set; }
        public event EventHandler DepartmentSelected;
        public WebForm2()
        {
            this.Presenter = new EmployeePVPresenter(this);
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                this.Presenter.Initialize();
            }
        }

        protected void ButtonSearch_Click(object sender, EventArgs e)
        {
            DepartmentSelected(this, e);
        }
    }
Page

PV模式,將全部的UI處理邏輯所有定義在Presenter上,意味着全部的UI處理邏輯均可以被測試。

可是,將全部可供操做的UI元素定義在對應接口中,對於一些複雜的富客戶端應用來講。接口會變得不少,這會提高編程所需的代碼量。

對於這種很複雜的View來講,咱們採用SC模式。

SC

在SC模式中,Presenter不是View調用Model的中介,而是最終決定如何響應用戶交互行爲的決策者。

View是Presenter委派到前端的客戶代理,做爲客戶的天然就是最終的用戶。對於鍵鼠操做的交互請求,View會彙報給Presenter。

Presenter處理用戶交互請求的流程,若是中間環節須要設計Model,它會直接發起對Model的調用。若是須要View會直接驅動View完成相應工做。

對於綁定數據,是Presenter主動推給View的。是單向操做。View自己僅僅實現了單純的、獨立的UI邏輯。

他處理的數據是Presenter實時推給它的,因此View儘量不維護數據狀態。定義在IView的接口只包含方法,而不包含屬性

Presenter所需的View狀態,應該在接受到View發送的用戶交互請求的時候一次獲得,而不須要經過View的屬性去獲取。

例:狀況如PV模式的狀況同樣

Model層,同樣,省略。

接口只包含方法,也就是綁定列表和綁定下拉框的方法。而後由於須要單擊事件的數據,因此定義一個事件數據類進行接收。

public interface IEmployeeView
    {
        void BindEmployees(IEnumerable<Employee> employees);
        void BindDepartments(IEnumerable<string> departments);
        event EventHandler<DepartmentSelectedEventArgs> DepartmentSelected;
    }

    public class DepartmentSelectedEventArgs : EventArgs
    {
        public string Department { get; private set; }
        public DepartmentSelectedEventArgs(string department)
        {
            this.Department = department;
        }
    }
接口

Presenter進行操做

public class EmployeePresenter
    {
        public IEmployeeView View { get; private set; }
        public EmployeeRepository Repository { get; private set; }
        public EmployeePresenter(IEmployeeView view)
        {
            this.View = view;
            this.Repository = new EmployeeRepository();
            this.View.DepartmentSelected += OnDepartmentSelected;
        }

        public void Initialize()
        {
            IEnumerable<Employee> employees = this.Repository.GetEmployees();
            this.View.BindEmployees(employees);
            string[] departments = new string[] { "", "採購部", "人事部", "銷售部" };
            this.View.BindDepartments(departments);
        }

        private void OnDepartmentSelected(object sender, DepartmentSelectedEventArgs e)
        {
            string department = e.Department;
            var employees = this.Repository.GetEmployees(department);
            this.View.BindEmployees(employees);
        }
    }
Presenter

頁面進行方法實現,和事件綁定

<div id="page">
            <div>
                <asp:DropDownList ID="DropDownListDepartments" runat="server"></asp:DropDownList>
                <asp:Button ID="ButtonSearch" runat="server" Text="查詢" OnClick="ButtonSearch_Click" />
            </div>
            <asp:GridView ID="GridViewEmployees" runat="server" AutoGenerateColumns="false" Width="100%">
                <Columns>
                    <asp:BoundField DataField="Name" HeaderText="姓名" />
                    <asp:BoundField DataField="Gender" HeaderText="性別" />
                    <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
                    <asp:BoundField DataField="Department" HeaderText="部門" />
                </Columns>
            </asp:GridView>
        </div>
DIV
public partial class WebForm1 : System.Web.UI.Page, IEmployeeView
    {
        public EmployeePresenter Presenter { get; private set; }
        public event EventHandler<DepartmentSelectedEventArgs> DepartmentSelected;
        public WebForm1()
        {
            this.Presenter = new EmployeePresenter(this);
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                this.Presenter.Initialize();
            }
        }

        protected void ButtonSearch_Click(object sender, EventArgs e)
        {
            string department = this.DropDownListDepartments.SelectedValue;
            DepartmentSelectedEventArgs eventArgs = new DepartmentSelectedEventArgs(department);
            if (DepartmentSelected != null)
            {
                DepartmentSelected(this, eventArgs);
            }
        }

        public void BindEmployees(IEnumerable<Employee> employees)
        {
            this.GridViewEmployees.DataSource = employees;
            this.GridViewEmployees.DataBind();
        }

        public void BindDepartments(IEnumerable<string> departments)
        {
            this.DropDownListDepartments.DataSource = departments;
            this.DropDownListDepartments.DataBind();
        }
    }
後臺

MVC

記錄一個MVC框架流程和模擬的MVC框架實現。具體代碼參考:https://coding.net/u/chenxygx/p/MVCDemo/git

處理請求流程

1. MVC利用路由系統對請求URL進行解析並獲得Controller和Action名稱以及其餘數據。

2. 根據Controller名稱反射出其類型,並將其激活。

3. 利用Action名稱解析出定義在目標Controller類型中對應的方法,而後執行Controller對象的這個方法。

4. Action方法能夠在執行過程當中直接對當前請求進行相應,也能夠返回一個ActionResult對象來相應請求。

5. 如返回ActionResult對象,會執行返回的對象來看成當前請求做最終的相應。

具體操做完成步驟,寫在代碼註釋中。

路由

當MVC接受到抵達的請求後,首要任務就是經過當前的HTTP請求的解析獲得目標的Controller和Action名稱。

MVC中會定義一個全局路由表RouteTable,裏面的Route對象對應一個路由模板。路由模板中定義Controller和Action的變量形式。

對於HTTP請求,會遍歷路由表RouteTable,找到URL匹配模式,解析核心路由數據 RouteData。

Controller

將獲得的RouteData對象包含的目標Controller名稱,根據其名稱進行激活對應的Controller對象

Action

Controller的Execute方法主要做用在於執行目標Action方法。若是返回ActionResult對象,還須要執行該對象來響應。

完整流程

Global 註冊路由信息RouteTable,加載Controller工廠ControllerBuilder

當請求到來時

UrlRoutingModule繼承自IHttpModule接口進行註冊。會利用RouteTable表示路由錶針對當前請求實施路由解析。

解析完成後會返回RouteData對象,包含Controller和Action名稱。經過RouteData對象的RouteHandler屬性獲得匹配Route對象採用的RouteHandler對象。

此用例中是MvcRouteHandler。調用這個對象的GetHttpHandler方法獲得一個MvcHandler對象。

UrlRouteingModule調用當前HTTP上下文的MapHttpHandler方法獲得HttpHandler對象實施映射。此時MvcHandler接管當前請求。

MvcHandler用來處理當前請求,會利用RouteData對象獲得目標Controller名稱,並藉助於註冊的ControllerFactory來激活對應的Controller對象。

對象激活後,Excute方法被MvcHandler調用。執行時會調用ActionInvoker對象的InvokeAction方法來執行目標Action方法並對當前的請求予以響應。

採用ActionInvoker是一個ControllerActionInvoker對象,當InvokeAction方法被執行的時候,

會利用註冊的ModelBinder採用Model綁定的方式生成目標Action方式的參數列表。並利用ActionExecutor對象以表達式樹的方式執行目標Action方法。

Action方法執行以後老是會返回一個ActionResultl的Actionfangfa ,MVC是會將執行的結果轉換成一個ActionResult對象。

ControllerActionInvoker會經過執行此ActionResult對象來對請求作最終的響應。

路由

路由能夠根據URL進行資源解析。不是專屬於MVC的,能夠在Web Forms應用中使用。幫助實現請求地址與物理文件的分離。

public class Global : HttpApplication
    {
        void Application_Start(object sender, EventArgs e)
        {
            RouteValueDictionary value = new RouteValueDictionary { { "Name", "Chenxy" }, { "Id", 123 } };
            RouteTable.Routes.MapPageRoute("Defual", "{Name}/{Id}", "~/Test1.aspx", true, value);
        }
    }

public partial class Test1 : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string name = RouteData.Values["Name"] as string;
            string id = RouteData.Values["Id"] as string;
        }
    }
路由機制

路由系統有兩個功能:

1)輸入URL並判斷請求是想要那個控制器和動做。

2)輸出URL是輸出一個超連接,用戶點擊之後變爲輸入URL。

URL路由是以下模式:{controller}/{action}(控制器和方法)

當有輸入請求的時候,路由的職責是將這個請求與一個模式進行匹配。而後從URL中爲定義的片斷變量提供值。

片斷變量指:花括號內名稱。上面的controller與action就是片斷變量。

RouteConfig配置文件(由Global.asax進行運行)

public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }

MapRoute返回的是Route對象繼承RouteBase。其中,RouteExistingFiles=false,表示可使用物理訪問模式。

路由順序

路由順序很是重要,路由系統會試圖根據最早被定義的路由模式來匹配一個輸入URL。只有不匹配的時候,纔會往下進行處理。

故此,須要優先匹配比較具體的路由。模糊的路由儘可能放在後方。

路由配置

路由配置還能夠進行多種多樣的操做。

 1 public class RouteConfig
 2     {
 3         public static void RegisterRoutes(RouteCollection routes)
 4         {
 5             routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 6             //默認路由,其中若是沒有輸入action,那麼默認路由找Index
 7             routes.MapRoute("LuYou", "{controller}/{action}", new { action = "Index" });
 8             //靜態URL,能夠建立固定前綴的路由。也可聲明static{controller}/{action}。
 9             routes.MapRoute("JingTai", "static/{controller}/{action}", new { controller = "Home", action = "Index" });
10             //自定義變量id,自定義路由能夠做爲參數運用。Get(string id)
11             routes.MapRoute("Id", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = "DefaultId" });
12             //可選URL片斷
13             routes.MapRoute("KeXuan", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional });
14             //可變長路由,*號做爲前綴表示變量可變長。不管有多少片斷,都可付給可變長變量。使用/進行分割
15             routes.MapRoute("Long", "{controller}/{action}/{*catchall}");
16             //優先命名空間,其中MvcDemo爲命名空間。當檢測此條路由時,會優先查找命名空間。
17             routes.MapRoute("namespace", "{controller}/{action}", new { controller = "Home", action = "Index" }, new[] { "MvcDemo" });
18             //約束路由,使用正則表達式約束,開頭爲H字母的URL
19             routes.MapRoute("YueShu", "{controller}/{action}", new { controller = "Home", action = "Index" }, new { controller = "^H.*" });
20             //約束路由組,方法爲Index或者About
21             routes.MapRoute("YueShus", "{controller}/{action}",
22                 new { controller = "Home", action = "Index" },
23                 new { controller = "^H.*", action = "^Index$|^About$" });
24             //約束請求方法
25             routes.MapRoute("YueShu2", "{controller}/{action}",
26                 new { controller = "Home", action = "Index" },
27                 new { controller = "^H.*", httpMethod = new HttpMethodConstraint("Get") });
28             //使用內置類型約束。明細:https://msdn.microsoft.com/zh-cn/library/system.web.mvc.routing.constraints(v=vs.118).aspx
29             routes.MapRoute("YueShu3", "{controller}/{action}",
30                 new { controller = "Home", action = "Index" },
31                 new { controller = "^H.*", httpMethod = new HttpMethodConstraint("Get"), id = new RangeRouteConstraint(10, 20) });
32         }
33     }

屬性路由

屬性路由是MVC5特有的一種路由形式。配置大於約定的一種形式,與上面的路由模式相違背。Net Core就採用的屬性路由來進行的配置。

在項目中,能夠同時使用兩種方法,而且沒有不良的效果。

默認狀況下,屬性路由是禁用狀態,首先須要開啓屬性路由。在RouteConfig.cs文件,添加

 public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapMvcAttributeRoutes();    
        }

這樣就會開啓自定義路由。

使用屬性路由

使用屬性路由主要是運用,Route特性

 1  [Route("api/Todo/GetAll")]
 2         public IEnumerable<TodoItem> GetAll()
 3         {
 4             return TodoItems.GetAll(); ;
 5         }
 6 
 7         [Route("api/Todo/GetById")]
 8         public IActionResult GetById(string id)
 9         {
10             var item = TodoItems.Find(id);
11             if (item == null)
12             {
13                 return NotFound();
14             }
15             return new ObjectResult(item);
16         }

 

也可使用片斷變量建立路由

[Route("api/[controller]/{id:string}")]

路由前綴

在控制器上方進行Route能夠定義路由前綴

[Route("api/[controller]")]
    public class TodoController : Controller
    {
        public ITodoRepository TodoItems { get; set; }
        public TodoController(ITodoRepository todoItems)
        {
            TodoItems = todoItems;
        }

        public IEnumerable<TodoItem> GetAll()
        {
            return TodoItems.GetAll();
        }

        [Route("String")]
        public string GetMessage()
        {
            return "ces";
        }
}

 

如上的路由配置,其中前綴就是 api/[controller]。在這個前綴下,輸入String,就會跳轉到GetMessage方法。例如:http://localhost:12109/api/Todo/String

輸出URL

輸出路由主要是調用Html.ActionLink輔助方法來進行操做。

<div>
    @Html.ActionLink("當前控制器的方法", "Index")
    @Html.ActionLink("其餘控制器的方法", "Index", "Home")
    @Html.ActionLink("傳遞額外變量", "Index", new { id = 3 })
    @Html.ActionLink("指定區域", "Index", new { area = "About" })
    @Html.ActionLink("指定CSS", "Index", new { @class = "Css" })
</div>

區域

MVC框架能夠將應用程序組成一個區域,每一個區域表明應用程序的一個功能段。

能夠經過右鍵,添加對應區域來進行操做。其中路由使用靜態URL來進行匹配。

POST和GET 

GET請求應用於全部只讀信息檢索,POST請求被用於各類修改應用程序狀態的操做。

GET請求是可設定地址的,全部信息都包含在URL中,所以他能夠設爲書籤並連接到這些地址。

控制器

控制器的做用是封裝應用程序邏輯,負責處理輸入請求、執行域模型上的操做,並選擇渲染給用戶的視圖。

控制器的建立均已Controller結尾。

接受數據

控制器獲取URL參數有三個主要的途徑:

1)經過上下文對象提取。

2)經過參數提取。

3)調用框架的模型綁定特性。

上下文對象

Controller類提供了一組便利屬性,能夠用來訪問請求的相關信息。

Request.QueryString  Get變量
Request.Form  POST變量
Request.Cookies  Cookies
Request.HttpMethod  用於請求的方法(POST or Get)
Request.Headers  HTTP報頭
Request.Url  請求的URL
Request.UserHostAddress  請求的用於IP地址
RouteData.Route  Routes條目
RouteData.Values  路由參數
HttpContext.Application  狀態庫
HttpContext.Cache  緩存庫
HttpContext.Items  請求的狀態庫
HttpContext.Session  會話狀態庫
User  用戶認證信息
TempData  用戶臨時存儲數據

例:獲取Post請求的Id變量。Rquest.Form["Id"]

動做參數

對應固定獲取的變量,可使用 Get(string id)。這種參數變量,來進行簡化。

輸出

控制器在完成一個請求後,須要生成一個響應。

若是想發送HTML響應,那麼必須建立HTML數據,而後經過Response.Write方法發送到客戶端。

若是想跳轉另外一個URL,那麼須要調用Response.Redirect方法。

動做結果

MVC框架經過使用動做結果把指明意圖和執行意圖分離開來。

不直接使用Response對象,而是返回一個派生於ActionResult類的對象。它描述了控制器響應要完成的功能。

ActionResult派生類以下

 1 類型 | 輔助器方法 | 描述
 2 ViewResult | View | 返回指定的或默認的視圖模板
 3 PartialViewResult | PartialView | 返回指定的或默認的分部視圖模板
 4 RedirectToRouteResult | [RedirectToAction\RedirectToActionPermanent\RedirectToRoute\RedirectToRoutePermanent] | 重定向發送給一個動做方法或特定的路由條目
 5 RedirectResult | [Redirect\RedirectPermanent] | 重定向發送給一個特定的URL
 6 ContentResult | Content | 返回原始的文本數據給瀏覽器
 7 FileResult | File | 將二進制數據流直接傳送給瀏覽器
 8 JsonResult | Json | 將對象序列化成Json格式,發送給瀏覽器
 9 JavaScriptResult | JavaScript | 發送一個由瀏覽器執行的JavaScript源代碼片斷
10 HttpUnauthorizedResult | None | 引起認證機制要求訪問者進行登陸
11 HttpNotFoundResult | HttpNotFound | 返回404
12 HttpStatusCodeResult | None | 返回指定HTTP碼
13 EmptyResult | None | 什麼也不幹

例:返回視圖。View("Home")

會搜索一下位置,進行查找Home這個視圖

1 /Views/控制器/視圖.aspx
2 /Views/控制器/視圖.ascx
3 /Views/控制器/視圖.cshtml
4 /Views/控制器/視圖.vbhtml
5 /Views/Shared/視圖.aspx
6 /Views/Shared/視圖.ascx
7 /Views/Shared/視圖.cshtml
8 /Views/Shared/視圖.vbhtml

經過路徑指定視圖,能夠提供一個明確的路徑來繞過搜索階段。

View("~/View/Other/Index.cshtml")

View同時能夠傳遞數據實體給視圖。View(DateTime.Now)   視圖中定義 @model DateTime,就可使用傳輸的實體對象了

保留臨時數據:TempData["Message"] ,數據只可以使用一次。

返回HTTP錯誤碼:return new HttpStatusCodeResult(404,"消息")

過濾器

過濾器把附加邏輯注入到MVC框架的請求處理中,實現了交叉關注。指能夠用整個應用程序,而又不適合放置在某個局部位置的功能。例如:受權、登陸、緩存

過濾器是.Net的特性,對請求處理管道添加了額外的步驟。

MVC支持5中不一樣的過濾器

1 類型 | 接口 | 默認實現 | 描述
2 認證過濾器 | IAuthenticationFilter | N/A | 最早運行優先於其餘過濾器和動做
3 受權過濾器 | IAuthorizationFilter | AuthorizeAttribute | 第二個運行在認證後
4 動做過濾器 | IActionFilter | ActionFilterAttribute | 在動做方法以前及以後
5 結果過濾器 | IResultFilter | ActionFilterAttribute | 在結果以前及以後
6 異常過濾器 | IExceptionFilter | HandleErrorAttribute | 在拋出異常時運行

 

過濾器能夠寫在控制器和方法上面

受權過濾器

自定義受權過濾器能夠派生AuthorizeAttribtue。判斷Session是否保存,而且添加跳轉操做。登陸頁只須要保存對應的Session便可。

 public class UserAuthorizationAttribute : ActionFilterAttribute
    {
        public static readonly string CookieUserName = "username";
        public static readonly string ManageLoginUrl = "/home/login";

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            //排除過濾器
            var actionNoCompress = filterContext.ActionDescriptor.GetCustomAttributes(typeof(NoCompressAttribute), false);
            var methodNoCompress = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(typeof(NoCompressAttribute), false);
            if (actionNoCompress.Length == 1 || methodNoCompress.Length == 1)
            {
                return;
            }

            //用戶受權驗證
            if (filterContext.HttpContext.Request.Cookies[CookieUserName] == null ||
                filterContext.HttpContext.Session == null ||
                filterContext.HttpContext.Session[filterContext.HttpContext.Session.SessionID] == null)
            {
                //跳轉到登陸界面
                filterContext.Result = new RedirectResult(ManageLoginUrl);
            }
            else
            {
                var cookieUserName = filterContext.HttpContext.Request.Cookies[CookieUserName].Value;
                var currentUserSession = filterContext.HttpContext.Session[filterContext.HttpContext.Session.SessionID] as UserSession;
                if (string.IsNullOrEmpty(cookieUserName) || currentUserSession == null)
                {
                    filterContext.Result = new RedirectResult(ManageLoginUrl);
                }
                else
                {
                    if (string.Compare(cookieUserName, currentUserSession.UserName, StringComparison.CurrentCultureIgnoreCase) != 0 ||
                        String.Compare(currentUserSession.LoginIpAddress, filterContext.HttpContext.Request.UserHostAddress, StringComparison.CurrentCultureIgnoreCase) != 0)
                    {
                        filterContext.Result = new RedirectResult(ManageLoginUrl);
                    }
                }
            }

            base.OnActionExecuting(filterContext);
        }
    }

受權過濾器,使用全局變量。能夠設置某幾個方法,不進行受權。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
    public class NoCompressAttribute : Attribute
    {
    }

使用內建過濾器

AuthorizeAttribute屬性有兩個:

名稱 | 類型 | 描述
Users | string | 一個逗號分隔的用戶名列表,容許這些用戶訪問
Roles | string | 一個逗號分隔的角色列表。用戶必須至少是這些角色之一

使用 [Authorize(Users="admin")] 限制登陸驗證,使用 [AllowAnonymous] 容許匿名訪問

受權過濾器如驗證失敗,會返回一個401錯誤。

須要在配置文件中,填寫錯誤返回地址:

<system.web>
<authentication mode="Forms">
      <forms loginUrl="~/Account/LogOn" timeout="2880"/>
    </authentication>
</system.web>

而後在登陸頁面,對用戶進行驗證,註冊到過濾器中

 FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);

添加此句則能夠將用戶添加到權限驗證中。

FormsAuthentication.SignOut();

註銷驗證

建立角色

以上方法是建立用戶,內建的受權過濾器能夠指定訪問角色。[Authorize(Users = "Chenxy",Roles = "Admin")]

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "Chenxy", DateTime.Now, DateTime.Now.AddMinutes(1), true, "Admin");
            var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
            cookie.HttpOnly = true;
            HttpContext.Response.Cookies.Add(cookie);

認證過濾器

認證過濾器是MVC5的新屬性,對控制器和動做如何驗證用戶提供了細粒度控制。

AuthenticationChallengeContext屬性有兩個:

名稱 | 描述
ActionDescriptor | 返回描述動做方法的ActionDescriptor,運用過濾器
Result | 設置表示認證質疑結果的ActionReult

異常過濾器

IExceptionFilter,當一個未處理異常出現時,OnException方法被調用。參數是ExceptionContext對象,派生於ControllerContext。

ControllerContext是控制器上下文,提供了許多屬性

1 名稱 | 類型 | 描述
2 Controller | ControllerBase | 返回請求的控制器對象
3 HttpContext | HttpContextBase | 提供對請求細節的訪問,以及對響應的訪問
4 IsChildAction | bool | 是否子動做
5 RequestContext | RequestContext | 提供對HttpContext和路由數據的訪問
6 RouteData | RouteData | 返回請求的路由數據

ExceptionContext額外屬性有

1 名稱 | 類型 | 描述
2 ActionDescriptor | ActionDescriptor | 提供動做方法的細節
3 Result | ActionResult | 動做方法的結果
4 Exception | Exception | 異常
5 ExceptionHandled | bool | 是否已處理過此異常

異常過濾器最簡單的辦法是派生FilterAttribute

public class RangeExceptionAttribute : FilterAttribute, IExceptionFilter
    {
        public void OnException(ExceptionContext filterContext)
        {
            if (!filterContext.ExceptionHandled && filterContext.Exception is ArgumentException)
            {
                filterContext.Result = new RedirectResult("~/Content/RangeErrorPage.html"); //返回靜態頁
                string val = (filterContext.Exception as ArgumentException).Message; //返回動態視圖
                filterContext.Result = new ViewResult { ViewName = "RangeError", ViewData = new ViewDataDictionary<string>(val) };
                filterContext.ExceptionHandled = true;
            }
        }
    }

使用方法同上

使用內建過濾器

Web.Config添加過濾器屬性

<customErrors mode="On" defaultRedirect="~/Content/RangeErrorPage.html"></customErrors>

而後在方法上添加註解

[HandleError(ExceptionType=typeof(ArgumentOutOfRangeException),View = "RangeError")]

動做過濾器

接口定義了兩個方法,OnActionExecuting方法在動做方法調用以前調用。OnActionRexcuted以後調用。

派生FilterAttribute最方便的方法。

調用方法同上,只須要在方法中寫處理內容

結果過濾器

接口定義了兩個方法,OnResultExcuting方法在動做方法調用以前調用。OnResultExecuted以後調用。

派生FilterAttribute最方便的方法。

調用方法同上,只須要在方法中寫處理內容

組合動做過濾器和結果過濾器

public class ActionFilterAttribute : FilterAttribute, IActionFilter, IResultFilter
    {
        public void OnActionExecuted(ActionExecutedContext filterContext)
        {
            throw new NotImplementedException();
        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            throw new NotImplementedException();
        }

        public void OnResultExecuted(ResultExecutedContext filterContext)
        {
            throw new NotImplementedException();
        }

        public void OnResultExecuting(ResultExecutingContext filterContext)
        {
            throw new NotImplementedException();
        }
    }

無註解過濾器

能夠在控制器方法中,重寫過濾器特性。直接overried便可找到對應的接口方法。

全局過濾器

能夠設置全局過濾器,應用於全部方法。

public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
    }

能夠在此處進行特性注入。

視圖 

Razor引擎支持分段概念,讓你可以提供一個佈局中的內容區域。

使用@RenderSection("Header")能夠定義填充區域

使用@section Header{} 能夠實際填充此區域

使用@RenderBody()能夠定義繼承母版頁的頁面全部區域外的內容。

如視圖頁面須要繼承佈局頁,使用 Layout

強類型視圖,須要在上方添加對應Model。@model WebServices.Models.Reservation

視圖中,可使用ViewContext上下文

1 名稱 | 描述
2 Controller | 返回處理當前請求的IController實現
3 RequestContext | 返回當前請求的細節
4 RouteData | 爲當前請求返回的路由數據
5 TempData | 返回和請求相關的臨時數據
6 View | 返回視圖實現
7 ViewBag | 返回視圖包
8 ViewData | 返回視圖模型數據字典

強類型視圖

在視圖的上方填寫:@model 能夠定義強類型視圖。

針對公用的命名空間,能夠用全局配置,來制定固有的命名空間。

web/view/web.config 
<system.web.webPages.razor>
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
      <namespaces>
        <add namespace="MvcDemo.Models" />
      </namespaces>
    </pages>
  </system.web.webPages.razor>

分部視圖

如需在應用程序中多個不一樣的地方,使用一樣的Razor標籤和HTML標記片斷。分部視圖是很好的辦法。

須要在頁面中引用,@Html.Partial("MyParital")

分部視圖也可定義強類型,並經過調用參數進行傳遞實體數據。

子動做

子動做是經過視圖調用的動做方法。當你但願將某種控制器邏輯應用於多個地方時,子動做可讓你避免重複的控制器邏輯。

使用 [ChildActionOnly] 能夠標記爲子動做。防止用戶做爲用戶請求的結果。

輔助器 

內聯輔助器

能夠在視圖中進行定義

@helper ListArrayItems(string[] items)
{
   foreach(string str in items)
  {
    <b>@str</b>
  }      
}

@ListArrayItems(ViewBag.Fruits) 使用 

外部輔助器

可使用擴展方法的形式,來設置外部輔助器。在頁面中引用便可。

Go(this HtmlHelper html)

內建輔助器

詳細參考:https://msdn.microsoft.com/zh-cn/library/system.web.mvc.htmlhelper(v=vs.118).aspx

@Html.BeginForm(){}

輸入輔助器

Html.CheckBox("myCheckbox",false)。根據屬性名稱來建立輔助器,能夠自動綁定數據,檢查位置以下:

ViewBag.DataValue

@Model.DataValue

強類型輔助器 

Html.CheckBox(m=>m.DataValue) 

建立Select元素

Html.DropDownListFor(x=>x.Gender,new SelectList(new[] {"",""}))

或者使用反射

new SelectList(Enum.GetNames(typeof(Role)))

模板輔助器

模板輔助器能夠生成特定的HTML標籤

1 輔助器 | 示例 | 描述
2 Display | Html.Display("FirstName") | 渲染指定模型屬性的只讀視圖
3 DisplayFor | Html.DisplayFor(m=>m.FirstName) | 強類型版本
4 Editor | Html.Editor("FirstName") | 渲染指定模型屬性的編輯視圖
5 EditorFor | Html.EditorFor(m=>m.FirstName) | 強類型版本
6 Label | Html.Label("FirstName") | 渲染Label元素,顯示屬性名稱
7 LabelFor | Html.LabelFor("FirstName") | 強類型版本

顯示屬性名稱和屬性值的列,應寫爲

<div>
        @Html.LabelFor(m => m.ClientName)
        @Html.DisplayFor(m => m.ClientName)
    </div>

總體模板輔助器

1 輔助器 | 示例 | 描述
2 DisplayForMmodel | Html.DisplayForModel() | 渲染整個模型對象的只讀視圖
3 EditorForModel | Html.EditorForModel() | 渲染整個模型對象的編輯器元素
4 LabelForModel | Html.LabelForModel() | 將模型對象的名稱渲染成一個label元素

例子:顯示一個修改頁面

<div class="form-group">
    @Html.LabelForModel()
    @using (Html.BeginForm("Add", "Home"))
    {
        @Html.EditorForModel()
        <button type="submit" class="btn btn-primary">Save</button>
    }
    <div>
        @Html.LabelFor(m => m.ClientName)
        @Html.DisplayFor(m => m.ClientName)
    </div>
</div>

因總體模板生成的頁面,是自動感應全部字段並顯示。須要經過特性來控制,屬性的顯示與顯示方式。

模型元數據 

模型元數據經過C#註解屬性來表示,經過註解屬性及其參數,給視圖輔助器提供一系列命令。

控制可見性

使用HiddenInputAttribute來控制屬性的隱藏。詳細參見:https://msdn.microsoft.com/zh-cn/library/system.web.mvc(v=vs.118).aspx

 public class Reservation
    {
        [HiddenInput]
        public int ReservationId { get; set; }
        public string ClientName { get; set; }
        public string Location { get; set; }
    }

 

此時使用EditorForModel 與 EditorFor 就會將此屬性改成只讀。

若是想徹底隱藏,使用 [HiddenInput(DisplayValue = false)]

若是想禁止建立,使用[ScaffoldColumn(false)]。注意,此屬性不會將內容進行生成。而HiddenInput是生成隱藏域。

控制屬性名

可使用DisplayName來控制顯示的屬性名。詳細參見:https://msdn.microsoft.com/zh-CN/library/system.componentmodel(v=vs.110).aspx

也可使用Display(Name)來控制顯示的屬性名。二者的區別是,DisplayName能夠控制實體類的名字。

Display(Name)的命名空間,有不少相似的控制特性:https://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations(v=vs.110).aspx

 public class Reservation
    {
        [HiddenInput(DisplayValue = false)]
        public int ReservationId { get; set; }
        [DisplayName("客戶名稱")]
        public string ClientName { get; set; }
        [Display(Name = "位置")]
        public string Location { get; set; }
    }

控制顯示值的類型

使用DataType註解能夠控制參數值的類型。

[DataType(DataType.Text)]  其中的枚舉有多種定義。 

控制渲染模板

使用UIHint註解能夠控制渲染模板的類型。當與編輯輔助器一塊兒使用的時候,就能夠控制渲染的HTML元素了

[UIHint("String")]
 public string ClientName { get; set; }

內建MVC框架視圖模板

 1 值 | 編輯輔助器 | 視圖輔助器
 2 Boolean | 渲染一個複選框 | 渲染一個只讀複選框
 3 Collection | 爲IEnumerable中的每一個元素渲染一個相應的模板 | 同編輯輔助器
 4 Decimal | 渲染一個單行文本框,數據值格式化,顯示兩位小數 | 格式化兩位小數的數據值
 5 DateTime | 渲染datetime標籤屬性 | 渲染DateTime變量的完整值
 6 Date | 渲染date標籤屬性 | 渲染DateTime日期成分
 7 EmailAddress | 渲染一個單行文本框 | 渲染一個mailto的URL
 8 HiddenInput | 建立隱藏的input元素 | 建立隱藏元素
 9 Html | 渲染單行文本框 | 超連接
10 MultilineText | 多行文本框 | 數據值
11 Number | number的input元素 | 數據值
12 Object | 自動推算 | 自動推算
13 Password | 密碼單行文本 | 明文字符
14 String | 單行文本框 | 數據值
15 Text | 等同String | 等同String
16 Tel | tel的input元素 | 數據值
17 Time | time的input元素 | DateTime變量的時間成分
18 Url | 單行文本框 | 超連接
19 Enum | 枚舉 | 枚舉

控制分部視圖渲染

不少時候,實體是由軟件自動生成的。這個時候,若是長期寫這種特性會有些繁瑣。

針對這種狀況,能夠建立一個分部視圖,由分部視圖進行定義,而後關聯主要的視圖。

[MetadataType(typeof(PersonMetaData))]
    public partial class Reservation
    {}

URL

建立基本連接和URL

1 描述 | 示例 | 輸出效果
2 相對於應用程序的URL | Url.Content("/Content/Site.css") | /Content/Site.css
3 連接到指定的動做/控制器 | Html.ActionLink("My Link","Index","Home") | <a href="/">My Link</a>
4 動做URL | Url.Action("GetPeople","People") | /People/GetPeople
5 使用路由數據的URL | Url.RouteUrl(new{controller="People",action="GetPeople"}) | /People/GetPeople
6 使用路由數據的連接 | Html.RouteUrl("My Link",new{controller="People",action="GetPeople"}) | <a href="/People/GetPeople">My Link</a>
7 連接到指定的路由 | Html.RouteLink("My Link","FormRoute",new{controller="People",action="GetPeople"}) | <a href="/app/forms/People/GetPeople">My Link</a>

漸進式Ajax 

Ajax首先須要修改Web.Config 在,appSettings 添加 <add key="UnobtrusiveJavaScriptEnabled" value="true" />

而後須要添加 Microsoft.jQuery.Unobtrusive.Ajax nuget包

最後須要添加對應的js引用,<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>

漸進式Ajax核心在於Ajax.BeginForm輔助器,能夠接受一個AjaxOptions對象。

AjaxOptions類定義詳細參考:https://msdn.microsoft.com/zh-cn/library/system.web.mvc.ajax.ajaxoptions(v=vs.118).aspx

@using Chenxy.Mvc.Controllers
@model string
@{
    ViewBag.Title = "GetPeople";
    AjaxOptions ajaxOpts = new AjaxOptions
    {
        UpdateTargetId = "tableBody",
        LoadingElementId = "loading",
        LoadingElementDuration = 50,
        Url = Url.Action("GetPeopleData"),
        Confirm = "您是否要查詢"
    };
}
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.min.js"></script>
<h2>GetPeople</h2>
<table>
    <thead>
        <tr>
            <th>First</th>
            <th>Role</th>
        </tr>
    </thead>
    <tbody id="tableBody">
        @Html.Action("GetPeopleData", new { selectedRole = Model })
    </tbody>
</table>
@using (Ajax.BeginForm("GetPeopleData", ajaxOpts))
{
    @Html.DropDownList("selecedRole", new SelectList(new[] { "All" }.Concat(Enum.GetNames(typeof(Role)))))
    <button type="submit">Sub</button>
}
<div id="loading" class="load" style="position:absolute; top:0; left:0; right:0; bottom:0; z-index:9; opacity:0.8; vertical-align: middle; text-align: center; background-color:currentColor; display:none;">
    <img alt="" src="" id="img" style="height:99%; visibility:hidden;">
    <img src="~/Images/loding.gif" style="opacity:0.7" />
</div>
GetPeople
@using Chenxy.Mvc.Controllers
@model IEnumerable<Person>
@foreach (Person p in Model)
{
    <tr>
        <td>@p.FirstName</td>
        <td>@p.Role</td>
    </tr>
}
GetPeopleData
public enum Role
    {
        Admin,
        User,
        Guest
    }
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Role Role { get; set; }
    }
    public class HomeController : Controller
    {
        private Person[] personData = {
            new Person { FirstName="Adam", LastName="Freeman",Role=Role.Admin },
            new Person { FirstName="Jacqui", LastName="Freeman",Role=Role.User },
            new Person { FirstName="John", LastName="Freeman",Role=Role.User },
            new Person { FirstName="Anne", LastName="Freeman",Role=Role.Guest },
        };
        public PartialViewResult GetPeopleData(string selecedRole = "All")
        {
            IEnumerable<Person> data = personData;
            if (selecedRole != "All")
            {
                Role selected = (Role)Enum.Parse(typeof(Role), selecedRole);
                data = personData.Where(p => p.Role == selected);
            }
            return PartialView(data);
        }

        public ActionResult GetPeople(string selectedRole = "All")
        {
            return View((object)selectedRole);
        }
}
Conotroller

添加Url能夠進行降級,以避免瀏覽器禁用javascript,沒法發送。

Ajax.ActionLink,能夠建立Ajax的超連接。點擊之後,也會異步執行Ajax。

使用Ajax回調

支持四個回調方法,這四個方法都可執行一個Javascript函數。

屬性 | jQuery事件 | 描述
OnBegin | beforeSend | 發送以前調用
OnComplete | commplete | 成功時調用
OnFailure | error | 失敗時調用
OnSuccess | success | 完成時調用,無論成功失敗

經過OnSuccess,也可使用Json進行數據插入操做。

首先須要修改控制器,讓他返回Json數據。

可使用Json方法,進行序列化操做。

public ActionResult GetPeopleData(string selectedRole = "All")
        {
            IEnumerable<Person> data = personData;
            if (selectedRole != "All")
            {
                Role selected = (Role)Enum.Parse(typeof(Role), selectedRole);
                data = personData.Where(p => p.Role == selected);
            }
            if (Request.IsAjaxRequest())
            {
                var formattedData = data.Select(p => new
                {
                    FirstName = p.FirstName,
                    LastName = p.LastName,
                    Role = p.Role.ToString()
                });
                return Json(formattedData, JsonRequestBehavior.AllowGet);
            }
            else
            {
                return PartialView(data);
            }
        }

設置返回Json,而後添加調用方法

<script type="text/javascript">
    function processData(data) {
        var target = $("#tableBody");
        target.empty();
        for (var i = 0; i < data.length; i++) {
            var person = data[i];
            target.append("<tr><td>" + person.FirstName + "</td><td>" + person.LastName + "</td><td>" + person.Role + "</td></tr>");
        }
    }
</script>

模型綁定

延遲加載

在model層中,的實體類或集合前面添加 virtual。會啓動延遲加載。

public class Metric
    {
        public int MetricId { get; set; }

        public string Type { get; set; }

        public virtual ICollection<Goal> Goals { get; set; }

        public virtual ICollection<GroupGoal> GroupGoals { get; set; }
    }

 

默認模型綁定器

參數綁定,會根據必定順序來進行查找綁定。

1 源 | 描述
2 Request.Form | form表單元素提供
3 RouteData.Values | 應用路由提供
4 Request.QueryString | URL字符串數據
5 Request.Files | 上傳文件

自定義前綴

當你但願將模型綁定到另外一個對象的時候,好比說,模型類有另外一個模型類的實體。

可使用,[Bind(Prefix="HomeAddress")] 定義在參數旁邊。其中HomeAddress,爲模型類中另外一個模型類的屬性名稱

public ActionResult DisplaySummary([Bind(Prefix="HomeAddress")]AddressSummary summary)
{}

選擇綁定 

若是你不但願模型中某個值被指定,能夠進行選擇性綁定。

使用Exclude能夠排除一個屬性,Include能夠指定一個屬性。

public ActionResult DisplaySummary([Bind(Prefix="HomeAddress",Exclude="Country")]AddressSummary summary)
{}

public ActionResult DisplaySummary([Bind(Include="City")]AddressSummary summary)
{}

模型驗證

當你須要驗證數據有效性時,可使用特性爲實體進行驗證限制。

驗證模型

在進行方法執行時,須要驗證數據有效性。

使用ModelState.AddModelError指定有問題的屬性名和錯誤信息。

使用ModelState.IsValidField屬性,來檢查是否添加過此異常。

使用ModelState.IsValid 來驗證是否有異常。

[HttpPost]
        public ViewResult MakeBooking(Appointment appt)
        {
            if (string.IsNullOrEmpty(appt.ClientName))
            {
                ModelState.AddModelError("ClientName", "Please enter yourname");
            }
            if (ModelState.IsValidField("Date") && DateTime.Now > appt.Date)
            {
                ModelState.AddModelError("Date", "Please enter a date inthe future");
            }
            if (ModelState.IsValidField("ClientName") && ModelState.IsValidField("Date") && appt.ClientName == "Joe" && appt.Date.DayOfWeek == DayOfWeek.Monday) {
                ModelState.AddModelError("", "Joe cannot book appointments onMondys");
            }
            if (ModelState.IsValid)
                return View("Completed", appt);
            else
                return View();
        }

在頁面須要添加一個顯示錯誤異常的地方。

@Html.ValidationSummary() 會在定義的位置,使用無序數列顯示你添加的錯誤異常。

ValidationSummary 有四個重載方法

方法 | 描述
ValidationSummary() | 顯示全部錯誤
ValidationSummary(bool) | 參數爲true,只顯示模型級錯誤
ValidationSummary(string) | 在錯誤以前,添加一條信息
ValidationSummary(bool,string) | 在模型級錯誤以前添加一條信息

添加錯誤時,不指定屬性則默認模型級錯誤

 if (ModelState.IsValidField("ClientName") && ModelState.IsValidField("Date") && appt.ClientName == "Joe" && appt.Date.DayOfWeek == DayOfWeek.Monday) {
                ModelState.AddModelError("", "Joe cannot book appointments onMondys");
            }

顯示屬性及驗證信息

當你設爲模型級錯誤時,能夠針對屬性進行細粒度分劃。

使用Html.ValidationMessageFor,能夠設置針對屬性的錯誤信息提示。

<span>@Html.ValidationMessageFor(m => m.ClientName)</span>

特性驗證

可使用特性,來進行內建的驗證

 public class Appointment
    {
        [Required]
        [StringLength(10, MinimumLength = 3)]
        public string ClientName { get; set; }

        [DataType(DataType.Date)]
        public DateTime Date { get; set; }

        [Range(typeof(bool), "true", "true", ErrorMessage = "You must accept theterms")]
        public bool TermsAccepted { get; set; }
    }

當屬性不知足時,會自動進行錯誤添加顯示。

內建的驗證屬性,詳細信息:https://msdn.microsoft.com/zh-cn/library/system.componentmodel.dataannotations(v=vs.110).aspx

屬性 | 實例 | 描述
Compare | [Compare("MyOther Property")] | 兩個屬性必須有一樣的值
Range | [Range(10,20)] | 指定最大值和最小值
RegularExpression | [RegularExression("pattern")] | 匹配指定的正則表達式
Required | [Required] | 非空值
StringLength | [StringLength(10)] | 最大長度

客戶端驗證

添加Nuget包,並引用能夠進行客戶端驗證

jQuery\jQuery.Validation\Microsoft.jQuery.Unobtrusive.Validation

而且在Web.Config中將如下屬性設置爲true

<add key="ClientValidationEnabled" value="true" />

<add key="UnobtrusiveJavaScriptEnabled" value="true" />

FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, "Chenxy", DateTime.Now.AddMinutes(1), DateTime.Now, true, "Admin");

安全驗證

ValidateAntiForgeryToken 能夠防止CSRF(跨網站請求僞造),可是防不住XSS(跨站腳本攻擊)。

用法:在View->Form表單中:<%:Html.AntiForgeryToken()%>

        在Controller->Action動做上:[ValidateAntiForgeryToken]

原理:

一、<%:Html.AntiForgeryToken()%>這個方法會生成一個隱藏域:<inputname="__RequestVerificationToken" type="hidden" value="7FTM...sdLr1" />而且會將一個以"__RequestVerificationToken「爲KEY的COOKIE給控制層。

二、[ValidateAntiForgeryToken],根據傳過來的令牌進行對比,若是相同,則容許訪問,若是不一樣則拒絕訪問。

關鍵:ValidateAntiForgeryToken只針對POST請求。

換句話說,[ValidateAntiForgeryToken]必須和[HttpPost]同時加在一個ACTION上才能夠正常使用。

WebAPI

http://www.cnblogs.com/chenxygx/p/6628714.html

捆綁包

針對腳本或者樣式表,能夠將多個文件進行單一處理。這樣會下降請求數量。

首先須要添加 Optimization 這個NuGet包。

而後進行捆綁包的定義(須要在Global.asax中,進行運行)

 public class BundleConfig
    {
        public static void RegisterBundles(BundleCollection bundles)
        {
            bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
                      "~/Scripts/bootstrap.js",
                      "~/Scripts/respond.js"));
            bundles.Add(new StyleBundle("~/Content/css").Include(
                      "~/Content/bootstrap.css",
                      "~/Content/site.css"));
        }
    }
捆綁包

使用方法:

@Styles.Render("~/Content/css")    

@Scripts.Render("~/bundles/scripts")

PostRequestHandlerExecute

PreRequestHandlerExecute

相關文章
相關標籤/搜索