Controller(控制器)在ASP.NET MVC中负责控制所有客户端与服务端的交互,并且负责协调Model与View之间数据传递,是ASP.NET MVC框架核心。Controller为ASP.NET MVC框架的核心组成部分,其主要负责处理浏览器请求,并决定响应什么内容给浏览器,但并不负责决定内容应如何显示(View的职责)。
5.1 动作方法
Controller本身就是一个类(Class),该类别有许多方法(Method),这些方法中只要是公开方法(public method)就会被视为是一个动作(Action)或动作方法(Action Method),并可以通过该动作方法接收客户端传来的请求和决定响应的视图(View)。
Controller应具备如下几个基本条件。
(1)Controller必须为公开类别;
(2)Controller名称必须以Controller结尾;
(3)必须继承自ASP.NET MVC内建的Controller类别,或实现IController自定义类别;
(4)动作方法必须为公开方法,任何非公开的方法如声明为private或protected的方法都不会被视为一个动作方法;
5.1.1 同名方法问题
在一般的类中,方法是可以重载的,但是动作方法是不可以重载的。如示例1中代码在编译的时候不会有任何问题,但在运行时就会出现如图5-1所示的错误。
示例1
public class AccountController : Controller { public ActionResult Register() { //省略代码 } public ActionResult Register(User user) { //省略代码 } } |
图5-1 动作方法不明确
上面的错误说明ASP.NET MVC 路由系统不能凭借方法参数去定位动作方法。
5.1.2 动作方法上的特性
解决同名方法的问题一般有3种方法,3种方法都是要在动作方法上应用特性。
-
谓词筛选特性
在ASP.NET MVC中,提供了区分不同请求的请求谓词筛选特性,如HttpGet、
HttpPost、HttpDelete和HttpPut,分别对应Get、Post、Delete和Put几种请求。对于示例1的代码,可以按照示例2的方式应用特性。
示例2
public class AccountController : Controller { [HttpGet] public ActionResult Register() { //省略代码 } [HttpPost] public ActionResult Register(User user) { //省略代码 } } |
示例2的代码表示,Register( ) 方法只能对应到Get方式的请求,Register(User user)方法只能对应到Post方式请求。
-
NonAction特性
若控制器某个方法特性为NonAction,即使该Action方法是"公开方法",也不会被被看作Action来运行。
主要用途:
- 保护Controller中的特定公开方法不要发布到Web上;
- 功能尚未开发完成就要进行部署,暂时不想将此方法删除。
示例3
public class HomeController : Controller { public ActionResult Index() { return View(); } [NonAction] public ActionResult Index(int id) { return View(); } } |
-
ActionName特性
在ASP.NET MVC中,路由中定义的Action的名称和控制器中定义的动作方法的名称命名通常是一致的,但这只是一般的方法,也可以让Action和动作方法的名称不一致,用法如示例4所示。
示例4
public class HomeController : Controller { public ActionResult Register() { //省略代码 } [ActionName("RegisterUser")] public ActionResult Register(User user) { //省略代码 } } |
示例5中,将Register(User user)方法的Action名声明为RegisterUser,解决了同名冲突问题。使用这种方法,需要注意在视图中提交表单的Action属性也要做相应的修改,如下面的代码。
<form action="/Account/RegisterUser" method="post">
</form>
5.1.3 动作方法的结果类型
在前面的内容中,我们习惯使用ActionResult作为动作方法的返回类型,通过View()方法返回对象。其实View()方法的返回类型是ViewResult,ViewResult和ActionResult的关系图5-2所示。
图5-2 ActionResult类关系图
在动作方法定义中,很显然使用了多态,ActionResult是一个抽象类,返回的对象为子类类型。在实际的开发中,有时候不需要 向客户端输出HTML,而是文件、文本等其它一些输出,这时候子类就派上用场了,常用的输出类型如表5-1所示。
表5-1 常用输出类型
输出类型 |
说 明 |
EmptyResult |
输出空内容 |
ContentResult |
将指定内容作为文本输出 |
JsonResult |
输出JSON字符串 |
JavaScriptResult |
输出JavaScript文件 |
RedirectResult |
指定的 URI 来执行重定向 |
RedirectToRouteResult |
指定路由值来执行重定向 |
FilePathResult |
指定文件路径来输出文件 |
FileContentResult |
指定字节数组来输出文件 |
FileStreamResult |
指定流来输出文件 |
ViewResult |
调用视图文件输出视图 |
在实际使用这些类型时,不需要在动作方法中实例化这些类的实例再返回,再Controller类中,已经定义了返回这些类型实例的方法,直接调用即可。如View()方法。常用的方法如表5-2所示。
表5-2 常用控制器输出方法
类型 |
方 法 |
EmptyResult |
无 |
ContentResult |
Content(string content) |
JsonResult |
Json(object data) |
JavaScriptResult |
JavaScript(string script) |
RedirectResult |
Redirect(string url) |
RedirectToRouteResult |
RedirectToAction(string actionName) |
FilePathResult |
File(string fileName, string contentType) |
FileContentResult |
File(byte[] fileContents, string contentType) |
FileStreamResult |
File(Stream fileStream, string contentType) |
ViewResult |
View(string viewName) |
各种方法的用法如示例5所示。
示例5
public class HomeController : Controller { public ActionResult Index() { return View(); } //页面跳转 public ActionResult RedirectTest() { return Redirect("/Home/Detail"); } //页面跳转 public ActionResult RedirectToRouteTest() { return RedirectToRoute(new { controller = "Home", action = "Detail", id = 1, cate = "test" }); } //页面跳转 public ActionResult RedirectToActionTest() { return RedirectToAction("Detail", new { id = 1, cate = "test" }); } //输出JSON文件 public ActionResult JsonTest() { var book = new { bookid = 1, bookName = "精通ASP.NET MVC", author = "Fangyc", publishData = DateTime.Now }; return Json(book, JsonRequestBehavior.AllowGet); } //输出JavaScript文件 public ActionResult JavaScriptTest() { string js = "alert('Welecome to ASP.NET MVC!')"; return JavaScript(js); } //按文件路径输出文件 public ActionResult FilePathTest() { return File("~/Content/rain.mp3", "audio/mp3"); } //对字符串编码并输出文件 public ActionResult FileContentTest() { string content = "Welcome to ASP.NET MVC!"; byte[] contents = System.Text.Encoding.UTF8.GetBytes(content); return File(contents, "text/plain"); } //用文件流输出文件 public ActionResult FileStreamTest() { string content = "Welcome to ASP.NET MVC!"; byte[] contents = System.Text.Encoding.UTF8.GetBytes(content); FileStream fs = new FileStream( Server.MapPath("~/Content/ASP.NET MVC.pdf"), FileMode.Open); return File(fs, "application/pdf"); } //输出简单文本 public ActionResult ContentTest() { string content = "<h1>Welcome to ASP.NET MVC!</h1>"; return Content(content); } } |
在ASP.NET MVC中,动作方法的返回类型并不一定是ActionResult类型,也可以是其它任何类型,甚至是void类型,如下面的代码。
public int Sum(int num1,int num2) { int sum=num1+num2; return sum; } public void log() { File.WriteAllText(@"D:log.test", "保存文件"); } |
上述这种返回类型的动作方法称为隐式动作方法,ASP.NET MVC 自动做了一些处理,将void类型对应到EmptyResult类型,将其它非ActionResult类型对应到ContentResult类型。
5.2 动作方法参数
我们以前在动作方法中获取 URL 和表单数据的方法,可能会采用如下方式:
string name=Request.Forms["name"];
string id=RouteData.Values["id"];
除了上面的方法外,ASP.NET MVC 还提供了更加便利的方法。
5.2.1 简单类型映射
在ASP.NET MVC中,可以把路由数据、URL数据、表单数据自动映射到动作方法参数中,这些参数可以直接定义为基本数据类型或string类型。
示例6
public ActionResult Detail() { int id = Request.QueryString["id"] != null? Convert.ToInt32(RouteData.Values["id"]):0; // 省略代码 return View(); }
public ActionResult Detail(int id=0) { // 省略代码 } |
示例6中,第二个方法参数使用了数据映射的功能,RouteData.Values["id"]的值可以自动映射到参数id中。
示例7
public ActionResult List() { int pageCount = 0;//总页数 int categoryId = Request.QueryString["categoryId"] != null ? Convert.ToInt32(Request.QueryString["categoryId"]) : 1; //页序号 int pageIndex = Request.QueryString["pageIndex"] != null ? Convert.ToInt32(Request.QueryString["pageIndex"]) : 1; //排序字段 string order = Request.QueryString["order"] != null ? Convert.ToString(Request.QueryString["order"]) : "PublishDate"; BookManager manager = new BookManager(); List<Book> list = manager.GetBooks( categoryId, this.PageSize, pageIndex, ref pageCount, order); }
public ActionResult List(int categoryId = 1, int pageIndex = 1, string order = "PublishDate") { int pageCount = 0;//总页数 BookManager manager = new BookManager(); List<Book> list = manager.GetBooks(categoryId, this.PageSize, pageIndex, ref pageCount, order); }
|
示例7中,第二个方法参数使用了数据映射的功能,该方法定义了多个参数,各个参数的值都由Request.QueryString映射而来。
示例8
public ActionResult LogIn() { string userName=Request.Form["userName"]; string password=Request.Form["password"]; string returnUrl=Request.Form["returnUrl"]; // 省略代码 } public ActionResult LogIn(string userName, string password, string returnUrl) { // 省略代码 } |
从上述三个示例可以看出,使用动作方法参数自动映射,简化了大量代码。
获取数据的优先级依次是表单数据--->路由数据--->URL数据。在定义动作方法参数时,需要保证以下两点。
- 参数名要和目标数据参数名一致(不区分大小写)。
- 参数数据类型要和来源数据的目标数据类型一致。
对于值类型参数的数据,如果从来源数据获取不到相应的值,就会出现参数映射的运行时错误,一般把这种参数直接声明为可空类型或可选参数。
5.2.2 映射模型
动作方法参数的映射不仅仅是简单类型,还可以是复杂类型。如示例 8所示。
示例8
<form action="/Account/RegisterUser1" class="member_form" id="Form1" method="post"> <p> <label><span>*</span>用户名</label> <input class="opt_input" name="LoginId" type="text"/> 5-12个字符或数字组成,可用中文名 </p> <p> <label><span>*</span>真实姓名</label> <input class="opt_input" name="Name" type="text"/> </p> <p> <label><span>*</span>密    码</label> <input class="opt_input" name="LoginPwd" type="password"/> </p> <p> <label><span>*</span>确认密码</label> <input class="opt_input" name="PasswordConfirm" type="password" /> </p> <p> <label><span>*</span>电子邮件</label> <input class="opt_input" name="Mail" type="text" /> </p> <!-- 省略其它代码 --> </form>
// 控制器代码 public ActionResult RegisterUser(User user) { UserManager manager = new UserManager(); if (!manager.Register(user)) { //省略代码 } return View("Register", user); }
|
示例8的方法中,并没有创建User对象,也没有给其属性赋值。其实这些工作是ASP.NET MVC通过分析User对象的属性和表单中元素按名称进行匹配并进行赋值。通过这种方式,自动完成对象创建和初始化赋值工作。这也被称为ASP.NET MVC的模型自动绑定功能。
5.3 案例:用户注册(或信息添加)