学习ASP.NET MVC3(5)----- Controller
其实按照正常的顺序来说,是应该先讲模型(Model) ,再讲控制器和视图的
但是由于模型完全可以专门用一层来做(比如多层架构的实体层)
因为实际上控制器Action的代码应该越少越好而将复杂的业务逻辑和数据访问留给"模型"去做,如果控制器包含过多的代码
则有可能将它变成另一种Page_Load的形式而不利于维护,但是如果在UI层上独立出一个模型文件夹来做这些,似乎和WPF的MVVM模式相差不多
而且复杂繁杂的代码完全可以用多层(BLL,Dao,Entitties等等)解决 ,所以在ASP.NET MVC中的模型意义并不大
对于我来说甚至可以忽略跳过 所以就跳过吧````
注意 上面所说的"模型"并不是具指实体层
ASP.NET MVC请求过程
一个用户 通过浏览器(Browser)发出一个请求,提交给Server端
途中经过iisapi.dll这个程序集的处理和ASP.NET基本框架的处理
然后通过路由(Routing)动态的调用Controller来处理响应的请求
请求调用时实际上就是调用Controller里的某个Action
Action通过种种处理(人工代码处理)执行一些业务逻辑,然后把Model和ViewData(要给客户展示的数据)传输给视图(View)
然后视图通过一系列框架上的东西渲染好HTML返回给客户端
Controller
控制器在ASP.NET MVC中扮演着处理客户端请求的角色
必须实现System.Web.Mvc.IController接口,通常直接继承System.Web.Mvc.Controller类
必须要以Controller结尾
通过不同的Action来处理具体的客户端请求
Action
是指在继承了System.Web.Mvc.Controller类中所以定的返回值的类型可以兼容ActionResult的方法
ASP.NET MVC 3的ActionResult
接下来看一个普通的控制器,和以前一样所有的解释都在注释里 这样比较直观
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class HomeController : Controller { //这个Index就相当于WebForm里的Default.aspx页面 //并不是说MVC里的"首页"就必须是这个Index方法/Action public ActionResult Index() { //下面的代码等同于ViewData["Message"] = "Welcome to ASP.NET MVC!"; 但是ViewData这种写法相对啰嗦 而且是纯字符串的不好控制 //其实ViewBag并没有Message这个属性, 但是它会在运行时发现,所以当这个Action传到相应的View的时候 可以在View里点出Message这个属性 ViewBag.Message = "Welcome to ASP.NET MVC!"; ViewBag.张三的名字 = "张三"; //View()这个方法返回一个ViewResult类型,也就是返回一个页面 //大家可能会觉得奇怪,View()方法并没有指定返回哪个页面,实际上View()有很多重载,你可以return View("ViewName")这样子来指定一个具体的视图 //如果没有指定的话,他会在和此控制器相同的视图文件夹里找到与此Action相对应的视图,比如现在这个是Home控制器的IndexAction、 //那它就会找Views文件夹下的Home文件夹下的Index.cshtml文件作为默认返回视图 return View(); } public ActionResult About() { //返回Views文件夹下的Home文件夹下的About.cshtml return View(); } } }
对应的Index的view:
@{ ViewBag.Title = "Home Page"; } <!--这里的Message就是开始在控制器已经赋好值的Message,值为Welcome to ASP.NET MVC!直接点出来使用--> <h2>@ViewBag.Message</h2> <p> To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>. </p>
如果要返回一个404的页面呢:
public ActionResult Page404() { return HttpNotFound(); }
效果如下:
如果将Action的名字改一下为Page405 但是访问的URL不变的话是什么样呢:
public ActionResult Page405() { return HttpNotFound(); }
效果如下:
虽然都是404,但是不同之处我想大家应该能够了解了吧
接下来在看个例子,自己创建一个继承Controller的类
上面忘记说了,如果要建立自己的控制器,就在添加里选择Controller就好了(当然也可以手动添加一个类,然后手动继承Controller类)
ControllerBase.cs:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class ControllerBase : Controller { public ActionResult Test() {
//返回一个空白页 return new EmptyResult(); } } }
然后修改HomeController继承自上面的ControllerBase:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class HomeController : ControllerBase { public ActionResult Index() { ViewBag.Message = "Welcome to ASP.NET MVC!"; ViewBag.张三的名字 = "张三"; return View(); } public ActionResult Page405() { return HttpNotFound(); } public ActionResult About() { return View(); } } }
运行看效果(把URL改为Home的Test):
可以看到就是个普通的空白页
HomeController里虽然没有Test这个Action,但是由于继承了ControllerBase,所以就也有了
创建自定义的ControllerBase,可以将很多通用的方法放在里面,以后每个控制器直接使用 方便很多
用于控制动作定义的特性
1.NonAction
2.ActionName
3.AcceptVerbs
NonAction:
使用该特性标记的公有方法不会被视为动作。
[NonAction] public void ThisIsNotAnAction() { //do something }
ActionName:
通过使用ActionName改变动作的名称
AcceptVerbs:
该特性设置动作能够对其做出响应的HTTP动词。例如,创建一个用于编辑产品细节的表单,需要两个方法:一个用于使用其数值编写表单,另一个用于将表单数值存储到数据库中
你可以编写两个独立的动作,一个名为WriteForm,另一个为ProcessForm,这不是一个好方法,因为为同一功能提供两个名称
或者使用AcceptVerbs特性,编写两个具有相同动作名称并且影响不同动词的方法,最常用的HTTP动词是GET和POST
当单击一个链接时使用第一个动词,而提交表单的时候使用第二个动词 如下列代码所示:
[AcceptVerbs(HttpVerbs.Get)] public ActionResult 更新信息() { //do something 显示表单 return View(); } [AcceptVerbs(HttpVerbs.Post)] public ActionResult 更新信息(FormCollection form) { //do something 更新信息到数据库 return View(); }
其实AcceptVerbs是以前的写法,MVC 3可以直接使用[HttpGet]和[HttpPost]
注意事项
能够通过URL访问的Action必须是Public的
如果标记了[NonAction]属性,则不能通过URL访问
默认,Action的方法名就是Action名(通过URL访问的名称),如有特殊需求也可以通过ActionName标记出特定的Action名
我们可以通过[HttpGet]和[HttpPost]来区分处理不同请求动作的同名Action
从请求检索数据
1.向动作传递简单类型
从请求检索数据的第一种方法是将数据作为方法参数传递给动作。或提供来自只包含几个参数的Post操作的参数。这种情况下,只能向动作传递简单类型数据,如整型或字符串等
从URL检索值(路由会在后面讲到,这里随便了解)
假如一个路由的规则为{controller}/{action}/{id},并且请求的URL是:http://localhost/Home/Edit/12,则Home对应{controller}、Edit对应{action}、12对应{id}
框架使用前两个标记确定控制器和动作(确定是哪个控制器的哪个Action),之后的所有标记将作为参数传递给动作
现在假定有一个动作并且接收几个参数 如下所示:
public ActionResult dongzuo(int year,int month,int day,string title,int commentId)
具有如下路由规则:
{controller}/{action}/{year}/{month}/{day}/{title}
请求的URL为:http://localhost/Home/dongzuo/2013/04/01/My-Birthday?commentId=4577
请求到达后,dongzuo这个动作的参数的值如下:
year:2013
month:04
day:01
title:My-Birthday
commentId:4577
前4个参数是从URL检索到的,并且映射到在路由规则中定义的标记,而最后一个commentId是从同名的查询字符串变量检索到的
所以,当请求到达后,你就可以使用动作那些参数进行操作了
2.从Post数据检索到值
除了URL和查询字符串检索参数值,还可以从使用POST提交的HTML表单获取值。具体只需要将HTML输入控件命名为参数名即可
接下来看个例子:
(1)首先创建一个新的ASP.NET MVC WEB站点
(2)创建一个控制器类,命名为BlogController,并为其添加两个方法:Create和Save
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespace MvcApplication1.Controllers { public class BlogController : Controller { public ActionResult Index() { return View(); } public ActionResult Create() { return View(); } public ActionResult Save(string postTitle,string postText) { ViewBag.Message = string.Format("Title:{0}<br/>Text:{1}", postTitle, postText); return View(); } } }
(3)在Views文件夹下创建一个新文件夹,命名为Blog
(4)在该新文件夹中添加一个内容视图页面,并将其命名为Create.cshtml:
@{ ViewBag.Title = "Create"; } <h2>Create</h2> <form action="/Blog/Save" method="post"> Title:<input type="text" id="postTitle" name="postTitle" /><br /> Text:<input type="text" id="postText" name="postText" /><br /> <input type="submit" /> </form>
(5)添加另外一个内容视图页面,并将其命名为Save.cshtml:
@{ ViewBag.Title = "Save"; } <h2>Save</h2> 从Create页面提交过来的值 @ViewBag.Message
(6)现在启动该Web站点,浏览到/Blog/Create 试一下,效果如下:
话说为啥<br/>没有换行而是直接显示出来了?百思不得其解啊