随笔 - 63  文章 - 1 评论 - 136 trackbacks - 1
<2008年3月>
2425262728291
2345678
9101112131415
16171819202122
23242526272829
303112345



AddThis Social Bookmark Button

搜索

 

常用链接

我的标签

随笔分类

随笔档案

好友日志

我的土窝

积分与排名

  • 积分 - 34076
  • 排名 - 1208

最新评论

原文地址:http://dotnetslackers.com/articles/aspnet/KiggBuildingADiggCloneWithASPNETMVC1.aspx
原文作者:Kazi Manzur Rashid
本文地址:http://www.cnblogs.com/jpwar/archive/2008/03/02/1087092.html
本文译者:第一控制.NET

代码下载

学习怎样使用asp.net mvc, LINQ to SQL and ASP.NET AJAX打造一个digg类型的应用程序。

前言
前面一段时间,我一直试着学习新的ASP.NET MVC framework,我看到了很多这方面的高级话题的讨论,比如ioc容器/依赖注入,View Engine,Controller factory等。但是我找不到一篇简单的文章来展现ASP.NET MVC framework的能量。当然,知道那些高级话题确实有一些额外的好处,但是他们并不能对你你在ASP.NET MVC Framework开发上有所帮助。在DotNetSlackers team打造的这篇文章里,我将用asp.net mvc开发一个简单的类似Digg / DotNetKicks类型的程序。你可以在下面的地址里查看这个程序:

程序演示

注意:这篇文章和代码都是基于ASP.NET 3.5 Extensions的第一个预览版。每当新的预览出来的时候,我们将相应的更新。
译注:貌似mix08版很快就要出来的样子。

准备
scottgu关于ASP.NET MVC Framework的一套介绍:
  • ASP.NET MVC 框架 简介
  • ASP.NET MVC 教程 (第一部分)
  • ASP.NET MVC 教程 (第二部分: URL路径选择)
  • ASP.NET MVC 教程 (第三部分: 把ViewData从控制器传到视图)
  • ASP.NET MVC 教程 (第四部分: 处理表单编辑和提交场景)

    译注:以上链接全部换为博客堂scottgu博客中文版链接,并比原文增加了第四部分的链接。

    Scott Hanselman的一段很酷的视频教程Screencast

    概述
    MVC (模式-视图-控制器)是一套开发ui为中心应用程序中很流行的模式。他建立在一个简单的概念上:把整个应用分割成三个逻辑模块

    • Model,模式
    • View,视图
    • Controller.控制器

    ASP.NET MVC Framework是mvc模式的一套实现,并且内置了开发web应用的能力。让我们快速浏览一下这三个模块。

    图一:MVC Framework

    images/mvc.jpg

    • Model: 是你程序的领域逻辑。通常来说,model的状态会存储在数据库里。在开发一个n层应用程序中,他是介于领域模型和商业逻辑之间的中间层。
    • View: 典型的用户界面,他负责把model的数据展示给用户,并且接受用户输入。
    • Controller: 处理用户的动作。他是整个ASP.NET MVC Framework的终极驱动力量。建立在用户动作之上,他决定了使用怎样的方法到model里取得数据,把这些数据组织成view data并且最终决定使用什么样的View去展示这些数据。

    跟web form模型相比,ASP.NET MVC Framework是一套更棒的开发web应用程序的方法。他给我们提供了一下能力:

    • 清晰的分层思路,一个模块专注于处理一类问题。并且在开发过程中给了我们绝佳的TDD (测试驱动开发) 体验。在单元测试中我们完全不用顾及其他模块,因为在framework中绝大多数的模块都是interface-based的所以这就允许我们为他们创建模仿对象。
    • 整个framework都是非常容易扩展的。可以在不影响其他模块的前提下轻松替换或者自定义每一个模块。
    • Pretty/SEO (搜索引擎优化) URLs. URLs的设置和创建权牢牢的掌握在我们手里。跟URL重写彻底说再见吧。
    • 真正的无状态网页。我们再也不需要处理postbacks和ViewState了。
    • 玩去控制HTML代码产生。这意味着再也没有多余的标签了。
    • 可以利用现有的ASP.NET的一些特性,比如Providers, Caching, Configuration等等。

    译注:我主要还是喜欢clean url和clean html。

    请求流程
    在asp.net web form程序中,URLs经常被映射到物理磁盘上的文件。当请求一个url时,于该文件相关的代码将被执行。但是,在ASP.NET MVC Framework中,URLs在Controllers就结束了,而不是传统的物理文件。Routing Handler把URL映射到Controller。当应用程序启动时,他需要注册URL筛选规则。当请求来临时,Routing Handler使用这些规则把请求映射到controller。让我们来快速浏览一下在ASP.NET MVC Framework中请求在不同层之间的流转过程:

    图二:The Request Flow

    images/request.jpg

    • 用户请求了一个URL.
    • ASP.NET MVC Framework在已经注册的筛选规则中寻找请求的URL所匹配的Controller.Framework把这个请求交给匹配的Controller
    • Controller调用Model来创建ViewData。在ViewData的创建过程中可能会多次调用model。
    • Model前面提到过他是一个中间层。他也许是一个数据存取模块,工作流,依赖于外部的web service等等。Model把Controller请求的数据返回给他。.
    • Controller选择一个View并且把他刚从model里得到的数据发送给View。View展现这些数据,并且生成html给用户浏览。

    默认约定
    在下一步开发之前,我们必须先知道下面一些默认的约定。

    首先,当为进来的请求匹配Controller时,framework使用类似UrlPathController 这样的模式来匹配。例如,如果请求的是 http://www.example.com/Home,那么就要使用HomeController 来处理这个请求。一旦当请求到达Controller,Controller根据子路径来执行一个指定的行为。或者,如果路径中没有行为那么就执行默认的行为。Controller 的默认行为是在application start事件中和筛选规则定义时一起定义的。行为在Controller类中定义为方法。例如,如果请求以下地址http://www.example.com/Home/Index,那么就会自动执行HomeController中的Index方法。如果url中还有子路径,那么就会把子路径中每个部分转换成方法的参数。
    其次,当使用Visual Studio建立ASP.NET MVC项目时,会自动建立Controllers, Models和Views这三个文件夹。推荐在对应的文件夹里创建文件。但是,如果你在开发一个很大型的项目,那么你可以把models分离出来放在一个或多个项目中。但是Controllers 和Views必须放在MVC项目中。对于每一个Controller,在Views下都会有名字与他想对应的文件夹。比如,如果有个名叫HomeController的Controller,那么在Views 文件夹里就必然又一个名叫Home 的文件夹。如果多个Controller需要使用同一个view,那么这个view就必须放在views文件夹的共享目录里。这个共享文件夹里也可以包含共享的用户控件,css文件,javascript文件等等。

    kigg的相关知识和功能

    在动手之前,先让我们探讨一下Digg/DotNetKicks类型程序的一些相关知识。这两个程序都是完全的社区驱动,人们在网上找到他们感兴趣的内容,然后在程序里提交。这些内容会立刻出现在upcoming story队列中。其他用户可以对这些文章投票,一旦投票达到某个数值,他就会出现在首页上。

    程序的主要功能如下:

    • 所有已经发布的Stories列表.
    • 根据Stories的分类进行列表.
    • Upcoming Stories列表.
    • 根据Stories的标签进行列表.
    • 根据Stories的发布用户进行列表.
    • 搜索Stories.
    • 查看Story的详细内容.
    • 允许用户提交新的Story (需要登录)
    • 允许用户对Story 进行Kigg (投票) (需要登录)
    • 允许用户对Story 进行评论(需要登录)
    • 允许用户登录.
    • 允许用户注册
    • 允许用户重设丢失的密码.

    Controllers 和 Actions 的定义

    Kigg的功能是和Story和用户有关的。所以我们可以把所有的功能归为以下两类:

    • StoryController: 处理所有Story的列表搜索提交投票等等。
    • UserController:处理身份验证,注册,忘记密码等等。

    译注:这里的排版稍微变动了一下。把上面一句总述从列表里独立出来了。

    建议使用实际的功能名称给Controller Actions命名。下面这些代码给出了StoryController里所以的行为方法:

    :
       1public class StoryController  
       
    2{  
       
    3.     //List published stories for all category or for a specific category  
       4.     [ControllerAction]  
       
    5.     public void Category(string name, int? page)  
       
    6.     {  
       
    7.     }
      
       
    8.   
       
    9.     //List all upcoming stories regardless the category  
      10.     [ControllerAction]  
      
    11.     public void Upcoming(int? page)  
      
    12.     {  
      
    13.     }
      
      
    14.   
      
    15.     //List Stories for a specific tag  
      16.     [ControllerAction]  
      
    17.     public void Tag(string name, int? page)  
      
    18.     {  
      
    19.     }
      
      
    20.   
      
    21.     //List Stories Posted by a Specific User  
      22.     [ControllerAction]  
      
    23.     public void PostedBy(string name, int? page)  
      
    24.     {  
      
    25.     }
      
      
    26.   
      
    27.     //Search the Stories  
      28.     [ControllerAction]  
      
    29.     public void Search(string q, int? page)  
      
    30.     {  
      
    31.     }
      
      
    32.   
      
    33.     //View the details of a specific story  
      34.     [ControllerAction]  
      
    35.     public void Detail(int id)  
      
    36.     {  
      
    37.     }
      
      
    38.   
      
    39.     //Submit a Story  
      40.     [ControllerAction]  
      
    41.     public void Submit(string storyUrl, string storyTitle, int storyCategoryId,   
      
    42.                        string storyDescription, string storyTags)  
      
    43.     {  
      
    44.     }
      
      
    45.   
      
    46.     //Kigg the Story  
      47.     [ControllerAction]  
      
    48.     public void Kigg(int storyId)  
      
    49.     {  
      
    50.     }
      
      
    51.   
      
    52.     //Post a Comment  
      53.     [ControllerAction]  
      
    54.     public void Comment(int storyId, string commentContent)  
      
    55.     {  
      
    56.     }
      
      
    57. }
      
    下面这些代码片段则展示了UserController里所以的行为方法:
       1public class UserController  
       
    2{  
       
    3.     // Login  
       4.     [ControllerAction]  
       
    5.     public void Login(string userName, string password, bool rememberMe)  
       
    6.     {  
       
    7.     }
      
       
    8.   
       
    9.     //Logout  
      10.     [ControllerAction]  
      
    11.     public void Logout()  
      
    12.     {  
      
    13.     }
      
      
    14.   
      
    15.     // Reset the current password and mail back the new password  
      16.     [ControllerAction]  
      
    17.     public void SendPassword(string email)  
      
    18.     {  
      
    19.     }
      
      
    20.   
      
    21.     //User Registration  
      22.     [ControllerAction]  
      
    23.     public void Signup(string userName, string password, string email)  
      
    24.     {  
      
    25.     }
      
      
    26. }
      

    注意所以的行为方法都定义为public并且使用了ControllerAction 属性。在下一个版本的ASP.NET MVC中这个属性就不再需要了,所有定义为公共的方法将自动成为一个行为方法。

    定义筛选规则

    一旦Controllers的参数签名确定就应该立刻开始声明把URLs映射到Controllers的行为方法的筛选规则。前面我提到过,这些映射规则在web.config文件中的application start事件中定义。定义筛选规则的时候你要注意,把最特殊的规则放在最上面。这个就跟try/catch块中定义错误处理规则一样,要遵循从特殊到一般的原则。如果你打开Global.asax 文件查看,你会发现我们明确的定义了两个方法来定义这些规则,并且在application start事件中调用方法。这么做是因为我们不想把不同版本iis中使用的规则弄乱。在iis7中有一个很cool的特性,所有的URLs都不需要扩展名,但是在老版本的iis中URLs都需要一个.mvc的扩展名。所以,为了同事支持这个两个版本的iis,我们就要把同一个URL定义两遍,为新的iis定义一套没扩展名的,为旧的iis定义一套有扩展名的。在这里,我们在web.confitg文件里设置当前程序是跑在什么版本的iis中,然后只把当前版本iis用的那套筛选规则读出来。这样做还有一个好处,那就是我们一会要讲的单元测试。下面这些代码展现了筛选规则是如何实现的:

     

       1protected void Application_Start(object sender, EventArgs e)  
       
    2{  
       
    3.     RegisterRoutes(RouteTable.Routes);  
       
    4. }
    public static void RegisterRoutes(RouteCollection routes)  
       
    5{  
       
    6.     int iisVersion = Convert.ToInt32(ConfigurationManager.AppSettings["IISVersion"]);  
       
    7.   
       
    8.     if (iisVersion >= 7)  
       
    9.     {  
      
    10.         RegisterRoutesForNewIIS(routes);  
      
    11.     }
      
      
    12.     else  
      
    13.     {  
      
    14.         RegisterRoutesForOldIIS(routes);  
      
    15.     }
      
      
    16. }
      
      
    17private static void RegisterRoutesForNewIIS(ICollection<Route> routes)  
      
    18{  
      
    19.     var defaults = new  
      
    20.     {  
      
    21.         controller = "Story",  
      
    22.         action = "Category",  
      
    23.         name = (string)null,  
      
    24.         page = (int?)null  
      
    25.     }
    ;  
      
    26.   
      
    27.     routes.Add(  
      
    28.                     new Route  
      
    29.                     {  
      
    30.                         Url = "User/Login",  
      
    31.                         RouteHandler = typeof(MvcRouteHandler),  
      
    32.                         Defaults = new  
      
    33.                         {  
      
    34.                             controller = "User",  
      
    35.                             action = "Login"  
      
    36.                         }
      
      
    37.                     }
      
      
    38.                 );  
      
    39.   
      
    40.     routes.Add(  
      
    41.                     new Route  
      
    42.                     {  
      
    43.                         Url = "User/Logout",  
      
    44.                         RouteHandler = typeof(MvcRouteHandler),  
      
    45.                         Defaults = new  
      
    46.                         {  
      
    47.                             controller = "User",  
      
    48.                             action = "Logout"  
      
    49.                         }
      
      
    50.                     }
      
      
    51.                 );  
      
    52.   
      
    53.     routes.Add(  
      
    54.                     new Route  
      
    55.                     {  
      
    56.                         Url = "User/Signup",  
      
    57.                         RouteHandler = typeof(MvcRouteHandler),  
      
    58.                         Defaults = new  
      
    59.                         {  
      
    60.                             controller = "User",  
      
    61.                             action = "Signup"  
      
    62.                         }
      
      
    63.                     }
      
      
    64.                 );  
      
    65.   
      
    66.     routes.Add(  
      
    67.                     new Route  
      
    68.                     {  
      
    69.                         Url = "User/SendPassword",  
      
    70.                         RouteHandler = typeof(MvcRouteHandler),  
      
    71.                         Defaults = new  
      
    72.                         {  
      
    73.                             controller = "User",  
      
    74.                             action = "SendPassword"  
      
    75.                         }
      
      
    76.                     }
      
      
    77.                 );  
      
    78.   
      
    79.     routes.Add(  
      
    80.                     new Route  
      
    81.                     {  
      
    82.                         Url = "Story/Detail/[id]",  
      
    83.                         RouteHandler = typeof(MvcRouteHandler),  
      
    84.                         Defaults = new  
      
    85.                         {  
      
    86.                             controller = "Story",  
      
    87.                             action = "Detail"  
      
    88.                         }
     &nb