ASP.NET MVC 面试题及答案






4.88/5 (594投票s)
本文的目的是从ASP.NET MVC面试的角度快速复习您的MVC知识。
请同时参阅我们关于60多个ASP.NET MVC面试题及答案的复习文章。
目录
- 免责声明
- 什么是MVC(模型-视图-控制器)?
- 解释MVC应用程序生命周期?
- MVC适用于Windows和Web应用程序吗?
- 使用MVC有什么好处?
- MVC与三层架构有区别吗?
- MVC的最新版本是什么?
- MVC 2、3、4、5和6版本之间有什么区别?
- MVC中的HTML助手是什么?
- “HTML.TextBox”与“HTML.TextBoxFor”有什么区别?
- MVC中的路由是什么?
- 路由映射代码写在哪里?
- 我们可以将多个URL映射到同一个操作吗?
- 解释MVC中的基于特性的路由?
- 在代码中定义路由结构有什么好处?
- 如何通过超链接从一个视图导航到另一个视图?
- 如何限制MVC操作只能通过GET或POST调用?
- 如何在MVC中维护会话?
- tempdata、viewdata和viewbag之间有什么区别?
- TempData和ViewData之间有什么区别?
- “TempData”是否也保留下一个请求的数据?
- “TempData”中的Keep和Peek有什么用?
- MVC中的部分视图是什么?
- 如何创建部分视图并使用它?
- 如何在MVC中进行验证?
- 我们可以一次性显示所有错误吗?
- 如何启用客户端数据注解验证?
- MVC中的Razor是什么?
- 为什么选择Razor,而我们已经有了ASPX?
- 那么,Razor和ASPX哪个更适合?
- 如何在MVC中进行身份验证和授权?
- 如何为MVC实现Windows身份验证?
- 如何在MVC中实现表单身份验证?
- 如何在MVC中实现AJAX?
- AJAX中可以跟踪哪些事件?
- ActionResult和ViewResult之间有什么区别?
- MVC中有哪些不同类型的结果?
- MVC中的ActionFilters是什么?
- 有哪些不同类型的ActionFilters?
- 如果我们有多个过滤器,执行顺序是什么?
- 我们能用MVC创建自己的自定义视图引擎吗?
- 如何在MVC中将结果以JSON格式返回?
- 什么是WebAPI?
- 但是WCF SOAP也做同样的事情,那么WebAPI有什么不同?
- 使用WCF可以实现REST,那为什么还要WebAPI?
- 如何检测MVC控制器是由POST还是GET调用的?
- MVC中的捆绑和最小化是什么?
- 捆绑如何提高性能?
- 如何在MVC中实现捆绑?
- 如何在调试模式下测试捆绑?
- 解释最小化及其实现方法
- 如何实现最小化?
- MVC中的区域(Areas)是什么?
- 解释MVC中的视图模型(View Model)概念?
- 视图模型类会包含什么样的逻辑?
- 如何在一个视图中使用两个(多个)模型?
- 解释MVC中显示模式(Display Mode)的必要性?
- 解释MVC模型绑定器(Model Binders)?
- 解释MVC脚手架(Scaffolding)的概念?
- 脚手架在内部使用什么来连接数据库?
- 如何在MVC中进行异常处理?
- 如何处理MVC单个视图中指向多个操作的多个提交按钮?
- 什么是CSRF攻击,以及如何在MVC中防止它?
免责声明
阅读或查看这些MVC面试题并不意味着您就能通过MVC面试。本文的目的是在您参加MVC面试之前快速复习您的MVC知识。本文不教授Asp.net MVC的步骤,而是在参加MVC面试前的一份临阵磨枪的复习资料。
如果您想从零开始学习MVC,请先阅读7天学会MVC(模型-视图-控制器),或者您也可以从我的YouTube上的MVC(模型-视图-控制器)分步视频系列开始。
如果您想在短时间内(例如2天,即16小时)学会MVC 5,下面是相应的视频系列。
需要帮助改进本文
我已尽我所能涵盖了我在MVC面试中遇到的问题。但我认为以下问题还不够,在真正的MVC面试中还会问更多。如果您能分享您遇到的问题,请在下面的评论中留言。我很乐意将它们纳入本文,以便他人受益。
如果您的问题很棒,我喜欢的话,我将免费寄送一本我的.NET面试题书给您,仅限印度(抱歉,我没有足够的财力寄往国外)。
什么是MVC(模型-视图-控制器)?
MVC是一种架构模式,它分离了表示和用户交互。它分为三个主要部分:模型、视图和控制器。下面是它们各自处理任务的方式。
- 视图负责外观和感觉。
- 模型代表现实世界中的对象,并向视图提供数据。
- 控制器负责接收最终用户的请求,并加载相应的模型和视图。
解释MVC应用程序生命周期?
MVC应用程序生命周期中有六个主要事件,下图总结了它们。
图片来源: - http://www.dotnetinterviewquestions.in/article_explain-mvc-application-life-cycle_210.html
任何Web应用程序都有两个主要的执行步骤:首先理解请求,然后根据请求类型发送相应的响应。MVC应用程序生命周期也不例外,它有两个主要阶段:首先创建请求对象,然后将响应发送到浏览器。
创建请求对象: -请求对象的创建有四个主要步骤。下面是详细的解释。
步骤1:填充路由: - MVC请求被映射到路由表,路由表指定要调用的控制器和操作。因此,如果这是第一个请求,首先要做的事情是在路由表中填充路由集合。路由表的填充发生在global.asax文件中。
步骤2:获取路由: -根据发送的URL,“UrlRoutingModule”会在路由表中搜索,以创建一个“RouteData”对象,该对象包含要调用的控制器和操作的详细信息。
步骤3:创建请求上下文: -“RouteData”对象用于创建“RequestContext”对象。
步骤4:创建控制器实例: -此请求对象被发送到“MvcHandler”实例以创建控制器类实例。一旦控制器类对象被创建,它就会调用控制器类的“Execute”方法。
创建响应对象: -此阶段有两个步骤:执行操作,最后将结果作为响应发送到视图。
MVC适用于Windows和Web应用程序吗?
MVC架构更适合Web应用程序而不是Windows应用程序。对于Windows应用程序,MVP(模型-视图-表示器)更为适用。如果您使用WPF和Silverlight,由于数据绑定,MVVM更为合适。
使用MVC有什么好处?
MVC有两个很大的好处:
- 实现了关注点分离,因为我们将代码隐藏移到了单独的文件中。通过将绑定代码移到单独的文件中,我们可以最大限度地重用代码。
- 可以进行自动化UI测试,因为现在代码隐藏(UI交互代码)已移至一个简单的.NET类。这为我们提供了编写单元测试和自动化手动测试的机会。
MVC与三层架构有区别吗?
MVC是传统三层架构的演进。三层架构的许多组件都包含在MVC中。所以下面是映射关系:
功能 | 三层/分层架构 | 模型-视图-控制器架构 |
外观 | 用户界面 | 视图 |
UI逻辑 | 用户界面 | 控制器 (Controller) |
业务逻辑/验证 | 中间层 | 模型 |
请求首先发送到 | 用户界面 | 控制器 (Controller) |
访问数据 | 数据访问层 | 数据访问层 |
MVC的最新版本是什么?
MVC 6是最新版本,也称为ASP VNEXT。
MVC 2、3、4、5和6版本之间有什么区别?
MVC 6
ASP.NET MVC和Web API已合并为一个。
依赖注入是内置的,并且是MVC的一部分。
Side by side - 将运行时和框架与您的应用程序一起部署
所有内容都通过NuGet打包,包括.NET运行时本身。
新的基于JSON的项目结构。
无需每次更改都重新编译。只需保存并刷新浏览器。
使用新的Roslyn实时编译器进行编译。
vNext是开源的,通过.NET Foundation并接受公众贡献。
vNext(和Rosyln)也运行在Mono上,目前可以在Mac和Linux上运行。
MVC 5
一个ASP.NET
基于特性的路由
Asp.Net Identity
MVC模板中的Bootstrap
身份验证过滤器
过滤器覆盖
MVC 4
ASP.NET Web API
更新和现代化的默认项目模板
新的移动项目模板
许多新功能支持移动应用程序
增强的异步方法支持
MVC 3
Razor
现成的项目模板
HTML 5启用的模板
支持多种视图引擎
JavaScript和Ajax
模型验证改进
MVC 2
客户端验证
模板化助手
区域 (Areas)
异步控制器
Html.ValidationSummary帮助方法
操作方法参数中的DefaultValueAttribute
使用模型绑定器绑定二进制数据
DataAnnotations属性
Model-Validator Providers
新的RequireHttpsAttribute操作过滤器
模板化助手
显示模型级错误
MVC中的HTML助手是什么?
HTML助手可帮助您在视图中呈现HTML控件。例如,如果您想在视图中显示一个HTML文本框,下面是HTML助手代码。
<%= Html.TextBox("LastName") %>
对于复选框,下面是HTML助手代码。这样,我们为存在的每个HTML控件都有HTML助手方法。
<%= Html.CheckBox("Married") %>
“HTML.TextBox”与“HTML.TextBoxFor”有什么区别?
它们都提供相同的HTML输出,“HTML.TextBoxFor”是强类型的,而“HTML.TextBox”不是。下面是一个简单的HTML代码,它只是创建一个名为“CustomerCode”的简单文本框。
Html.TextBox("CustomerCode")
下面是“Html.TextBoxFor”代码,它使用对象“m”的属性名“CustomerCode”创建HTML文本框。
Html.TextBoxFor(m => m.CustomerCode)
同样,对于其他HTML控件,我们有“Html.CheckBox”和“Html.CheckBoxFor”用于复选框。
MVC中的路由是什么?
路由帮助您定义URL结构并将URL与控制器进行映射。
例如,假设我们希望当用户键入“https:///View/ViewCustomer/”时,它转到“Customer”控制器并调用DisplayCustomer
操作。这是通过使用maproute
函数在routes
集合中添加条目来定义的。下面是显示URL结构和与控制器及操作的映射如何定义的下划线代码。
routes.MapRoute( "View", // Route name "View/ViewCustomer/{id}", // URL with parameters new { controller = "Customer", action = "DisplayCustomer", id = UrlParameter.Optional }); // Parameter defaults
路由映射代码写在哪里?
路由映射代码写在“RouteConfig.cs”文件中,并通过“global.asax”应用程序的启动事件进行注册。
我们可以将多个URL映射到同一个操作吗?
是的,您可以。您只需创建两个具有不同键名称的条目,并指定相同的控制器和操作。
解释MVC中的基于特性的路由?
这是MVC 5引入的功能。通过使用“Route”属性,我们可以定义URL结构。例如,在下面的代码中,我们使用路由属性修饰了“GotoAbout”操作。路由属性表示“GotoAbout”可以通过URL结构“Users/about”来调用。
public class HomeController : Controller
{
[Route("Users/about")]
public ActionResult GotoAbout()
{
return View();
}
}
在代码中定义路由结构有什么好处?
大多数时候,开发人员会直接在操作方法中编写代码。开发人员可以立即看到URL结构,而无需转到“routeconfig.cs”并查看冗长的代码。例如,在下面的代码中,开发人员可以立即看到“GotoAbout”操作可以通过四种不同的URL结构来调用。
这比滚动浏览“routeconfig.cs”文件并查看长行代码来找出哪个URL结构映射到哪个操作要用户友好得多。
public class HomeController : Controller
{
[Route("Users/about")]
[Route("Users/WhoareWe")]
[Route("Users/OurTeam")]
[Route("Users/aboutCompany")]
public ActionResult GotoAbout()
{
return View();
}
}
如何通过超链接从一个视图导航到另一个视图?
通过使用ActionLink
方法,如下面的代码所示。下面的代码将创建一个简单的URL,用于导航到“Home”控制器并调用GotoHome
操作。
<%= Html.ActionLink("Home","Gotohome") %>
如何限制MVC操作只能通过GET或POST调用?
我们可以使用HttpGet
或HttpPost
属性修饰MVC操作,以限制HTTP调用的类型。例如,您可以在下面的代码片段中看到,DisplayCustomer
操作只能由HttpGet
调用。如果我们尝试对DisplayCustomer
执行HTTP POST,它将抛出错误。
[HttpGet]
public ViewResult DisplayCustomer(int id)
{
Customer objCustomer = Customers[id];
return View("DisplayCustomer",objCustomer);
}
如何在MVC中维护会话?
在MVC中,可以通过三种方式维护会话:tempdata、viewdata和viewbag。
tempdata、viewdata和viewbag之间有什么区别?
- Temp data - 在您从一个控制器移动到另一个控制器或从一个操作移动到另一个操作时,有助于维护数据。换句话说,当您重定向时,tempdata有助于在这些重定向之间维护数据。它内部使用会话变量。
- View data - 在您从控制器移动到视图时,有助于维护数据。
- View Bag - 它是view data的动态包装器。当您使用
Viewbag
类型时,不需要类型转换。它内部使用dynamic
关键字。
- Session variables - 通过使用会话变量,我们可以维护从任何实体到任何实体的所有数据。
- Hidden fields and HTML controls - 仅有助于从UI维护数据到控制器。因此,您可以使用POST或GET HTTP方法将数据从HTML控件或隐藏字段发送到控制器。
下面是一个总结表,显示了不同的持久化机制。
在...之间维护数据 | ViewData/ViewBag | TempData | 隐藏字段 | Session |
控制器到控制器 | 否 | 是 | 否 | 是 |
控制器到视图 | 是 | 否 | 否 | 是 |
视图到控制器 | 否 | 否 | 是 | 是 |
TempData和ViewData之间有什么区别?
“TempData”维护整个请求的数据,而“ViewData”仅维护从控制器到视图的数据。
“TempData”是否也保留下一个请求的数据?
“TempData”在当前请求中始终可用,而在后续请求中,它是否可用取决于“TempData”是否被读取。
所以,如果“TempData”一旦被读取,在后续请求中将不可用。
“TempData”中的Keep和Peek有什么用?
一旦“TempData”在当前请求中被读取,在后续请求中就不可用。如果我们希望“TempData”被读取并且在后续请求中也可用,那么在读取之后,我们需要调用“Keep”方法,如下面的代码所示。
@TempData[“MyData”];
TempData.Keep(“MyData”);
更快捷的实现方法是使用“Peek”。此函数有助于读取以及指示MVC在后续请求中维护“TempData”。
string str = TempData.Peek("Td").ToString();
如果您想更详细地阅读,可以阅读这篇关于MVC Peek和Keep的详细博客。
MVC中的部分视图是什么?
部分视图是可重用的视图(如用户控件),可以嵌入到其他视图中。例如,假设您网站的所有页面都有标准的结构,包含左侧菜单、页眉和页脚,如下图所示。
对于每个页面,您都希望重用左侧菜单、页眉和页脚控件。因此,您可以为每个这些项创建部分视图,然后在主视图中调用该部分视图。
如何创建部分视图并使用它?
当您将视图添加到项目中时,需要勾选“创建部分视图”复选框。
创建部分视图后,您可以使用Html.RenderPartial
方法在主视图中调用部分视图,如以下代码片段所示。
<body>
<div>
<% Html.RenderPartial("MyView"); %>
</div>
</body>
如何在MVC中进行验证?
在MVC中进行验证的最简单方法之一是使用数据注解。数据注解只不过是可以应用于模型属性的属性。例如,在下面的代码片段中,我们有一个简单的Customer
类,其中有一个customercode
属性。
此CustomerCode
属性已标记为Required
数据注解属性。换句话说,如果未提供客户代码,它将不接受。
public class Customer
{
[Required(ErrorMessage="Customer code is required")]
public string CustomerCode
{
set;
get;
}
}
为了显示验证错误消息,我们需要使用属于Html
帮助类(helper class)的ValidateMessageFor
方法。
<% using (Html.BeginForm("PostCustomer", "Home", FormMethod.Post))
{ %>
<%=Html.TextBoxFor(m => m.CustomerCode)%>
<%=Html.ValidationMessageFor(m => m.CustomerCode)%>
<input type="submit" value="Submit customer data" />
<%}%>
稍后在控制器中,我们可以使用ModelState.IsValid
属性检查模型是否正确,并据此采取相应的操作。
public ActionResult PostCustomer(Customer obj)
{
if (ModelState.IsValid)
{
obj.Save();
return View("Thanks");
}
else
{
return View("Customer");
}
}
下面是视图上显示错误消息的简单视图。
我们可以一次性显示所有错误吗?
是的,我们可以。使用Html
帮助类中的ValidationSummary
方法。
<%= Html.ValidationSummary() %>
MVC中用于验证的其他数据注解属性有哪些?
如果您想检查字符串长度,可以使用StringLength
。
[StringLength(160)]
public string FirstName { get; set; }
如果您想使用正则表达式,可以使用RegularExpression
属性。
[RegularExpression(@"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}")]public string Email { get; set; }
如果您想检查数字是否在范围内,可以使用Range
属性。
[Range(10,25)]public int Age { get; set; }
有时您可能想比较一个字段的值与另一个字段的值,我们可以使用Compare
属性。
public string Password { get; set; }[Compare("Password")]public string ConfirmPass { get; set; }
如果您想获取特定的错误消息,可以使用Errors
集合。
var ErrMessage = ModelState["Email"].Errors[0].ErrorMessage;
如果您自己创建了模型对象,可以在控制器中显式调用TryUpdateModel
来检查对象是否有效。
TryUpdateModel(NewCustomer);
如果您想在控制器中添加错误,可以使用AddModelError
函数。
ModelState.AddModelError("FirstName", "This is my server-side error.");
如何启用客户端数据注解验证?
这是一个两步过程:首先引用必要的jQuery文件。
<script src="<%= Url.Content("~/Scripts/jquery-1.5.1.js") %>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/jquery.validate.js") %>" type="text/javascript"></script>
<script src="<%= Url.Content("~/Scripts/jquery.validate.unobtrusive.js") %>" type="text/javascript"></script>
第二步是调用EnableClientValidation
方法。
<% Html.EnableClientValidation(); %>
MVC中的Razor是什么?
它是一个轻量级的视图引擎。直到MVC 3,我们只有一个视图类型,即ASPX。Razor是在MVC 3中引入的。
为什么选择Razor,而我们已经有了ASPX?
Razor比ASPX更简洁、轻量级,并且语法更简单。例如,在ASPX中显示简单的时间,我们需要编写:
<%=DateTime.Now%>
在Razor中,只需一行代码:
@DateTime.Now
那么,Razor和ASPX哪个更适合?
根据微软的说法,Razor更受欢迎,因为它轻量级且语法简单。
如何在MVC中进行身份验证和授权?
您可以使用Windows或表单身份验证来实现MVC。
如何为MVC实现Windows身份验证?
对于Windows身份验证,您需要修改web.config文件并将身份验证模式设置为Windows。
<authentication mode="Windows"/>
<authorization>
<deny users="?"/>
</authorization>
然后在控制器或操作上,您可以使用Authorize
属性来指定哪些用户可以访问这些控制器和操作。下面是相应的代码片段。现在只有控制器和操作中指定的那些用户才能访问。
[Authorize(Users= @"WIN-3LI600MWLQN\Administrator")]
public class StartController : Controller
{
//
// GET: /Start/
[Authorize(Users = @"WIN-3LI600MWLQN\Administrator")]
public ActionResult Index()
{
return View("MyView");
}
}
如何在MVC中实现表单身份验证?
表单身份验证的实现方式与ASP.NET相同。第一步是将身份验证模式设置为Forms
。loginUrl
指向控制器而不是页面。
<authentication mode="Forms">
<forms loginUrl="~/Home/Login" timeout="2880"/>
</authentication>
我们还需要创建一个控制器,在其中检查用户是否正确。如果用户正确,我们将设置Cookie值。
public ActionResult Login()
{
if ((Request.Form["txtUserName"] == "Shiv") &&
(Request.Form["txtPassword"] == "Shiv@123"))
{
FormsAuthentication.SetAuthCookie("Shiv",true);
return View("About");
}
else
{
return View("Index");
}
}
所有其他操作都需要使用Authorize
属性进行标记,以便任何未经授权的用户访问这些控制器都会被重定向到将进行身份验证的控制器(在本例中为“Login”控制器)。
[Authorize]
PublicActionResult Default()
{
return View();
}
[Authorize]
publicActionResult About()
{
return View();
}
如何在MVC中实现AJAX?
您可以通过两种方式在MVC中实现AJAX:
- AJAX库
- jQuery
下面是如何使用“AJAX”帮助库实现AJAX的简单示例。在下面的代码中,您可以看到我们有一个简单的表单,它使用Ajax.BeginForm
语法创建。此表单调用名为getCustomer
的控制器操作。因此,提交操作单击将是一个异步AJAX调用。
<script language="javascript">
function OnSuccess(data1)
{
// Do something here
}
</script>
<div>
<%
var AjaxOpt = new AjaxOptions{OnSuccess="OnSuccess"};
%>
<% using (Ajax.BeginForm("getCustomer","MyAjax",AjaxOpt)) { %>
<input id="txtCustomerCode" type="text" /><br />
<input id="txtCustomerName" type="text" /><br />
<input id="Submit2" type="submit" value="submit"/></div>
<%} %>
如果您想在超链接单击时发出AJAX调用,可以使用Ajax.ActionLink
函数,如下面的代码所示。
因此,如果您想创建一个名为GetDate
的AJAX异步超链接,它调用控制器中的GetDate
函数,下面的代码就是这样做的。控制器响应后,这些数据将显示在名为DateDiv
的HTML DIV
标签中。
<span id="DateDiv" />
<%:
Ajax.ActionLink("Get Date","GetDate",
new AjaxOptions {UpdateTargetId = "DateDiv" })
%>
下面是控制器代码。您可以看到GetDate
函数是如何有10秒的延迟的。
public class Default1Controller : Controller
{
public string GetDate()
{
Thread.Sleep(10000);
return DateTime.Now.ToString();
}
}
创建AJAX调用的第二种方法是使用jQuery。在下面的代码中,您可以看到我们正在向URL /MyAjax/getCustomer发出AJAX POST调用。这是通过使用$.post
完成的。所有这些逻辑都放入一个名为GetData
的函数中,您可以根据需要调用GetData
函数来响应按钮或超链接的单击事件。
function GetData()
{
var url = "/MyAjax/getCustomer";
$.post(url, function (data)
{
$("#txtCustomerCode").val(data.CustomerCode);
$("#txtCustomerName").val(data.CustomerName);
}
)
}
AJAX中可以跟踪哪些事件?
ActionResult和ViewResult之间有什么区别?
ActionResult
是一个抽象类,而ViewResult
继承自ActionResult
类。ActionResult
有几个派生类,如ViewResult
、JsonResult
、FileStreamResult
等。ActionResult
可用于利用多态性和动态性。因此,如果您动态返回不同类型的视图,ActionResult
是最好的选择。例如,在下面的代码片段中,您可以看到我们有一个名为DynamicView
的简单操作。根据标志(IsHtmlView
),它将返回ViewResult
或JsonResult
。
public ActionResult DynamicView()
{
if (IsHtmlView)
return View(); // returns simple ViewResult
else
return Json(); // returns JsonResult view
}
MVC中有哪些不同类型的结果?
注意:很难记住所有12种类型。但对于面试,您可以记住一些重要的,如ActionResult
、ViewResult
和JsonResult
。下面是供您参考的详细列表。
MVC中有12种结果类型,顶层是ActionResult
类,它是一个基类,可以有11个子类型,如下所示:
ViewResult
- 将指定的视图呈现到响应流PartialViewResult
- 将指定的部分视图呈现到响应流EmptyResult
- 返回一个空响应RedirectResult
- 执行HTTP重定向到指定的URLRedirectToRouteResult
- 根据给定的路由数据,通过路由引擎执行HTTP重定向到确定的URLJsonResult
- 将给定的ViewData
对象序列化为JSON格式JavaScriptResult
- 返回一段可以在客户端执行的JavaScript代码ContentResult
- 将内容写入响应流,无需视图FileContentResult
- 将文件返回到客户端FileStreamResult
- 将文件返回到客户端,该文件由Stream
提供FilePathResult
- 将文件返回到客户端
MVC中的ActionFilters是什么?
ActionFilters帮助您在MVC操作执行期间或执行后执行逻辑。
Action filters在以下场景中很有用:
- 在操作发生之前实现后处理逻辑。
- 取消当前执行。
- 检查返回的值。
- 为操作提供额外数据。
您可以通过两种方式创建Action filters:
- 内联Action filter。
- 创建
ActionFilter
属性。
要创建内联Action属性,我们需要实现IActionFilter
接口。IActionFilter
接口有两个方法:OnActionExecuted
和OnActionExecuting
。我们可以在这些方法中实现预处理逻辑或取消逻辑。
public class Default1Controller : Controller , IActionFilter
{
public ActionResult Index(Customer obj)
{
return View(obj);
}
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
Trace.WriteLine("Action Executed");
}
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
Trace.WriteLine("Action is executing");
}
}
内联Action属性的问题在于它不能在控制器之间重用。因此,我们可以将内联Action filter转换为Action filter属性。要创建Action filter属性,我们需要继承自ActionFilterAttribute
并实现IActionFilter
接口,如下面的代码所示。
public class MyActionAttribute : ActionFilterAttribute , IActionFilter
{
void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
{
Trace.WriteLine("Action Executed");
}
void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
{
Trace.WriteLine("Action executing");
}
}
之后,我们可以用Action属性装饰我们想要在其上执行的控制器。您可以看到在下面的代码中,我用MyActionAttribute
类装饰了Default1Controller
,该类在前面的代码中创建。
[MyActionAttribute]
public class Default1Controller : Controller
{
public ActionResult Index(Customer obj)
{
return View(obj);
}
}
有哪些不同类型的ActionFilters?
- 授权过滤器
- 操作过滤器
- 结果过滤器
- 异常过滤器
如果我们有多个过滤器,执行顺序是什么?
- 授权过滤器
- 操作过滤器
- 响应过滤器
- 异常过滤器
我们可以用MVC创建自己的自定义视图引擎吗?
是的,我们可以在MVC中创建自己的自定义视图引擎。要创建我们自己的自定义视图引擎,我们需要遵循三个步骤:
假设我们想创建一个自定义视图引擎,用户可以在其中键入类似“<DateTime>”的命令,它应该显示当前日期和时间。
步骤1:我们需要创建一个实现IView
接口的类。在此类中,我们应该在render
函数中编写视图如何呈现的逻辑。下面是相应的简单代码片段。
public class MyCustomView : IView
{
private string _FolderPath; // Define where our views are stored
public string FolderPath
{
get { return _FolderPath; }
set { _FolderPath = value; }
}
public void Render(ViewContext viewContext, System.IO.TextWriter writer)
{
// Parsing logic <dateTime>
// read the view file
string strFileData = File.ReadAllText(_FolderPath);
// we need to and replace <datetime> datetime.now value
string strFinal = strFileData.Replace("<DateTime>", DateTime.Now.ToString());
// this replaced data has to sent for display
writer.Write(strFinal);
}
}
步骤2:我们需要创建一个继承自VirtualPathProviderViewEngine
的类,在该类中我们需要提供视图名称的文件夹路径和扩展名。例如,对于Razor,扩展名是“cshtml”;对于aspx,视图扩展名是“.aspx”,所以对于我们的自定义视图,我们也需要提供一个扩展名。下面是代码的样子。您可以看到ViewLocationFormats
设置为Views文件夹,扩展名为“.myview”。
public class MyViewEngineProvider : VirtualPathProviderViewEngine
{
// We will create the object of Mycustome view
public MyViewEngineProvider() // constructor
{
// Define the location of the View file
this.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.myview",
"~/Views/Shared/{0}.myview" }; //location and extension of our views
}
protected override IView CreateView(
ControllerContext controllerContext, string viewPath, string masterPath)
{
var physicalpath = controllerContext.HttpContext.Server.MapPath(viewPath);
MyCustomView obj = new MyCustomView(); // Custom view engine class
obj.FolderPath = physicalpath; // set the path where the views will be stored
return obj; // returned this view paresing
// logic so that it can be registered in the view engine collection
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var physicalpath = controllerContext.HttpContext.Server.MapPath(partialPath);
MyCustomView obj = new MyCustomView(); // Custom view engine class
obj.FolderPath = physicalpath; // set the path where the views will be stored
return obj;
// returned this view paresing logic
// so that it can be registered in the view engine collection
}
}
步骤3:我们需要在自定义视图集合中注册该视图。在ViewEngines
集合中注册自定义视图引擎的最佳位置是global.asax文件。下面是相应的代码片段。
protected void Application_Start()
{
// Step3 :- register this object in the view engine collection
ViewEngines.Engines.Add(new MyViewEngineProvider());
…..
}
下面是使用顶部定义的命令编写的自定义视图的简单输出。
如果您调用此视图,您应该会看到以下输出:
如何在MVC中将结果以JSON格式返回?
在MVC中,我们有JsonResult
类,通过它可以以JSON格式返回数据。下面是一个简单的示例代码,它使用JsonResult
以JSON格式返回一个Customer
对象。
public JsonResult getCustomer() { Customer obj = new Customer(); obj.CustomerCode = "1001"; obj.CustomerName = "Shiv"; return Json(obj,JsonRequestBehavior.AllowGet); }
如果您通过浏览器调用该操作,则上面的代码的JSON输出如下:
什么是WebAPI?
HTTP是最常用的协议。在过去的许多年里,浏览器是消耗HTTP之上公开数据的首选客户端。但随着时间的推移,客户端的多样性开始扩散。我们需要从移动设备、JavaScript、Windows应用程序等客户端消费HTTP上的数据。
为了满足广泛的客户端需求,REST被提议作为一种方法。您可以从WCF章节中阅读更多关于REST的信息。
WebAPI是一种技术,您可以通过它遵循REST原则通过HTTP公开数据。
但是WCF SOAP也做同样的事情,那么WebAPI有什么不同?
SOAP | WEB API | |
大小 | 由于复杂的WSDL结构而显得笨重。 | 轻量级,只传输必要的信息。 |
协议 | 独立于协议。 | 仅用于HTTP协议 |
格式 | 为了解析SOAP消息,客户端需要理解WSDL格式。编写自定义代码来解析WSDL是一项繁重的工作。如果您的客户端足够智能,可以创建代理对象(就像我们在.NET中所做的“添加引用”一样),那么SOAP就更容易使用和调用。 | WebAPI的输出是简单的字符串消息、JSON、简单的XML格式等。因此,编写解析逻辑非常容易。 |
原则 | SOAP遵循WS-*规范。 | WebAPI遵循REST原则。(请参阅WCF章节中的REST。) |
使用WCF也可以实现REST,那么为什么还要WebAPI?
WCF的目的是实现SOA,其初衷并非实现REST。WebAPI是从头开始构建的,其唯一目标是使用REST创建HTTP服务。由于专注于创建REST服务,WebAPI更受欢迎。
如何在MVC中实现WebAPI
下面是实现WebAPI的步骤:
步骤1:使用WebAPI模板创建项目。
步骤2:创建项目后,您会注意到控制器现在继承自ApiController
,并且您可以实现HTTP协议的POST、GET、PUT和DELETE方法。
public class ValuesController : ApiController
{
// GET api/values
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
return "value";
}
// POST api/values
public void Post([FromBody]string value)
{
}
// PUT api/values/5
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
public void Delete(int id)
{
}
}
步骤3:如果您进行HTTP GET调用,您应该会得到以下结果:
如何检测MVC控制器是由POST还是GET调用的?
要检测对控制器的调用是POST操作还是GET操作,我们可以使用Request.HttpMethod
属性,如下面的代码片段所示。
public ActionResult SomeAction()
{
if (Request.HttpMethod == "POST")
{
return View("SomePage");
}
else
{
return View("SomeOtherPage");
}
}
MVC中的捆绑和最小化是什么?
捆绑和最小化有助于提高页面的请求加载时间,从而提高性能。
捆绑如何提高性能?
Web项目总是需要CSS和脚本文件。捆绑有助于我们将多个JavaScript和CSS文件合并为一个单一的实体,从而将多个请求最小化为一个请求。
例如,考虑以下页面对Web的请求。此页面使用两个JavaScript文件Javascript1.js和Javascript2.js。因此,当请求此页面时,它会进行三次请求调用:
- 一次用于Index页面。
- 两次用于其他两个JavaScript文件:Javascript1.js和Javascript2.js。
如果有很多JavaScript文件,下面的场景会变得更糟,导致多个请求,从而降低性能。如果我们能以某种方式将所有JS文件合并成一个包并作为单个单元请求它们,那将提高性能(请参见下一张图,它有一个单一请求)。
那么,我们如何在MVC中实现捆绑?
从App_Start文件夹打开BundleConfig.cs。
在BundleConfig.cs中,将您想要捆绑到单个实体的JS文件添加到bundles集合中。在下面的代码中,我们将Scripts文件夹中存在的所有javascript JS文件合并为单个单元,添加到bundles集合中。
bundles.Add(new ScriptBundle("~/Scripts/MyScripts").Include( "~/Scripts/*.js"));
您的BundleConfig.cs文件看起来像这样:
public class BundleConfig { public static void RegisterBundles(BundleCollection bundles) { bundles.Add(new ScriptBundle("~/Scripts/MyScripts").Include( "~/Scripts/*.js")); BundleTable.EnableOptimizations = true; } }
一旦我们将脚本合并为一个单元,然后就需要使用以下代码在视图中包含所有JS文件。以下代码需要放在ASPX或Razor视图中。
<%= Scripts.Render("~/Scripts/MyScripts") %>
如果您现在查看页面的请求,您会发现脚本请求已合并为一个请求。
如何在调试模式下测试捆绑?
如果您处于调试模式,则需要在bundleconfig.cs文件中将EnableOptimizations
设置为true,否则在页面请求中将看不到捆绑效果。
BundleTable.EnableOptimizations = true;
解释最小化及其实现方法
最小化通过删除空格、注释等来减小脚本和CSS文件的大小。例如,下面是一段带有注释的简单JavaScript代码。
// This is test var x = 0; x = x + 1; x = x * 2;
实现最小化后,JavaScript代码如下所示。您可以看到空格和注释是如何被删除以减小文件大小,从而提高性能。
var x=0;x=x+1;x=x*2;
如何实现最小化?
当您实现捆绑时,最小化会自动实现。换句话说,实现捆绑和最小化的步骤是相同的。
MVC中的区域(Areas)是什么?
区域(Areas)通过将功能分组为独立的模块来帮助您使项目更有条理。例如,在下面的MVC项目中,我们有四个控制器类,随着时间的推移,如果添加更多控制器类,管理起来将变得困难。在更大的项目中,您最终会拥有成百上千个控制器类,这会给维护带来痛苦。
如果我们能将控制器类分组为逻辑部分,如“发票”和“会计”,那将使生活更轻松,而这正是“区域”的用途。
您可以通过右键单击MVC解决方案并选择“区域”菜单来添加一个区域,如下图所示。
在下图所示的图像中,我们创建了两个“区域”:“Account”和“Invoicing”,并在其中放置了相应的控制器。您可以看到与先前状态相比,项目看起来更加有条理。
解释MVC中的视图模型(View Model)概念?
视图模型(View Model)是一个简单的类,它表示要在视图上显示的数据。
例如,下面是一个简单的customermodel对象,具有“CustomerName”和“Amount”属性。
CustomerViewModel obj = new CustomerViewModel(); obj.Customer.CustomerName = "Shiv"; obj.Customer.Amount = 1000;
但是,当这个“Customer”模型对象显示在MVC视图上时,它看起来像下图所示。它有“CustomerName”、“Amount”以及“Customer Buying Level”字段显示在视图/屏幕上。“Customer buying Level”是一个颜色指示,表示客户的购买强度。
“Customer buying level”的颜色取决于“Amount”属性的值。如果金额大于2000,则颜色为红色;如果金额大于1500,则颜色为橙色;否则,颜色为黄色。
换句话说,“Customer buying level”是一个基于金额计算的额外属性。
因此,Customer ViewModel类有三个属性:
- “TxtCustomerName”文本框直接使用“CustomerName”属性的数据。
- “TxtAmount”文本框直接使用模型中的“Amount”属性的数据。
- “CustomerBuyingLevelColor”根据“Amount”值显示颜色值。
客户模型 | 客户视图模型 |
CustomerName | TxtCustomerName |
金额 | TxtAmount |
CustomerBuyingLevelColor |
视图模型类会包含什么样的逻辑?
顾名思义,视图模型(ViewModel)这个类包含连接视图和模型的胶水代码或连接代码。
因此,视图模型类可以包含以下类型的逻辑:
- 颜色转换逻辑: -例如,您的模型中有一个“Grade”属性,您希望在UI上显示“高等级”为“红色”,“低等级”为“黄色”,而“普通等级”为“绿色”。
- 数据格式转换逻辑:-您的模型有一个“Status”属性,其值为“Married”(已婚)和“Unmarried”(未婚)。在UI中,您希望将其显示为复选框,如果“已婚”则选中,如果“未婚”则不选中。
- 聚合逻辑:-您有两个不同的“Customer”和“Address”模型类,并且有一个视图一次性显示“Customer”和“Address”数据。
- 结构缩减:-您有一个“Customer”模型,其中包含“customerCode”和“CustomerName”,而您只想显示“CustomerName”。因此,您可以创建一个模型包装器,并公开必要的属性。
如何在一个视图中使用两个(多个)模型?
让我们首先尝试理解面试官的意图。当我们用模型绑定视图时,我们使用下面的图形所示的模型下拉列表。在下面的图形中,我们只能选择一个模型。
但是,如果我们想将“Customer”和“Order”类绑定到视图呢?
为此,我们需要创建一个视图模型,它聚合了这两个类,如下面的代码所示。然后将该视图模型绑定到视图。
public class CustOrderVM { public Customer cust = new Customer(); public Order Ord = new Order(); }
在视图中,我们可以使用视图模型来引用两个模型,如以下代码所示。
<%= model.cust.Name %> <%= model.Ord.Number %>
解释MVC中显示模式(Display Mode)的必要性?
显示模式(Display mode)会根据用户登录的设备来显示视图。因此,我们可以为不同的设备创建不同的视图,显示模式会处理其余的。
例如,我们可以创建一个视图“Home.aspx”,它将为桌面计算机呈现,而Home.Mobile.aspx将为移动设备呈现。现在,当最终用户向MVC应用程序发送请求时,显示模式会检查“user agent”头信息,并相应地为设备呈现合适的视图。
解释MVC模型绑定器(Model Binders)?
模型绑定器(Model binder)将HTML表单元素映射到模型。它充当HTML UI和MVC模型之间的桥梁。很多时候,HTML UI的名称与模型属性的名称不同。因此,在绑定器中,我们可以编写UI和模型之间的映射逻辑。
解释MVC脚手架(Scaffolding)的概念?
Note :- Do not get scared with the word. Its actually a very simple thing.
脚手架(Scaffolding)是一种技术,MVC模板通过该技术自动生成CRUD代码。CRUD代表创建、读取、更新和删除。
因此,要使用脚手架技术生成代码,我们需要选择一种模板类型(跳过空模板)。
例如,如果您选择“使用Entity Framework”模板,则会生成以下代码。
它会生成控制器代码、视图以及表结构,如下图所示。
脚手架在内部使用什么来连接数据库?
它在内部使用Entity Framework。
如何在MVC中进行异常处理?
在控制器中,您可以重写“OnException”事件,并将“Result”设置为当发生错误时要调用的视图名称。在下面的代码中,您可以看到我们将“Result”设置为名为“Error”的视图。
我们还设置了异常,以便它可以在视图中显示。
public class HomeController : Controller { protected override void OnException(ExceptionContext filterContext) { Exception ex = filterContext.Exception; filterContext.ExceptionHandled = true; var model = new HandleErrorInfo(filterContext.Exception, "Controller","Action"); filterContext.Result = new ViewResult() { ViewName = "Error", ViewData = new ViewDataDictionary(model) }; } }
要在视图中显示上述错误,我们可以使用以下代码:
@Model.Exception;
如何处理MVC单个视图中指向多个操作的多个提交按钮?
让我们详细阐述面试官想问什么,因为上述问题只是一个单行本,并没有明确面试官想问的内容。
设想一个场景,您有一个带有两个提交按钮的视图,如下面的代码所示。
<form action="Action1" method=post> <input type=”submit” name=”Submit1”/> <input type=”submit” name=”Submit2”> </form>
在上面的代码中,当最终用户单击任何一个提交按钮时,它都会向“Action1”发出HTTP POST请求。
面试官的问题是:
“如果我们希望在单击“Submit1”按钮时调用“Action1”,而在单击“Submit2”按钮时调用“Action2”该怎么办?”
现在我们已经理解了问题,让我们详细回答这个问题。有两种方法可以解决上述问题:一种是普通的HTML方式,另一种是“Ajax”方式。
在HTML方式中,我们需要创建两个表单并将“Submit”按钮放在每个表单内。并且每个表单的action都会指向不同的/各自的操作。您可以看到下面的代码,第一个表单发布到“Action1”,第二个表单将根据单击哪个“Submit”按钮发布到“Action2”。
<form action="Action1" method=post> <input type=”submit” name=”Submit1”/> </form> <form action="Action2" method=post> <input type=”submit” name=”Submit2”> </form>
如果面试官抱怨上述方法不是AJAX,那么第二种方法就派上用场了。在Ajax方式中,我们可以创建两个不同的函数“Fun1”和“Fun2”,请看下面的代码。这些函数使用JQUERY或其他框架发出Ajax调用。这些函数中的每一个都绑定到“Submit”按钮的“OnClick”事件。
<Script language=javascript> function Fun1() { $.post(“/Action1”,null,CallBack1); } function Fun2() { $.post(“/Action2”,null,CallBack2); } </Script> <form action="/Action1" method=post> <input type=submit name=sub1 onclick=”Fun2()”/> </form> <form action="/Action2" method=post> <input type=submit name=sub2 onclick=”Fun1()”/> </form>
什么是CSRF攻击,以及如何在MVC中防止它?
CSRF代表跨站请求伪造。所以,如果您看到伪造的词典含义:
“模仿或复制支票、官方文件上的签名等,以欺骗权威来源以获取经济利益的行为。”因此,当涉及到网站时,这种伪造被称为CSRF(跨站请求伪造)。
CSRF是一种攻击网站的方法,在这种方法中,攻击者模仿(即伪造)为可信来源并将数据发送到网站。真正的网站会无辜地处理信息,认为数据来自可信来源。
例如,考虑下面的在线银行屏幕。最终用户使用此屏幕来转账。
下面是一个由攻击者创建的伪造网站,从外面看起来像一个游戏网站,但内部它会点击银行网站进行转账。
伪造网站的内部HTML包含那些隐藏字段,其中包含账号和金额以进行转账。
Win 1000000 US$ <form action="https://:23936/Genuine/Transfer" method=post> <input type=hidden name="amount" value="10000" /> <input type=hidden name="account" value="3002" /> <input type=submit value="Play the ultimate game" /> </form>现在,假设用户已登录到真实的银行网站,而攻击者将此伪造的游戏链接发送到他的电子邮件。最终用户认为这是一个游戏网站,点击“Play the Ultimate Game”按钮,内部恶意代码会执行转账过程。
因此,可以通过使用令牌来解决此问题的正确方法:
- 最终用户浏览到转账屏幕。在屏幕提供服务之前,服务器会在HTML屏幕中注入一个秘密令牌,形式为隐藏字段。
- 从现在开始,当最终用户将请求发回时,他必须始终发送秘密令牌。该令牌将在服务器上进行验证。
在MVC中实现令牌是一个两步过程:
首先,在操作上应用“ValidateAntiForgeryToken”属性。
[ValidateAntiForgeryToken] public ActionResult Transfer() { // password sending logic will be here return Content(Request.Form["amount"] + " has been transferred to account " + Request.Form["account"]); }
其次,在HTML UI屏幕上调用“@Html.AntiForgeryToken()”来生成令牌。
转账 <form action="Transfer" method=post> 输入金额 <input type="text" name="amount" value="" />
输入账号
@Html.AntiForgeryToken() <input type=submit value="transfer money" /> </form>因此,从现在开始,任何不受信任的来源向服务器发送请求都会导致以下伪造错误。
如果您查看HTML的源代码,您会发现以下带有秘密密钥的验证令牌隐藏字段。
<input name="__RequestVerificationToken" type="hidden" value="7iUdhsDNpEwiZFTYrH5kp/q7jL0sZz+CSBh8mb2ebwvxMJ3eYmUZXp+uofko6eiPD0fmC7Q0o4SXeGgRpxFp0i+Hx3fgVlVybgCYpyhFw5IRyYhNqi9KyH0se0hBPRu/9kYwEXXnVGB9ggdXCVPcIud/gUzjWVCvU1QxGA9dKPA=" />
请阅读这篇博客,其中详细介绍了如何使用“IModelBinder”接口创建模型绑定器: - 解释MVC模型绑定器?请从本文顶部下载MVC面试问答的电子学习副本,以供准备。
有关各种主题(包括ASP.NET、设计模式、WCF、MVC、BI、WPF)的技术培训,请访问www.sukesh-marla.com。
欲进一步阅读,请观看以下面试准备视频和分步视频系列。