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

相关文章
相关标签/搜索