• ASP.NET MVC 初体验


    MVC系列文章终于开始了,前段时间公司项目结束后一直在封装一个html+ashx+js+easyui的权限系统,最近差不多也完成了,迟些时候会分享源码给大家。当然这个MVC系列结束后如果时间允许还会另写一个MVC + EF + ExtJs的权限系统作为总结,希望有兴趣的朋友保持关注。

    本系列文章使用的环境是:VS2010 + 4.0 Framework、sql 2008企业版、MVC3、EF4.1
    注:VS2010不带MVC3,ASP.NET MVC 3 RTM下载地址。装的时候vs不要打开,先装MVC3,再装汉化包。安装中不要强制取消,就算取消也要等回滚完,否则下次vs启动不了。

    一、ASP.NET MVC 3 小试牛刀

    新建项目 - 创建mvc3 web应用程序 - 模板选空、视图引擎Razor、不使用HTML5,建好项目后,解决方案是这样的:

    和传统的webForm项目结构完全不一样,这里不写任何代码直接Ctrl+F5跑下程序,会得到:

    webForm里直接运行项目会列出已有的文件让你点选然后运行,当然也可以在页面上右键 - 在浏览器中查看,mvc项目里无法通过在.cshtml视图文件上右键 - 在浏览器中查看的方式运行页面,而是通过【路由】找控制器Controller里的Action,Action再调用指定的视图View呈现页面。因此运行MVC项目只能Ctrl+F5或者F5调试的方式运行程序,要看不同的页面需要在路由里进行调整。后续会有文章详细讲解路由,这里仅快速理解下MVC:
    M Model:实体模型 / 视图模型(ViewModel);
    V View:视图。简单说就是html页面,这个html页面接收强类型的数据,方便展示数据;
    C Controller:控制器。处理浏览器发过来的请求,根据url过来的路径去控制器里寻找对应的Action响应浏览器请求。

    解决上面404错误问题先得看看Global.asax里默认路由的定义:

    routes.MapRoute(
        "Default", // 路由名称
        "{controller}/{action}/{id}", // 带有参数的 URL
        new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
    );

    只有一个路由,按照路由匹配自上到下的顺序,那么本程序使用的就是这个名为Default的路由。根据这个路由的定义浏览器有类似这种Url:localhost:****/abc/def/e 请求过来自然就和这个路由匹配({controller}/{action}/{id})上了,那么找的是名称为abc的Controller,调用abcController里的名为def的Action,呈现的是def这个Action里指定的视图View。当然这种Url:localhost:**** 请求过来也能和这个名为Default的路由匹配上,因为这个默认路由定义了默认值:HomeController、Index Action、Id值可选。

    到这里就很明显知道为何是404了,因为既没有HomeController,也没有Index这个Action,同时也没有任何视图View。在解决方案里的Controller目录上右键 - 创建控制器,为了和这个路由的定义搭上,控制器的名字就是HomeController,是一个空控制器。新建就好了后打开HomeController.cs:

    public ActionResult Index()
    {
        return View();
    }

    HomeController里默认就有一个叫Index的Action,并且这个Action的返回值不是void,也不是bool,也不是string,而是ActionResult。这个ActionResult就可以理解是视图,返回一个视图给Action(上后面的代码直接return View();的话就是返回默认视图,即和Action同名的Index),然后响应给控制器,最终显示给用户。
    注:常用的还有JsonResultRedirectResult等等。具体点这里

    先不返回视图给Action,修改下返回值类型并返回一个string试试:

    public string Index()
    {
        return "hElLo WOrLd!!!";
    }

    很明显,返回的不是ActionResult了,而是string,先按F6编译下再Ctrl+F5跑下程序,显示:

    很容易理解:默认路由找HomeController下的名为Index的Action拿到数据显示到浏览器,这里拿到的数据就是Index这个Action返回的字符串。右键查看下页面的源码,很纯粹的源码,就一个“hElLo WOrLd!!!”字符串。
    注意下上图的url:localhost:1030,其实完整的是localhost:1030/home/index(默认路由里已经指定了,所以可以省略),看的出路由里定义之后就体现在浏览器url里了:先找Home,再找Home下的Index。

    再修改方法:

    public ActionResult Index()
    {
        return View();
    }

    然后在这个方法里右键 - 添加视图 - 默认就是和Action同名的Index视图,不使用模板。添加后项目的Views目录下就多一个Home子目录,目录里有一个Index.cshtml,修改其中的代码为:

    @{
        Layout = null;
    }
    hElLo WOrLd!!!

    Layout=null就是不使用模板。F6编译下项目再跑下程序发现和之前显示一样,查看源码也一样。这里很好理解:Controller找Action,Action调用对应的View呈现页面
    完整的url是:localhost:1030/home/index 这里跟上面稍有不同:上面的index这个action直接就返回了一个string,未调用任何试图,这里的index显示的是view返回的string。

    二、向视图中输出动态内容

    asp、aspx等动态页面相比于静态的html好处就是可以根据传递的参数动态的显示内容,视图.cshtml自然也可以,可以通过ViewBag来实现,修改action:

    public ActionResult Index()
    {
        int hour = DateTime.Now.Hour;
        ViewBag.Greeting = hour < 12 ? "早上好" : "下午好";
        return View();
    }

    通过一个三元表达式给ViewBag.Greeting赋了一个值,然后在前台View加个@符号就可以调用了:@ViewBag.Greeting WOrLd!!!
    F6编译下项目再跑下程序,如愿的显示了:下午好 WOrLd!!!
    注:前台和后台的ViewBag打点都是无智能提示的,只要前后台的名称一样即可调用。

    三、稍复杂的例子

    ok,到这基本就已经知道mvc是怎么玩的了,现在来一个稍复杂的例子,模拟邀请客人参加party的小程序:
    先到解决方案的Models目录下添加一个客人信息类:

    /// <summary>
    /// 客人信息类
    /// </summary>
    public class GuestResponse
    {
        public string Name { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public bool? WillAttend { get; set; }  //是否参加(可空类型)
    }

    修改之前的Index视图(Index.cshtml):

    @{
        Layout = null;
    }
    <!DOCTYPE html>
    <html>
    <head>
        <title>Index</title>
    </head>
    <body>
        <div>
            @ViewBag.Greeting, WOrLd!!!
            <p>
                我们将举办一个Party<br />
            </p>
            @Html.ActionLink("填表参加", "RsvpForm")
        </div>
    </body>
    </html>

    跟普通html页面不同的是Rozar语法里所有的页面元素都是通过html辅助方法生成的,这里的@Html.ActionLink("填表参加", "RsvpForm")就是生成一个a标签。Rozar语法简单清爽,写个@符号就可以开始在页面上写代码了。

    根据智能提示可知,第一个参数"RSVP Now"是a标签的文字,第二个参数"RsvpForm"是这个a标签跳转的地址,通过Action实现跳转自然需要添加这个Action,否则点击又找不到视图了:

    public ViewResult RsvpForm()
    {
        return View();
    }

    一看到return View();自然返回的是默认的视图,即RsvpForm。继续去添加这个视图:在这个ResvpForm这个Action内右键 - 添加视图 - 勾选上“创建强类型视图” - 选择GuestResponse模型类(如果找不到就先按F6编译下项目)- 不使用模板类和脚本库,生成的View是:

    @model SimpleMVC3Demo.Models.GuestResponse
    @{
        Layout = null;
    }
    <!DOCTYPE html>
    <html>
    <head>
        <title>RsvpForm</title>
    </head>
    <body>
        <div>
        </div>
    </body>
    </html>

    注意第一行:@model SimpleMVC3Demo.Models.GuestResponse 这就是勾选强类型视图的好处,直接把强类型抛到了View里,其实手动写这一行也是一样的效果(同样需要编译项目)
    继续完善RsvpForm这个视图,在body里去掉div然后添加表单和一些文本框下拉列表供用户填写信息:

        @using (Html.BeginForm("RsvpForm", "Home", FormMethod.Post))
        { 
            <p>
                Your name: @Html.TextBoxFor(x => x.Name)
            </p> 
            <p>
                Your email: @Html.TextBoxFor(x => x.Email)</p> 
            <p>
                Your phone: @Html.TextBoxFor(x => x.Phone)</p> 
            <p>
                Will you attend?
                @Html.DropDownListFor(x => x.WillAttend, new[] { 
                    new SelectListItem() {Text = "我会参加Party", Value = bool.TrueString}, 
                    new SelectListItem() {Text = "我不参加Party", Value = bool.FalseString} 
                }, "Choose an option")
            </p> 
            <input type="submit" value="提交" /> 
        }

    这里的x => x.属性方式其实就是lambda表达式的写法,这里不用x,用任何字母都可以,前提是必须已经把强类型的抛到View里。

    这就是这个表单提交的地址,其实就等于webForm里的:

    <form action="/Home/RsvpForm" method="post"> 
        <input type="text" name="username" />
        <input type="text" name="userpwd" />
        <input type="submit" value="提交" />
    </form> 

    这个Form提交的地址是指定的HomeController下的RsvpForm,之前已经有一个:

    public ViewResult RsvpForm()
    {
        return View();
    }

    其实提交表单处理的不是这个Action,这个是get请求的Action,需要另加一个post的Action(mvc里默认的action响应的是get方式的请求,用于请求页面内容,响应post请求的action一般是提交表单等):

    [HttpPost]
    public ViewResult RsvpForm(GuestResponse guestResponse)
    {
        return View("Thanks", guestResponse);
    }

    这只是个简单的Action,并没有对用户提交的表单内容进行保存,而是直接返回了一个Thanks视图,并且把用户对象也就是用户在表单里填写的用户信息传到了Thanks这个视图,来看看这个用户对象guestResponse如何传递的:在这个HttpPost下面按F9加个断点,然后按F5调试下项目,当提交表单时,这个对象其实就已经有属性了:

    很明显这个对象已经有值了,继续在Views目录下的Home目录上右键 - 添加视图 - 视图名称Thanks - 勾选强类型视图 - 不使用模板页和脚本库。ok,这个视图也拿到了传递过去的用户对象,其实就可以在body里输出一些友好的回复:

        <div>
            <h1>
                Thank you, @Model.Name!</h1>
            @if (Model.WillAttend == true)
            { 
                @:你能来真的太好了,饮料和零食都为你准备好了!
            }
            else
            { 
                @:很遗憾你不能来。
            }
        </div>

    注意看,Razor视图引擎里可以直接写if else逻辑语句。

    写到这里该跑下程序了,再次F6编译下后按Ctrl + F5跑下程序。
    第一个页面:

    源码:

        <div>
            下午好, WOrLd!!!
            <p>
                我们将举办一个Party<br />
            </p>
            <a href="/Home/RsvpForm">填表参加</a>
        </div>

    第二个页面:

    源码:

    <form action="/Home/RsvpForm" method="post">
         <p> Your name: <input id="Name" name="Name" type="text" value="" /> </p> <p> Your email: <input id="Email" name="Email" type="text" value="" /></p> <p> Your phone: <input id="Phone" name="Phone" type="text" value="" /></p> <p> Will you attend? <select id="WillAttend" name="WillAttend">
          <
    option value="">请选择</option>       <option value="True">我会参加Party</option>       <option value="False">我不参加Party</option>     </select> </p> <input type="submit" value="提交" /> </form>

    第三个页面:

    源码:

        <div>
            <h1>
                Thank you, 汪杰!</h1>
                你能来真的太好了,饮料和零食都为你准备好了!
        </div>

    注意看上面两个图的url地址一样,都是:localhost:1031/Home/RsvpForm
    但是页面显示的内容不同,因为一个是get请求,一个提交表单是post请求,提交后被导向另一个视图了。

    验证表单:刚才的表单就算任何都不填写也可以提交,显然不符合常理,验证需要三步,第一去模型类里定义验证规则:

    [Required(ErrorMessage = "姓名不能为空")]
    public string Name { get; set; }
    
    [Required(ErrorMessage = "邮箱地址不能为空")]
    [RegularExpression(".+\@.+\..+", ErrorMessage = "请输入合法的邮箱地址")]
    public string Email { get; set; }
    
    [Required(ErrorMessage = "电话不能为空")]
    public string Phone { get; set; }
    
    [Required(ErrorMessage = "请选择是否参加")]
    public bool? WillAttend { get; set; }   //是否参加(可空类型)

    这是Data Annotation标注的形式来验证的,如果还不知道Data Annotation为何物,建议先去了解一下
    注:需要引用命名空间System.ComponentModel.DataAnnotations;

    第二去控制器里判断是否通过验证:

    if (ModelState.IsValid)
    {
        return View("Thanks", guestResponse);
    }
    else
    {
        return View();
    }

    直接调用的ModelState.IsValid方法来验证模型是否通过验证。当然到这里还不算结束,第三去View里应用验证:
    在using语句下面加上:@Html.ValidationSummary() 表示此表单需要验证,这时候如果表单数据不合法则不会被导向到Thanks视图。

    到此,再跑程序生成的html页面就又不同了,会有验证信息在里面。一旦用户输入的内容不符合在模型类里定义的规则,那么验证信息就会显示出来。看源码:

    <form action="/Home/RsvpForm" method="post">
    <
    div class="validation-summary-valid" data-valmsg-summary="true">
    <
    ul><li style="display:none"></li></ul>
    </
    div>
         <p> Your name: <input data-val="true" data-val-required="姓名不能为空" id="Name" name="Name" type="text" value="" /> </p> <p> Your email: <input data-val="true" data-val-regex="请输入合法的邮箱地址" data-val-regex-pattern=".+@.+..+" data-val-required="邮箱地址不能为空" id="Email" name="Email" type="text" value="" /></p> <p> Your phone: <input data-val="true" data-val-required="电话不能为空" id="Phone" name="Phone" type="text" value="" /></p> <p> Will you attend? <select data-val="true" data-val-required="请选择是否参加" id="WillAttend" name="WillAttend">
          <
    option value="">请选择</option>       <option value="True">我会参加Party</option>       <option value="False">我不参加Party</option>      </select> </p> <input type="submit" value="提交" /> </form>

    高亮错误信息行其实很简单,直接在RsvpForm视图里添加一个默认css的引用:

    <link rel="Stylesheet" href="@Href("~/Content/Site.css")" type="text/css"/>

    再提交非法表单信息就高亮了错误行:

    其实提交表单的一刹那浏览器还是有刷新的,说明这是个服务器端验证,实际项目中更应该服务端和客户端双验证,后续文章会有详细介绍。

    本文源码

    系列文章导航

  • 相关阅读:
    ThinkPHP
    ThinkPHP
    静态化
    静态化
    静态化
    设计模式
    sublime
    静态化
    OPTIMIZE TABLE 小解
    information_schema系列八(事物,锁)
  • 原文地址:https://www.cnblogs.com/oppoic/p/welcome_to_asp_net_mvc.html
Copyright © 2020-2023  润新知