ASP.NET MVC 是微软官方提供的以MVC模式为基础的ASP.NET Web应用程序(Web Application)框架,它由Castle的MonoRail而来。
MVC 编程模式
MVC 是三种 ASP.NET 编程模式中的一种。
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式。
(1)Model(模型)表示应用程序核心(比如数据库记录列表)。
(2)View(视图)显示数据(数据库记录)。
(3)Controller(控制器)处理输入(写入数据库记录)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
新建一个ASP.NET MVC4应用程序,结构如下图所示:
对各个文件夹的说明:
(1)App_Data 文件夹用于存储应用程序数据。
(2)Content 文件夹用于存放静态文件,比如样式表(CSS 文件)、图标和图像。
(3)Controllers 文件夹包含负责处理用户输入和相应的控制器类。
(4)Models 文件夹包含表示应用程序模型的类。模型控制并操作应用程序的数据。
(5)Views 文件夹用于存储与应用程序的显示相关的 HTML 文件(用户界面)。
(6)Scripts 文件夹存储应用程序的 JavaScript 文件。
下面就主要的Controller、Model和View做出说明。
一、控制器
1、描述
控制器(Controller)主要负责响应用户的输入,并在响应时修改模型(Model)。通过这种方式,控制器主要关注的是应用程序流、输入数据的处理,以及对相关视图(View)输出数据的提供。
2、简单控制器
新建一个ASP.NET MVC4应用程序,会自动生成一个HomeController和AccountController。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
}
}
直接按F5运行程序即可看到index试图中的内容,此时浏览器的URL为:
http://localhost:4573,或者修改浏览器地址为:
http://localhost:4573/home
http://localhost:4573/home/index
程序默认会托管在VS2013自带的IIS中,采用的端口号为4573。如果程序托管在MyWeb.com中,则URL应为:
http://MyWeb.com/home/index。
3、控制器操作中的参数
前面的例子Action中返回的是字符串常量,下面就让它们通过相应URL传进来的参数动态地执行操作。
在这里,我们使用HttpUtility.HtmlEncode来预处理用户输入。这样就能阻止用户用链接向视图中注入JavaScriptd代码或HTML标记,比如/home/sayhello?content=<script>window.location='http://www.baidu.com'</script>。
public string SayHello(string content)
{
string message = HttpUtility.HtmlEncode("Hello " + content);
return message;
}
public string Details(int Id)
{
return "ID:" + Id;
}
4、路由---将URL映射到动作
框架是如何知道将URL映射到一个特定的控制动作的?答案就在Global.asax文件的RegisterRoutes方法中。该方法定义了将一个URL模式映射到控制器或动作的路由。
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing; namespace MvcApplication1
{
// 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
// 请访问 http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
}
- 在RegisterRoutes方法上按F12,转到该方法的定义,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing; namespace MvcApplication1
{
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 }
);
}
}
}
5、控制器总结
(1)不需要做任何配置,只需浏览到/控制器/动作URL即可;
(2)控制器是一个非常简单的类,继承自System.Web.Mvc.Controller类;
(3)用控制器在浏览器中显示文本,没有用到View或Model。
二、视图
1、作用
提供用户界面。一个控制器可以对应多个试图,一个视图可以被多个控制器使用。
在Action名上右键→添加试图→View1。
2、指定视图
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>View1</title>
</head>
<body>
<div>
<h2>@ViewBag.Message</h2>
</div>
</body>
</html>
3、ViewData和ViewBag的区别于联系
在前面的例子中,使用了ViewBag的Message属性从控制器往视图传递数据,ViewData是一个特殊的字典类,可以按标准语法进行使用:ViewData["CurrentTime"]=DateTime.Now;
这种语法也可以用动态封装器ViewBag:ViewBag.CurrentTime=DateTime.Now;
注意:
(1)尽管只有当要访问的关键字是有效的C#标识符是,ViewBag才起作用,如在ViewData["Key With Spaces"]就不能使用ViewBag访问,编译不通过;
(2)动态值不能作为一个参数传递给扩展方法。因为C#编译器为了选择正确的扩展方法,在编译是必须知道每个参数真正类型。如:@Html.TextBox("name",ViewBag.Name)不会编译通过,可以改为:
①@Html.TextBox("name",ViewData["Name"])
②@Html.TextBox("name",(string)ViewBag.Name)
4、强类型视图
现在需要编写一个显示Album实例列表的视图。一简单的方法就是通过ViewBag属性把那些Album实例添加到视图数据字典中,然后在视图中迭代他们。
(1)首先,在Models文件夹下新建一个Album类,为了简单起见,只定义一个Title属性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcApplication1.Models
{
public class Album
{
public string Title { get; set; }
}
}
- (2)控制器
public ActionResult List()
{
var album = new List<Album>();
for (int i = ; i < ; i++)
{
album.Add(new Album { Title = "Product " + i.ToString() });
}
//一、使用ViewBag传值
//ViewBag.Album = album;
//return View("ListView");
//二、使用ViewData传值
ViewData["Album"] = album;
return View("ListView");
}
(3)视图
在List上右键→添加视图。
@{
Layout = null;
}
<!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ListView</title>
</head>
<body>
<div>
<ul>
@*一、使用ViewBag传值*@
@*@foreach(MvcApplication1.Models.Album a in (ViewBag.Album as IEnumerable
<MvcApplication1.Models.Album>))
{
<li>@a.Title</li>
}*@
@*二、使用ViewBag传值*@ @foreach (MvcApplication1.Models.Album a in (ViewData["Album"] as IEnumerable<MvcApplication1.Models.Album>))
{
<li>@a.Title</li>
} </ul>
<pre name="code" class="html">
<ul>
@foreach(dynamic d in ViewBag.Album)
{
<li>@d.Title</li>
} </ul>
</div>
</body>
</html>
运行效果:
<ul>
@foreach (dynamic d in ViewBag.Album)
{
<li>@d.Title</li>
}
</ul>
请记住,ViewData是ViewDataDictionary类型的,它有一个额外的Model属性,可以用来在视图中获取指定的模型对象。由于在ViewData中只能传递一个模型对象,因此,我们利用这一点可以很容易的实现向视图传递一个特定的类对象。
在Controller方法中,可以通过重载的List方法中传递模型实例来指定模型,代码如下:
public ActionResult List()
{
var album = new List<Album>();
for (int i=;i < ; i++)
{
album.Add(new Album { Title="Product " + i.ToString() });
}
ViewData["Album"]=album;
return View("ListView",album);
} @model IEnumerable<MvcApplication1.Models.Album> @{
Layout = null;
} <!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ListView</title>
</head>
<body>
<div>
<ul>
@foreach(dynamic d in Model)
{
<li>@d.Title</li>
}
</ul>
</div>
</body>
</html>
如果不想输入模型类型的完全限定类型名,可使用using关键字声明,如下所示:
@using MvcApplication1.Models @model IEnumerable<Album> @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ListView</title>
</head>
<body>
<div>
<ul>
@foreach (dynamic d in Model)
{
<li>@d.Title</li>
}
</ul>
</div>
</body>
</html>
对于在视图中经常使用的名称空间,一个较好的方法就是在Views目录下的web.config文件中声明。
<system.web.webPages.razor>
<host factoryType = "System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
< pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="MvcApplication1.Models"/>
</namespaces>
</pages>
</system.web.webPages.razor>
5、Razor视图引擎
5.1 先来看一个简单的例子。
@{
Layout = null;
} @{
var items = new string[] { "one", "two", "three" };
} <!DOCTYPE html>
<html>
<head>
<title>ListView</title>
</head> <body>
<div>
<ul>
@foreach (var item in items)
{
<li>@item</li>
}
</ul>
</div>
</body>
</html>
5.2 Html编码
因为在很多情况下需要用视图显示用户输入,如博客评论等,所以总是存在着潜在的跨站脚本注入攻击(也成XSS),值得称赞的是,Razor表达式是Html自动编码的,如下不会弹出一个警示框,而是直接显示html代码。
@{
string message = "<script>alert('haacked!');</script>";
}
然而,如果想要展示Html标签,就要返回一个System.Web.IHtml对象的实例,Razor并不对它进行编码。当然也可以创建一个HtmlStringl实例或者使用Html.Raw便捷方法。
@{ string message = "<script>alert('haacked!');</script>"; }
<span>@Html.Raw(message)</span>
- 效果:
虽然这种自动的HTML编码通过对一HTML形式显示的用户输入进行编码能有效的缓和XSS的脆弱性,但是对于在JavaScript中时远远不够的。例如:
<script type="text/javascript">
$(function ()
{
var message = 'Hello @Ajax.JavaScriptStringEncode(ViewBag.Message)';
$("#message").html(message).show('slow'); });
</script>
- 当在JavaScript中将用户提供的值赋给变量时,要使用JavaScript字符串编码而不仅仅是HTML编码,记住这一点是很重要的,也就是要用@Ajax.JavaScriptStringEncode方法对用户输入进行编码。
5.3 布局
Razor的布局有助于使用应用程序中的多个视图保持一致的外观,可使用布局为网站定义公共模板(或只是其中的一部分),公共模板包含一个或多个占位符,应用程序中的其他视图为他们提供内容。
下面看一个非常简单的布局,新建一个名称为MyLayout.cshtml的视图,由于要作为公共模板,所以将其放在/Views/Shared/路径下。
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head>
<body>
<h1>@ViewBag.Title</h1>
<div id="Container">@RenderBody()</div>
</body>
</html>
- 其中的@RenderBody()称为占位符,用来标记使用这个模板的视图将渲染他们的主要内容的位置。
接下来新建一个视图,将使用其作为模板。
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
ViewBag.Title = "GoodLuck";
}
<p>This is the main content</p>
- 运行效果如下:
布局可能有多个节,例如下面示例在前面的布局基础上添加了一个页脚节:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head>
<body>
<h1>@ViewBag.Title</h1>
<div id="Container">@RenderBody()</div>
<footer>@RenderSection("Footer")</footer>
</body>
</html>
- 在不做任何改变的情况下再次运行前面的视图,将会抛出一个异常,提示没有定义的Footer节。
默认情况下,视图必须为布局中定义的没一个节提供相应的内容。更新后的View1视图如下所示:
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
ViewBag.Title = "GoodLuck";
} <p>This is the main content</p> @section Footer{
This is the <strong>footer</strong>.
}
RenderSection方法有一个重载的版本,允许在布局中指定不需要的节,可以给required参数传递一个false值来标记Footer节是可选的:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head> <body>
<h1>@ViewBag.Title</h1>
<div id="Container">@RenderBody()</div>
<footer>@RenderSection("Footer", required: false)</footer>
</body>
</html>
5.4 ViewStart
在前面的例子中,每一个视图都是用Layout属性来指定它的布局,如果多个视图使用同一个布局,就会产生冗余,并且很难维护。
_ViewStart.cshtml页面可用来消除这种冗余,这个文件的代码先于同目录下任何视图代码的执行,这个文件也可以递归地应用到子目录下的任何视图。
@{
Layout="~/Views/Shared/_Layout.cshtml";
}
因为这个代码先于任何视图执行,所以一个视图可以重写Layout属性的默认值,从而重新选择一个不同的布局。
5.4 指定部分视图
除了返回视图之外,操作方法也可以通过PartialView方法以PartialResult的形式返回部分视图。
The end
ASP.NET MVC 是微软官方提供的以MVC模式为基础的ASP.NET Web应用程序(Web Application)框架,它由Castle的MonoRail而来。
MVC 编程模式
MVC 是三种 ASP.NET 编程模式中的一种。
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式。
(1)Model(模型)表示应用程序核心(比如数据库记录列表)。
(2)View(视图)显示数据(数据库记录)。
(3)Controller(控制器)处理输入(写入数据库记录)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
新建一个ASP.NET MVC4应用程序,结构如下图所示:
对各个文件夹的说明:
(1)App_Data 文件夹用于存储应用程序数据。
(2)Content 文件夹用于存放静态文件,比如样式表(CSS 文件)、图标和图像。
(3)Controllers 文件夹包含负责处理用户输入和相应的控制器类。
(4)Models 文件夹包含表示应用程序模型的类。模型控制并操作应用程序的数据。
(5)Views 文件夹用于存储与应用程序的显示相关的 HTML 文件(用户界面)。
(6)Scripts 文件夹存储应用程序的 JavaScript 文件。
下面就主要的Controller、Model和View做出说明。
一、控制器
1、描述
控制器(Controller)主要负责响应用户的输入,并在响应时修改模型(Model)。通过这种方式,控制器主要关注的是应用程序流、输入数据的处理,以及对相关视图(View)输出数据的提供。
2、简单控制器
新建一个ASP.NET MVC4应用程序,会自动生成一个HomeController和AccountController。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc; namespace MvcApplication1.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
}
}
直接按F5运行程序即可看到index试图中的内容,此时浏览器的URL为:
http://localhost:4573,或者修改浏览器地址为:
http://localhost:4573/home
http://localhost:4573/home/index
程序默认会托管在VS2013自带的IIS中,采用的端口号为4573。如果程序托管在MyWeb.com中,则URL应为:
http://MyWeb.com/home/index。
3、控制器操作中的参数
前面的例子Action中返回的是字符串常量,下面就让它们通过相应URL传进来的参数动态地执行操作。
在这里,我们使用HttpUtility.HtmlEncode来预处理用户输入。这样就能阻止用户用链接向视图中注入JavaScriptd代码或HTML标记,比如/home/sayhello?content=<script>window.location='http://www.baidu.com'</script>。
public string SayHello(string content)
{
string message = HttpUtility.HtmlEncode("Hello " + content);
return message;
}
public string Details(int Id)
{
return "ID:" + Id;
}
4、路由---将URL映射到动作
框架是如何知道将URL映射到一个特定的控制动作的?答案就在Global.asax文件的RegisterRoutes方法中。该方法定义了将一个URL模式映射到控制器或动作的路由。
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing; namespace MvcApplication1
{
// 注意: 有关启用 IIS6 或 IIS7 经典模式的说明,
// 请访问 http://go.microsoft.com/?LinkId=9394801
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AuthConfig.RegisterAuth();
}
}
}
在RegisterRoutes方法上按F12,转到该方法的定义,如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing; namespace MvcApplication1
{
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 }
);
}
}
}
5、控制器总结
(1)不需要做任何配置,只需浏览到/控制器/动作URL即可;
(2)控制器是一个非常简单的类,继承自System.Web.Mvc.Controller类;
(3)用控制器在浏览器中显示文本,没有用到View或Model。
二、视图
1、作用
提供用户界面。一个控制器可以对应多个试图,一个视图可以被多个控制器使用。
在Action名上右键→添加试图→View1。
2、指定视图
@{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>View1</title>
</head>
<body>
<div>
<h2>@ViewBag.Message</h2>
</div>
</body>
</html>
3、ViewData和ViewBag的区别于联系
在前面的例子中,使用了ViewBag的Message属性从控制器往视图传递数据,ViewData是一个特殊的字典类,可以按标准语法进行使用:ViewData["CurrentTime"]=DateTime.Now;
这种语法也可以用动态封装器ViewBag:ViewBag.CurrentTime=DateTime.Now;
注意:
(1)尽管只有当要访问的关键字是有效的C#标识符是,ViewBag才起作用,如在ViewData["Key With Spaces"]就不能使用ViewBag访问,编译不通过;
(2)动态值不能作为一个参数传递给扩展方法。因为C#编译器为了选择正确的扩展方法,在编译是必须知道每个参数真正类型。如:@Html.TextBox("name",ViewBag.Name)不会编译通过,可以改为:
①@Html.TextBox("name",ViewData["Name"])
②@Html.TextBox("name",(string)ViewBag.Name)
4、强类型视图
现在需要编写一个显示Album实例列表的视图。一简单的方法就是通过ViewBag属性把那些Album实例添加到视图数据字典中,然后在视图中迭代他们。
(1)首先,在Models文件夹下新建一个Album类,为了简单起见,只定义一个Title属性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web; namespace MvcApplication1.Models
{
public class Album
{
public string Title { get; set; }
}
}
(2)控制器
public ActionResult List()
{
var album = new List<Album>();
for (int i=;i < ;i++)
{
album.Add(new Album { Title="Product " + i.ToString() });
} //一、使用ViewBag传值
//ViewBag.Album = album;
//return View("ListView"); //二、使用ViewData传值
ViewData["Album"] = album;
return View("ListView");
}
(3)视图
在List上右键→添加视图。
<!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ListView</title>
</head>
<body>
<div>
<ul>
@*一、使用ViewBag传值*@
@*@foreach(MvcApplication1.Models.Album a in (ViewBag.Album as IEnumerable<MvcApplication1.Models.Album>))
{
<li>@a.Title</li>
}*@ @*二、使用ViewBag传值*@
@foreach (MvcApplication1.Models.Album a in (ViewData["Album"] as IEnumerable<MvcApplication1.Models.Album>))
{
<li>@a.Title</li>
}
</ul> <pre name="code" class="html"> <ul>
@foreach(dynamic d in ViewBag.Album)
{
<li>@d.Title</li>
}
</ul>
</div>
</body>
</html>
运行效果:
<ul>
@foreach (dynamic d in ViewBag.Album)
{
<li>@d.Title</li>
}
</ul>
请记住,ViewData是ViewDataDictionary类型的,它有一个额外的Model属性,可以用来在视图中获取指定的模型对象。由于在ViewData中只能传递一个模型对象,因此,我们利用这一点可以很容易的实现向视图传递一个特定的类对象。
在Controller方法中,可以通过重载的List方法中传递模型实例来指定模型,代码如下:
public ActionResult List()
{
var album = new List<Album>();
for (int i=;i < ;i++)
{
album.Add(new Album { Title="Product " + i.ToString() });
}
ViewData["Album"]=album;
return View("ListView",album);
} @model IEnumerable<MvcApplication1.Models.Album> @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ListView</title>
</head>
<body>
<div>
<ul>
@foreach(dynamic d in Model)
{
<li>@d.Title</li>
}
</ul>
</div>
</body>
</html>
如果不想输入模型类型的完全限定类型名,可使用using关键字声明,如下所示:
@using MvcApplication1.Models
@model IEnumerable<Album> @{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>ListView</title>
</head>
<body>
<div>
<ul>
@foreach (dynamic d in Model)
{
<li>@d.Title</li>
}
</ul>
</div>
</body>
</html>
对于在视图中经常使用的名称空间,一个较好的方法就是在Views目录下的web.config文件中声明。
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization" />
<add namespace="System.Web.Routing" />
<add namespace="MvcApplication1.Models" />
</namespaces>
</pages>
</system.web.webPages.razor>
5、Razor视图引擎
5.1 先来看一个简单的例子。
@{
Layout = null;
} @{
var items = new string[] { "one", "two", "three" };
}
<!DOCTYPE html> <html>
<head>
<title>ListView</title>
</head>
<body>
<div>
<ul>
@foreach (var item in items)
{
<li>@item</li>
}
</ul>
</div>
</body>
</html>
5.2 Html编码
因为在很多情况下需要用视图显示用户输入,如博客评论等,所以总是存在着潜在的跨站脚本注入攻击(也成XSS),值得称赞的是,Razor表达式是Html自动编码的,如下不会弹出一个警示框,而是直接显示html代码。
@{
string message = "<script>alert('haacked!');</script>";
} <span>@message</span>
然而,如果想要展示Html标签,就要返回一个System.Web.IHtml对象的实例,Razor并不对它进行编码。当然也可以创建一个HtmlStringl实例或者使用Html.Raw便捷方法。
@{
string message = "<script>alert('haacked!');</script>";
} <span>@Html.Raw(message)</span>
- 效果:
虽然这种自动的HTML编码通过对一HTML形式显示的用户输入进行编码能有效的缓和XSS的脆弱性,但是对于在JavaScript中时远远不够的。例如:
<script type="text/javascript">
$(function()
{
var message = 'Hello @Ajax.JavaScriptStringEncode(ViewBag.Message)';
$("#message").html(message).show('slow');
});
</script>
当在JavaScript中将用户提供的值赋给变量时,要使用JavaScript字符串编码而不仅仅是HTML编码,记住这一点是很重要的,也就是要用@Ajax.JavaScriptStringEncode方法对用户输入进行编码。
5.3 布局
Razor的布局有助于使用应用程序中的多个视图保持一致的外观,可使用布局为网站定义公共模板(或只是其中的一部分),公共模板包含一个或多个占位符,应用程序中的其他视图为他们提供内容。
下面看一个非常简单的布局,新建一个名称为MyLayout.cshtml的视图,由于要作为公共模板,所以将其放在/Views/Shared/路径下。
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head>
<body>
<h1>@ViewBag.Title</h1>
<div id="Container">@RenderBody()</div>
</body>
</html>
- 其中的@RenderBody()称为占位符,用来标记使用这个模板的视图将渲染他们的主要内容的位置。
接下来新建一个视图,将使用其作为模板。
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
ViewBag.Title = "GoodLuck";
} <p>This is the main content</p>
布局可能有多个节,例如下面示例在前面的布局基础上添加了一个页脚节:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head> <body>
<h1>@ViewBag.Title</h1>
<div id="Container">@RenderBody()</div>
<footer>@RenderSection("Footer")</footer>
</body>
</html>
- 在不做任何改变的情况下再次运行前面的视图,将会抛出一个异常,提示没有定义的Footer节。
默认情况下,视图必须为布局中定义的没一个节提供相应的内容。更新后的View1视图如下所示:
@{
Layout = "~/Views/Shared/MyLayout.cshtml";
ViewBag.Title = "GoodLuck";
} <p>This is the main content</p>
@section Footer{
This is the <strong>footer</strong>
}
RenderSection方法有一个重载的版本,允许在布局中指定不需要的节,可以给required参数传递一个false值来标记Footer节是可选的:
<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
</head>
<body>
<h1>@ViewBag.Title</h1>
<div id="Container">@RenderBody()</div>
<footer>@RenderSection("Footer", required: false)</footer> </body>
</html>
5.4 ViewStart
在前面的例子中,每一个视图都是用Layout属性来指定它的布局,如果多个视图使用同一个布局,就会产生冗余,并且很难维护。
_ViewStart.cshtml页面可用来消除这种冗余,这个文件的代码先于同目录下任何视图代码的执行,这个文件也可以递归地应用到子目录下的任何视图。
@{
Layout="~/Views/Shared/_Layout.cshtml";
}
因为这个代码先于任何视图执行,所以一个视图可以重写Layout属性的默认值,从而重新选择一个不同的布局。
5.4 指定部分视图
除了返回视图之外,操作方法也可以通过PartialView方法以PartialResult的形式返回部分视图。
The end