网站迁移

将 Web 应用程序从 WebMatrix 迁移到 ASP.NET MVC 3

Brandon Satrom

下载代码示例

今年 1 月份,Microsoft 针对使用 Microsoft .NET Framework 进行的 Web 开发推出了一种新的编程模型,叫做 ASP.NET Web Pages。WebMatrix (web.ms/WebMatrix) 目前默认支持 Web Pages。这是一种以页面为中心的编程模型,其行为方式与 PHP 非常相似:每个页面都包含自己的业务逻辑、数据访问和动态内容,以将 HTML 呈现到浏览器当中。

我们会出于多种原因而选择在 WebMatrix 中构建网站。但如果您知道在将来的某个时刻需要迁移到 Visual Studio,会怎么样呢?如果 ASP.NET MVC 3 是您的最终选择,您需要在迁移时重新开发站点吗?如果您害怕 WebMatrix 和 Web Pages 会为自己设置许多障碍,那么不用担心。

作为核心 ASP.NET 框架的一部分,Web Pages 已经充分考虑了灵活性。尽管 Web Pages 没有任何技术限制会强迫您迁移到 ASP.NET MVC,但您的团队、产品或公司还是有可能需要这么做。

在本文中,我们将讨论您应该选择迁移的一些原因(以及不选择迁移的一些原因)。我们还会讨论将 Web Pages 站点迁移到 ASP.NET MVC(如果您选择这么做)的策略。我们将讨论如何从页面迁移到视图、如何处理业务逻辑和帮助程序代码,以及如何在应用程序中引入模型。最后,我们将讨论和演示如何通过路由,以及在必要时支持永久重定向来保留现有的站点 URL。

何时迁移?

在我们探讨从 Web Pages 迁移到 ASP.NET MVC 的原因之前,让我们先讨论不进行迁移的几个原因。对于初学者,不应当因为担心应用程序无法扩展而将站点从 Web Pages 迁移到 ASP.NET MVC。因为 Web Pages 是构建在 ASP.NET 之上的,针对 ASP.NET MVC 或 Web 窗体编写的应用程序具备的很多性能特点,它也能提供。很明显,每种应用程序的执行模型都略有不同,但没有任何因素导致 ASP.NET MVC 在本质上比 Web Pages 应用程序的扩展性更高或更低。规模和性能与您在构建站点时制定的设计决策有关,因为这是由您选择的基础框架决定的。

如果您想要将站点从 Web Pages 迁移到 ASP.NET MVC,仅仅是因为这很酷、很有趣,听说每个人都在这么做,或者是因为您需要在 Visual Studio 中处理站点,那么这可不是一个好主意。而且对于后一种情况,其实您已经可以使用 Visual Studio 处理 Web Pages 站点。ASP.NET MVC 只是 Web 应用程序体系结构中的一种选择,并不具备让站点立刻就变好的魔力。从 Web Pages 迁移到 ASP.NET MVC 只是一种选择,不是必须的。尽管 Microsoft 已经做了极其出色的工作,让这种迁移顺利完成,但您还是需要投入时间、资源和资金,就像所有其他迁移活动一样。由于上述原因,请确保您是基于合理的原因而迁移到 ASP.NET MVC,这一点很重要。

合理的原因可能包括:单元测试对于您和您的团队很重要。由于 Web Pages 模型是以页面为中心的,因此无法对 Web Pages 站点使用现有的单元测试工具。使用 WatiN 或 Selenium 等工具进行 Web UI 测试还是有可能的,但无法使用 NUnit 或 MsTest 等工具进行代码级的单元测试。如果您的站点日趋复杂,单元测试对于您来说非常重要,则可以迁移到 ASP.NET MVC。

如果您喜欢对代码进行单元测试,可能也会喜欢在应用程序中采取一些关注点分离措施。尽管可以在 Web Pages 中使用帮助程序和代码文件创建清晰、分离的代码,但这种模型并不像 ASP.NET MVC 那样适合这种分离。如果关注点分离对您很重要,并且您希望应用程序体系结构支持这种分离,迁移是合适的选择。

除了这两个原因,也可能有其他原因导致您需要迁移,具体取决于站点或组织的具体情况。如果您的团队处于发展阶段,站点的复杂性日趋增加,需要更丰富的业务功能,迁移则是明智的选择。如果为了更好地利用更丰富的开发生态系统以进行源代码控制、负载测试等工作,迁移也可能是必要的。

为迁移做准备

在本文中,我们将以 WebMatrix 附带的 Photo Gallery 为模板站点,将其迁移到 ASP.NET MVC。迁移过程的核心是从“页面”迁移到“模型”、“控制器”和“视图”,但在此之前,我们需要进行一些准备工作。

由于我们要在本文有限的篇幅内详细介绍手动迁移中涉及的步骤,因此无法详细叙述每个步骤的所有内容。我们的目标是介绍主要步骤,简要涉及次要问题。我们还选择忽略与整个转换过程的核心部分不相关的内容,例如数据访问的最佳实践、潜在的程序集结构、依赖关系注入等等。这些内容很重要,但很多都可以归结为开发文化和个人偏好,可以在迁移后的重构中解决。

特别要注意的是,在本文中,我们不会在 WebMatrix 中使用 Visual Studio 的“打开”功能,该功能会将您的当前站点以“网站”项目的形式在 Visual Studio 打开。即使您选择不迁移到 ASP.NET MVC,也可以使用此选项,但我们更倾向于使用针对 ASP.NET MVC 的 Visual Studio“Web 应用程序”项目类型,即先创建一个空站点,然后手动迁移内容。

因此,在迁移的一开始,我们选择“文件”|“新建”|“项目”,然后选择 ASP.NET MVC 3 Web 应用程序和空模板,并使用 Razor 作为默认的视图引擎。

设置目标应用程序后,您需要执行一些初始的迁移工作。以下是这些初始步骤的简要介绍:

  1. 通过 NuGet (nuget.org) 将您在 Web Pages 站点中使用的所有程序包添加到 ASP.NET MVC 站点中。
  2. 添加对 System.Web.Helpers、WebMatrix.Data 和 WebMatrix.WebData 的引用。在“属性”窗格中将每个属性都设置为 Copy Local = true。
  3. 将 _AppStart.cshtml 的内容移动到 Global.asax 的 Application_Start 方法中。尽管可以移动 _AppStart 并按原样使用,我们还是建议您通过现有的 ASP.NET MVC 启动代码将其逻辑集中到 Global.asax 中。
  4. 将 <roleManager enabled=true /> 添加到根 web.config 的 <system.web> 部分。Photo Gallery 应用程序使用 WebMatrix.WebData 中的新 WebSecurity 成员资格提供程序,因此我们需要在配置中添加该项,以便让网站正常工作。
  5. 移动应用程序的 Content 或 Scripts 文件夹下的所有样式表、脚本文件和图像。将这些文件中的所有资源链接都更新为相应的新路径。
  6. 修改 Global.asax 中的默认路由,使其指向 Gallery 控制器和 Default 操作。
  7. 将 App_Data 文件夹中的 SQL Compact 数据库复制到站点的 App_Data 文件夹中。如果您的站点使用其他数据库,请将该连接字符串添加到应用程序的 Web.Config 文件中。

从“页面”迁移到“视图”

完成 ASP.NET MVC 站点的初始设置后,就可以开始迁移现有站点的核心部分了:页面。在 Web Pages 中,页面 (.[cs/vb]html) 包含标记、业务逻辑和该页面所需的数据访问。在迁移到 ASP.NET MVC 的过程中,您要做的主要工作是将每个页面打散,然后将页面内容分散到控制器操作(业务逻辑)、数据访问类(数据访问)和视图(标记)当中。

首先,您需要迁移站点的布局。与 Web 窗体和 ASP.NET MVC 中的母版页类似,“布局”页面是用来指定站点布局结构的文件。Web Pages 和 ASP.NET MVC 3(与 Razor 视图引擎结合使用时)使用相同的“布局”子系统,因此这部分迁移工作应当很简单。在 Photo Gallery 站点中,根 _SiteLayout.cshtml 文件包含站点的结构。复制该文件的内容,然后导航到您的 ASP.NET MVC 站点。打开位于 Views/Shared/_Layout.cshtml 的布局文件,然后将 _SiteLayout.cshtml 的内容粘贴进去。

完成后,您需要对 _Layout.cshtml 稍作修改。首先,将指向样式表的链接更改为指向 ASP.NET MVC 应用程序中的新位置(~/Content/Site.css,而不是 ~/Styles/Site.css)。接下来,需要将 @Page.Title 更改为 @ViewBag.Title。这两个都是动态类型的对象,能够包含站点中页面的显示数据或其他数据,并且您可能已猜到,Page 是用于 Web Pages 的,ViewBag 是用于 ASP.NET MVC 的。

您需要在 _Layout.cshtml 中进行的最后一项更改适用于迁移到 ASP.NET MVC 的所有页面,因此请铭记在心。请注意,_Layout.cshtml 使用 @Href 调用将 URL 插入页面。对于引用静态内容(脚本、CSS 等)的调用,这些内容可以保持不变。但是,您会希望更改所有指向站点页面的 @Href 调用。尽管它们在迁移前后的工作方式一样,它们还是指向静态 URL。在 ASP.NET MVC 中,使用 ASP.NET 路由创建呈现视图时显示的 URL 被认为是一种更好的做法。这样做可以将更简洁可靠的链接绑定到您的路由表定义中,而不是在站点中硬编码。

因此,您将需要更改像下面这样的链接:

<div id="banner">
  <p class="site-title">
    <a href="@Href("~/")">Photo Gallery</a>
  </p>
...
</div>

改为使用 @Url.RouteUrl@Url.Action:

<div id="banner">
  <p class="site-title">
    <a href="@Url.Action("Default", "Gallery")">Photo Gallery</a>
  </p>
...
</div>

迁移完站点布局后,您可以开始将页面迁移到视图。 在 Web Pages 应用程序中,如果有任何 .cshtml 页面被 RenderPage 调用执行,请将这些页面移动到 Views/Shared 之下(站点级页面)或适当的视图子文件夹中(由 Account 等控制器共享的页面)。 每个调用其中一个部分页面的页面都需要进行更新以反映新的位置。

所有剩余页面都应移动到 Views 之下,按控制器整理到不同的文件夹中。 因为 Web Pages 站点没有控制器这样的概念,所以需要在迁移过程中引入控制器。 好消息是,Photo Gallery 应用程序中存在明显的控制器结构,为您如何处理自己的站点起到很好的示范作用。

例如,Photo Gallery 模板站点使用以下文件夹来页面分组:Account、Gallery、Photo、Tag 和 User。 每个文件夹都包含相应的页面,这些页面实现与该分组相关的一些功能。 例如,Account 文件夹包含用于在站点上登录和注销以及注册用户的页面。 Gallery 文件夹包含一个库列表页面、一个用于添加新库的页面,以及一个用于查看库中照片的页面。 其余的文件夹也是这样的组织结构。 尽管 Web Pages 站点中并不需要这样的结构,但拥有这种结构的确能够简化向 ASP.NET MVC 的迁移过程。 因为在这种情况下,每个文件夹都恰好对应一个控制器,每个 .cshtml 文件都对应一个操作和视图。

让我们先将 Account 文件夹和它的三个页面(Login、Logout 和 Register)移动到 ASP.NET MVC 应用程序的 Views 目录下。 在 ASP.NET MVC 中,页面会立即变成视图,这是由它们在应用程序中的位置所决定的。 但这还没有结束,因为您的应用程序需要一个控制器和操作,以便在收到请求时将这些视图提交给用户。

控制器简介

按照 MVC 的惯例,如果您的视图下有一个 Account 文件夹,则意味着您应当有一个名为 AccountController 的控制器,因此接下来就是在 Controllers 文件夹下创建该控制器。 只需右键单击并选择“添加”|“控制器”。 我们可以通过这个空的控制器创建相应的操作方法,其中包含我们迁移到应用程序中的每个 .cshtml 页面顶部包含的逻辑。

我们首先处理 Login.cshtml,其中包含图 1 中所示的代码。

图 1 Login.cshtml 中包含的业务逻辑

Page.Title = "Login";
if (IsPost) {
  var email = Request["email"];
  if (email.IsEmpty()) {
    ModelState.AddError(
      "email", "You must specify an email address.");
  }
  var password = Request["password"];
  if (password.IsEmpty()) {
    ModelState.AddError(
      "password", "You must specify a password.");
  }

  if (ModelState.IsValid) {
    var rememberMe = Request["rememberMe"].AsBool();
    if (WebSecurity.Login(email, password, rememberMe)) { 
      string returnUrl = Request["returnUrl"];        
      if (!returnUrl.IsEmpty()) {
        Context.RedirectLocal(returnUrl);
      } else{
        Response.Redirect("~/");
      }
    } else {
      ModelState.AddFormError(
        "The email or password provided is incorrect.");
    }
  }
}

请注意,此处要处理两种情况。 第一种情况是当用户第一次加载登录页面时。 此时,“页面”设置其标题并直接转换为标记。 第二种情况包含在 IsPost 条件中,表示在用户完成登录表单并单击“登录”按钮时执行的逻辑。

在 ASP.NET MVC 中,我们通过在控制器中创建两个操作方法来处理提交空窗体和接受窗体提交。这两个操作方法分别用于空窗体和处理提交。 第一个操作将设置页面标题并返回登录视图,而第二个操作则包含 IsPost 条件内的逻辑。 图 2 中介绍了这些操作。 添加这两个操作后,从 Login.cshtml 中删除标头代码。

图 2 登录控制器操作

public ActionResult Login() {
  ViewBag.Title = "Login";
  return View();
}

[HttpPost]
public ActionResult Login(string email, string password, 
  bool?
rememberMe, string returnUrl) {
  if (email.IsEmpty())
    ModelState.AddModelError("email", 
      "You must specify an email address.");
  if (password.IsEmpty())
    ModelState.AddModelError("password", 
      "You must specify a password.");
  if (!ModelState.IsValid)
    return View();
  if (WebSecurity.Login(email, password, 
    rememberMe.HasValue ?
rememberMe.Value : false)) {
    if (!string.IsNullOrEmpty(returnUrl))
      return Redirect(returnUrl);
    return RedirectToAction("Default", "Gallery");
  }

  ModelState.AddModelError("_FORM", 
    "The email or password provided is incorrect");
  return View();
}

在原始页面和生成的操作方法之间,有几个主要的不同之处需要注意。 对于初学者来说,您会注意到 IsPost 条件不是必要的。 在 ASP.NET MVC 中,我们创建另一个 Login 操作方法并使用 [HttpPost] 属性来修饰该方法,以此为登录页面创建 Post 操作。 第一个 Login 方法现在仅用于设置 ViewBag.Title 属性并返回一个 ViewResult,此 ViewResult 随即在 Views/Account 中查找名为 Login.cshtml 的视图页面。

您可能已经注意到,第二个不同之处是:Post 操作包含几个参数,并且原始页面使用的 Request 调用都没了。 通过在方法中使用与“登录”窗体中的字段名称对应的参数(email、password 和 rememberMe),我们可以使用 ASP.NET MVC 的默认模型绑定器将这些项以参数的形式传递给操作,从而避免亲自调用 Request 对象,并使我们的操作逻辑更简洁。

最后,Web Pages 应用程序和 ASP.NET MVC 应用程序在处理验证和执行重定向方面有一些细微的差别。 在 Web Pages 站点中,ModelState.AddError 和 .AddFormError 是在遇到无效窗体数据时,用来通知页面的调用。 在 ASP.NET MVC 应用程序中,我们使用 ModelState.AddModelError,这个差别不大,但却是所有页面都需要的更改。 对于重新定向,Web Pages 站点在重新路由用户时调用 Response.Redirect。 在 ASP.NET MVC 中,由于控制器操作应当返回一个 ActionResult,因此我们调用 return RedirectToRoute(“Default”),它可以生成相同的结果。

迁移登录页面之后,我们还可以迅速处理 Logout.cshtml。 在 Web Pages 中,有些页面的作用是执行一个操作然后重定向用户(如 Logout.cshtml),因此可能只包含逻辑而不包含标记:

@{
  WebSecurity.Logout();
  Response.Redirect("~/");
}

在 ASP.NET MVC 中,我们将添加一个 Logout 操作,用于执行这项任务:

public ActionResult Logout() {
  WebSecurity.Logout();
  return RedirectToAction("Default", "Gallery");
}

由于视图只表示页面的可见元素而不具备任何功能,而且我们创建了一个操作来解决用户的注销和重定向工作,因此可以从应用程序中删除 Logout.cshtml 视图。

到目前为止,我们已将 Account 页面复制到 Views/Account 文件夹从而将这些页面转换为视图,创建了 AccountController 来处理发送给 Account 页面的请求,并实现了一些操作方法来处理登录和注销活动。 现在,您可以构建和运行站点,并将 Account/Login 附加到浏览器的地址栏中(请注意,默认的主页指向 Gallery/Default,而我们还未实现,因此不会显示)。

目前您需要解决的其他网站功能是包含在 Web Pages 站点的 App_Code 目录中的代码和帮助程序。 在迁移开始的时候,您可以将这个目录整个移动到 ASP.NET MVC 应用程序中并包含在项目中。 如果该目录包含任何代码文件(.cs 或 .vb),您可以将它们保存在 App_Code 中或移到其他地方。 不论怎么做,您都需要将每个文件的 Build Action 属性更改为 Compile 而不是 Content。 如果该目录包含带有 @helper 方法声明的 .cshtml 文件,您可以保留这些文件并在 ASP.NET MVC 应用程序中按原样使用它们。

对于 Web Pages 站点的其余部分,您将遵循相似的过程:为每个视图文件夹创建控制器,为每个页面创建操作,将每个页面的标头代码移动到一个或多个操作中。 您很快就可以将所有页面清楚地分离到各个控制器操作和视图中。 但是,MVC 模式中还有一部分尚未在本文中提及:模型。

将数据访问迁移到 Repository 类

将业务逻辑从每个页面取出并移到一个或多个控制器操作中的过程相当简单,只有一点例外:数据访问。 尽管某些页面可能与登录和注销页面类似,只包含一些逻辑而没有数据访问,但 Web Pages 站点的大部分页面都可能要使用数据库。

Account/Register.cshtml 页面就是一个例子。 当用户完成注册表单然后单击“注册”时,该页面将进行两次数据库调用,如图 3 所示。

图 3 Register.cshtml 数据库逻辑

var db = Database.Open("PhotoGallery");
      
var user = db.QuerySingle("SELECT Email FROM 
UserProfiles WHERE LOWER(Email) = LOWER(@0)", email);
       
if (user == null) {      
  db.Execute(
    "INSERT INTO UserProfiles (Email, DisplayName, Bio) 
    VALUES (@0, @1, @2)", email, email, "");

  try {
    WebSecurity.CreateAccount(email, password);
    WebSecurity.Login(email, password);
    Response.Redirect("~/");
  } catch (System.Web.Security.MembershipCreateUserException e) {
    ModelState.AddFormError(e.ToString());
  }
} else {
  ModelState.AddFormError("Email address is already in use.");
}

首先,注册页面打开 PhotoGallery 数据库并返回一个代表该数据库的 WebMatrix.Data.Database 对象。 然后页面使用该对象查找与用户提供的值相符的现有电子邮件地址。 如果该地址不存在,将会创建一个新的 UserProfile 记录,并使用 WebSecurity 成员资格提供程序为用户创建一个帐户。

只要我们添加了对 WebMatrix.Data 的引用并将 Copy Local 属性设置为 true,我们就能在不做任何更改的情况下使用此数据库逻辑,而站点也可以正常工作。 在迁移过程中,这可能是您为了保持站点正常运行而希望采取的战术步骤。

但是在本文中,我们将更进一步,创建包含数据访问的其他对象,就像我们从头开发 ASP.NET MVC 应用程序时所做的一样。 有许多模式可供您使用,用来分离控制器和数据访问逻辑。 我们将对 Photo Gallery 使用 Repository 模式。通过将数据访问提取到存储库类,我们可以封装此逻辑,并且尽量不影响我们以后的选择:是应该添加正式的模型对象还是应该添加对象关系映射 (ORM) 系统(例如 Entity Framework)。

首先,我们在应用程序中创建一个 Repositories 文件夹,以及一个简单的类 AccountRepository.cs。 然后,我们可以在 Register 操作中单步执行每个数据库调用,并将该逻辑移到存储库中,如图 4 所示。

图 4 AccountRepository

public class AccountRepository {
  readonly Database _database;
  public AccountRepository() {
    database = Database.Open("PhotoGallery");
  }

  public dynamic GetAccountEmail(string email) { 
    return _database.QuerySingle(
      "SELECT Email FROM UserProfiles 
      WHERE LOWER(Email) = LOWER(@0)", email);
  }
 
  public void CreateAccount(string email) {
    _database.Execute(
      "INSERT INTO UserProfiles 
      (Email, DisplayName, Bio) VALUES (@0, @1, @2)", 
      email, email, "");
  }
}

我们把对 Database.Open 的调用添加到存储库的构造函数中,并创建了两个方法,一个用于查找帐户电子邮件,另一个用于创建帐户。

请注意,GetAccountEmail 的返回类型是动态的。 在 WebMatrix.Data 中,许多查询方法都返回动态结果或 IEnumerable<dynamic>,既然这种做法可行,就没有理由不在您的存储库中继续沿用。

图 5 中介绍了新的 Register 方法(使用 AccountRespository)。

图 5 使用 AccountRepository 的 Register 操作

[HttpPost]
public ActionResult Register(string email, string password, 
  string confirmPassword) {

  // Check Parameters (omitted)

  if (!ModelState.IsValid)
    return View();
 
  var db = new AccountRepository();
  var user = db.GetAccountEmail(email);
 
  if (user == null) {
    db.CreateAccount(email);
 
    try {
      WebSecurity.CreateAccount(email, password);
      WebSecurity.Login(email, password);
      return RedirectToAction("Default", "Gallery");
    }
    catch (System.Web.Security.MembershipCreateUserException e) {
      ModelState.AddModelError("_FORM", e.ToString());
    }
  }
  else {
    ModelState.AddModelError("_FORM", 
      "Email address is already in use.");
  }
 
  return View();
}

使用动态返回类型是完全可以接受的,并且在迁移过程中可能尤为适用,这是因为您将站点作为一个功能齐全的 ASP.NET MVC 应用程序建立并运行。 您无需在 ASP.NET MVC 应用程序中使用强类型化的模型,因此只要您不需要代码驱动的数据模型定义,就可以利用这种策略。 动态模型 ASP.NET MVC 应用程序的控制器逻辑和视图将正常运行,有一点除外。

您可能已经注意到,在 Web Pages 应用程序中,窗体字段是使用标准标记显式定义的:

<input type="text" />
<input type="submit" />
...

在 ASP.NET MVC 中,使用窗体控件的首选方式是使用 Html.TextBox 或 Html.TextBoxFor 等 Html 帮助程序方法,因为这些方法使用传递到视图中的模型来设置当前值并处理窗体验证。 如果您希望在视图迁移后使用这些帮助程序方法,就必须引入强类型化的模型对象,并停止在存储库中使用动态类型,因为这些帮助程序方法无法与动态模型配合使用。

保留站点 URL

站点的 URL 很重要。 无论站点的状态如何,许多外部资源都依赖您的现有 URL,包括搜索引擎、文档、通信、测试脚本等等。 因为这种依赖关系,您不应任意更改 URL,即使为了迁移也不行。

可以考虑使用 ASP.NET 路由来确保现有的 URL 得到保留。 ASP.NET 路由可以协助请求与正确的资源相匹配,在我们的示例就是控制器和操作。 Web Pages 使用的路由系统不同于 ASP.NET MVC,因此您需要花一些时间来确保现有的 URL 得到保留。

Web Pages 应用程序能够处理带扩展名和不带扩展名的 URL。 例如,以下两个 URL 指向同一个页面:

http://mysite/Gallery/Default.cshtml

http://mysite/Gallery/Default

但 ASP.NET MVC 应用程序不会处理使用 .cshtml 扩展名的第一个 URL。 在整个站点中使用不带扩展名的 URL 可以确保搜索引擎和其他的从属站点都不使用带扩展名的 URL,从而将迁移对站点造成的影响降至最低。 但是,如果您的确需要处理带扩展名的现有 URL,可以在 ASP.NET MVC 应用程序中创建路由以确保这些 URL 不会失效。

以 Photo Gallery 应用程序的默认路由为例:

routes.MapRoute(
  "Default", 
  "{controller}/{action}/{id}", 
  new { controller = "Home", 
    action = "Index", id = "" } 
);

要支持系统中的原有 URL,我们需要在 Global.asax 文件中此定义上方的路由表中添加额外的路由。 下面是这种定义的示例:

routes.MapRoute(
  "LegacyUrl",
  "{controller}/{action}.cshtml/{id}",
  new { controller = "Gallery", 
    action = "Default", id = "" }
);

在这个路由条目中,包含 .cshtml 扩展名的 URL 被处理并发送到适当的控制器和操作中,前提是您现有的 Web Pages 站点结构已明确映射到一个控制器/操作结构。

在规划迁移时,请切记您的应用程序可能需要更改默认的路由,甚至是添加额外的路由以支持现有 URL。但是,如果您决定破坏现有的 URL,请务必加入相应操作,以便为用户实现永久重定向。

总结

为了强调从 Web Pages 迁移到 ASP.NET MVC 可能造成的影响,让我们看看 Photo Gallery 站点在迁移前后的结构。在图 6 中,左边是 WebMatrix 中的 Web Pages 站点结构。右边是完全迁移到 ASP.NET MVC 之后的站点。请注意,尽管结构有所变化,您还是会觉得最终结果并不陌生。

图 6 迁移前后的应用程序布局

今天,ASP.NET 开发人员有三种框架可供选择:Web 窗体、Web Pages 和 ASP.NET MVC。每种框架各有优势,而选择一种框架并不妨碍您利用其他框架,甚至在将来的某个时候彻底迁移。此外,所有这三种框架都是基于 ASP.NET 构建的,因此不应因为技术原因而从一种框架迁移到另一种框架。如果您选择进行迁移,那么 Web Pages 和 ASP.NET MVC 的相似性可让您继续使用 NuGet、Razor、Web Deploy、IIS Express 和 SQL Compact 等技术,而无需进行修改。

如果您使用 Web Pages 构建应用程序并决定进行迁移,那么从 Web Pages 迁移到 ASP.NET MVC 是最方便的。如果您在设计 Web Pages 站点时提前做好准备,例如按照功能将页面分组到不同的文件夹中、为所有资源使用相对 URL 并将所有业务逻辑放在每个页面的顶部,则会更容易地完成迁移。在真正开始迁移时,您会发现正如预期的那样,从 Web Pages 迁移到 ASP.NET MVC 非常顺利简单。

您可以在 bit.ly/WebMatrixToMVC 找到本文中使用的许多技术和技巧的链接,以及许多其他资源。

Brandon Satrom 是 Microsoft 在德克萨斯州奥斯汀市以外的高级开发推广人员。他的博客地址为 userInexperience.com,播客地址为 DeveloperSmackdown.com,还可以通过 Twitter 地址 twitter.com/BrandonSatrom 关注他。

Clark Sell 是 Microsoft 在芝加哥之外的资深开发推广人员。他的博客地址为 csell.net,播客地址为 DeveloperSmackdown.com,还可以通过 Twitter 地址 twitter.com/csell5 关注他。

衷心感谢以下技术专家对本文的审阅:Phil HaackErik Porter