Fork me on GitHub
听雨轩
生命易破碎,梦想只争朝夕!

7.9 从控制器访问模型数据

本节,您将创建一个新的MoviesController类并编写代码读取电影数据并用视图模板在浏览器中显示他们。在继续前,请先编译你的应用程序,不然下面找不到模型类。

右键Controllers文件夹新建一个MoviesController控制器。选择下面的选项:

· Controller name: MoviesController. (默认. )

· Template: Controller with read/write actions and views, using Entity Framework.

· Model class: MOVIE (MvcMovie.DAL)

· Data context class: MovieEntities (MvcMovie.DAL)

· Views: Razor (CSHTML). (默认.)

clip_image002

单击“Add”。Visual Studio创建了下面的文件夹和文件:

1. 在项目的Controller文件夹下MoviesController.cs文件

2. 在项目的View文件夹下Movies文件夹

3. 在新创建的文件夹Views\Movies下Create.cshtml, Delete.cshtml,
Details.cshtml, Edit.cshtml, and Index.cshtml。

clip_image004

ASP.NET MVC 3架构机制自动创建CRUD(create, read, update, and delete)响应方法和视图。您现在拥有了全部的web应用程序的功能,支持增加、显示、编辑、删除电影作品。

运行应用程序并通过在浏览器地址栏中URL后追加/Movies来浏览Movies控制器。因为应用程序依托默认的路由(在Global.asax文件中定义),浏览请求http://localhost:xxxxx/Movies被路由到Movies控制器的默认方法Index。换句话说,http://localhost:xxxxx/Movies实际上和http://localhost:xxxxx/Movies/Index是一样的。因为您还没有添加任何东西,所以电影列表是空的。

clip_image006

7.10 创建电影

选择“Create New”链接。输入一个电影的详细信息然后单击“Create”按钮。

clip_image008

单击“Create”按钮使页面回发到服务器端(那里的电影信息将会保存在数据库中)。您将重定向到/Movies URL,在列表里您能看到刚被添加的电影信息。

此时,Visual Studio还自动实现了数据验证,在上图的创建页面,不输入信息,然后点击“Create”会出现如下结果(后面我们会介绍自定义模型验证):

clip_image010

运行结果:

clip_image012

查看数据库:

clip_image014

您可以创建一些电影作品,测试全部的功能,编辑、明细、删除。

7.11 审视代码

打开Controllers\MoviesController.cs文件并审视生成的Index方法代码。部分控制器Index方法的代码如下所示:

public class MoviesController : Controller
    {
        private MovieEntities db = new MovieEntities();

        //
        // GET: /Movies/

        public ViewResult Index()
        {
            return View(db.MOVIEs.ToList());
        }
}

如前面所述,下面的行在MoviesController类中实例化了一个电影数据库的内容。

private MovieEntities db = new MovieEntities();

Movies控制器返回数据库中的所有电影资料实体并把结果传递给Index视图。

强类型模型和@model关键字

在教程的前面部分,您了解了如何使用ViewBag对象把数据通过控制器传递给视图。ViewBag是一个动态对象,提供了方便的迟绑定方式将信息传递给视图。

ASP.NET MVC也支持传递强类型数据给视图模板。这种强类型的方式支持编译时检查代码和丰富的智能感知。我们将在MoviesController类和Index.cshtml视图模板中采用这种方式。请注意该代码创建一个List对象时调用Index方法中View Helper方法。代码通过控制器传递电影列表给视图:

public ViewResult Index()
        {
            return View(db.MOVIEs.ToList());
        }

通过在视图模板文件的顶部包含@model表达式,您可以指定在视图中您期望使用的对象类型。当您创建电影控制器的时候,Visual Studio自动在视图模板文件Index.cshtml的顶部包含@model表达式:@model IEnumerable<MvcMovie.DAL.MOVIE>

@model指令允许您访问由控制器使用强类型Model对象传递给视图的电影列表。比如,Index.cshtml模板,代码通过foreach表达式遍历了基于强类型的电影资料。

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.TITLE)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.RELEASEDATE)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.GENRE)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.PRICE)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
            @Html.ActionLink("Details", "Details", new { id=item.ID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.ID })
        </td>
    </tr>
}

由于实体对象是强类型(被用作可枚举对象),循环中的每个item对象都是强类型的Movie。其他好处指的是编译时检查代码和全部的智能感知支持。

clip_image002

您现在拥有了数据库并通过简单的列表展现了他们。

审视编辑方法和视图

在这一节中,您将审视Movies控制器生成的响应方法和视图。然后您将添加

一个自定义搜索页面。运行程序并通过在URL追加/Moives浏览movie控制器。把鼠标悬停在Edit链接上,看看它执行的URL.

clip_image004

Edit的链接由视图Views\Movies\Index.cshtml 的Html.ActionLink方法生成。

@Html.ActionLink("Edit", "Edit", new { id=item.ID })

clip_image006[5]

Html对象是一个助手,它是WebViewPage基类暴露的属性。助手的ActionLink方法可以很容易的生成HTML超链接,它指向控制器的响应方法。ActionLink的第一个参数表示超链接的文本呈现(比如:<a>Edit Me</a>),第二个参数是要调用的响应方法的名称,最后一个参数生成路由数据的匿名对象(anonymous object,这里指ID=4)。

您可以使用查询字符串(query string)传递参数给响应方法。比如URLhttp://localhost:xxxxx/Movies/Edit?ID=4传递ID=4给Movies控制器的Edit方法。

clip_image008[5]

打开Movies控制器。有两个Edit方法,如下所示:

// GET: /Movies/Edit/5

       public ActionResult Edit(decimal id)
       {
           MOVIE movie = db.MOVIEs.Single(m => m.ID == id);
           return View(movie);
       }

       //
       // POST: /Movies/Edit/5

       [HttpPost]
       public ActionResult Edit(MOVIE movie)
       {
           if (ModelState.IsValid)
           {
               db.MOVIEs.Attach(movie);
               db.ObjectStateManager.ChangeObjectState(movie, EntityState.Modified);
               db.SaveChanges();
               return RedirectToAction("Index");
           }
           return View(movie);
       }

注意第二个Edit方法前面是HttpPost属性。它表明这个重载的Edit方法只能被

POST请求调用。您也可以给第一个Edit方法采用HttpGet属性,但是这不是必须的,应为方法默认为HttpGet(响应方法隐含的了HttpGet属性将被认为是HttpGet方法)。

HttpGet方法将电影的ID作为参数,并使用实体框架的Find方法查找电影,然后返回被找到的电影给视图模板。当架构体系创建编辑模板时,它检查Movie类并为每个属性生成<label><input>代码去呈现元素。下面的代码展示了自动生成的Edit视图模板:

@model MvcMovie.DAL.MOVIE

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>MOVIE</legend>

        @Html.HiddenFor(model => model.ID)

        <div class="editor-label">
            @Html.LabelFor(model => model.TITLE)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.TITLE)
            @Html.ValidationMessageFor(model => model.TITLE)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.RELEASEDATE)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.RELEASEDATE)
            @Html.ValidationMessageFor(model => model.RELEASEDATE)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.GENRE)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.GENRE)
            @Html.ValidationMessageFor(model => model.GENRE)
        </div>

        <div class="editor-label">
            @Html.LabelFor(model => model.PRICE)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.PRICE)
            @Html.ValidationMessageFor(model => model.PRICE)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

<form>中的<input>元素表示一个提交按钮,点击时将提交页面数据到Controller中的Edit方法。

处理POST请求

由架构体系生成的属性为HttpGet的Edit方法没有检查传给它的ID的有效性。如果用户删除URL的ID字段,错误信息如下所示:

clip_image002[5]

用户还可以传递一个不存在的ID,比如:

http://localhost:xxxxx/Movies/Edit/1234.您可以给HttpGet Edit方法做两点修改来限制URL。首先,把ID参数改为默认值为0 (id不是必须传递)。

您也可以在回传电影对象给视图模板之前,检查Find方法是否真正的找到了电影信息。

public ActionResult Edit(decimal id=0)
        {
            MOVIE movie = db.MOVIEs.FirstOrDefault(m => m.ID == id);
            if(movie==null)
            {
                return HttpNotFound();
            }
            return View(movie);
        }

如果没有找到,HttpNotFound方法被调用。

所有的HttpGet方法都遵循类似的模式。它们获取一个电影对象(在Index中返回对象列表),然后传递模型给视图。Create方法传递一个空电影对象给Create视图。所有的方法(创建、编辑、删除)都有一个HttpPost的重载方法。

在HTTP GET方法中修改数据存在安全风险,在博客ASP.NET MVC Tip #46 – Don’t use Delete Links because they create Security Holes中有描述。在HTTP GET方法中修改数据违反了HTTP的最佳实践和REST架构模式(其中规定GET请求不应改变应用程序状态)。换句话,执行GET操作应该是一个无副作用的安全操作。

posted on 2011-08-13 16:53  流水殇  阅读(4960)  评论(2编辑  收藏  举报