RESTful XHTML

使用 ASP.NET MVC 的 RESTful 服务

Aaron Skonnard

MSDN 代码库 中可用的代码下载
浏览在线代码

鏈  枃璁 ㄨ :

  • 其余部分
  • XHTML
  • ASP.NET mvc
本文使用以下技术:
其余部分,XHTML,ASP.NET

内容

XHTML: 表示数据和链接
XHTML: 表示输入的窗体
了解 ASP.NET MVC 体系结构
实现模型
实现控制器
路由设计 URI
实现视图
使用书签服务
确认

可以浏览 A RESTful 服务是一个站点 资源的程序。当设计 RESTful 服务,您必须仔细考虑您的站点将如何工作。这意味着资源的链接有助于导航,描述服务由于某种原因,并考虑如何使用者将浏览您的服务在运行时输入的表示形式的设计。获取这些操作的权限经常被忽视,但它们实现 REST 已提供完全可能的中心。

目前,人们导航使用 Web 浏览器知道如何呈现 HTML 和其他常见的内容类型的网站。HTML 提供建立链接资源 (<a> 元素) 之间,用于描述和提交应用程序输入的语法和语义 (<form> 和 <input> 元素)。

用户单击时的呈现的页中 <a> 元素,浏览器知道问题目标资源的 HTTP GET 请求,并显示响应。浏览器在遇到一个 <form> 元素时它知道如何呈现到一个用户界面,用户可以填写并提交使用一个 GET 或 POST 请求中的窗体说明。当用户按下一个提交按钮时,浏览器对数据进行编码,并发送使用指定的请求。这两种功能负责很大程度上取得成功的网站。

使用通用的 HTTP 界面与中的链接可以随着时间的推移将请求重定向到新位置并更改而不更改客户端代码的安全动态的某些方面。一种标准方法为窗体意味着您可以添加或删除输入的属性并再次更改默认的值,而不更改客户端代码。这两种功能可非常用于构建应用程序随着时间的推移不断发展的。

RESTful 服务由于某种原因还应该提供您决定使用任何资源表示通过这些两个功能。渚嬪的方式  如果您的服务设计自定义的 XML 方言您应可能提出您自己的元素建立链接,描述将指导您的网站通过使用者的服务输入。也可以只使用 XHTML。

大多数开发人员不立即视为 XHTML 的"服务,"选项,但这实际上它用于的方法之一。XHTML 文档是定义的标准格式的 XML 允许使用标准 XML API 自动处理。和 XHTML 也是 HTML,因为它附带了 <a> <form>,<input> 的建模元素链接导航和服务的输入,如上文所述。第一次唯一在有点奇怪的是用户定义的数据结构的模型,但是,您可以用类和字段 <div> 模型并 <span> 元素和 <ol> 实体和 <li> 元素的集合。我将介绍如何执行此操作以后在相应的文章中详细。

汇总,还有几个原因 XHTML 视为默认表示形式为 RESTful 服务。第一次,您可以利用语法并语义的重要元素 <a> <form>,而不是您自己 inventing <input> 和。第二个,您将得到与感觉很多网站因为它们将是可浏览用户和应用程序的服务。由一个人仍解释该 XHTML — 只是程序员的而不是在运行时用户的开发过程中。这简化了在整个开发过程的内容,并使它更容易了解您的服务的工作原理的使用者。最后,您可以利用标准的 Web 开发框架构建 RESTful 服务。

为生成基于 XHTML 的服务提供一个本身 RESTful 模型的一类框架 ASP.NET MVC。本文遍历一些 XHTML 设计概念,然后显示了如何生成的一个完整 XHTML 基于 RESTful 服务,您可以从 MSDN Magazine 网站下载。

XHTML: 表示数据和链接

深入 ASP.NET MVC 的详细信息之前,第一次看如何可以表示通用数据结构中和集合 XHTML。此方法不是唯一的方法来完成此,但相当通常基于 XHTML 的服务在今天。

本文,我将说明如何实现一个简单的书签服务。该服务允许用户创建、 检索、 更新,和删除书签和导航站点以各种不同的方式中的书签。假设您有一个 C# 类表示一个书签,如下所示:

public class Bookmark
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Url { get; set; }
    public string User { get; set; }
}

第一个问题是如何可以表示 XHTML 中的一个书签实例? 一种方法是将合并 <div>,<span>,和 <a> 元素位置 <div> 元素表示结构、 <span> 元素表示字段,和 <a> 元素表示的标识和链接到其他资源。 鍙 ﹀ 的方式  可做批注这些元素与 XHTML"类"属性来提供其他类型的元数据。 下面是一个完整的示例:

<div class="bookmark">
  <span class="bookmark-id">25</span>
  <span class="bookmark-title">Aaron's Blog</span>
  <a class="bookmark-url-link" href="http://pluralsight.com/aaron" 
  >http://pluralsight.com/aaron</a>
  <span class="bookmark-username">skonnard</span>
</div>

下一个问题是使用者将如何处理该信息吗? 因为它是格式良好的 XML 的使用者可以使用任何 XML API 提取书签的信息。 大多数.NET 程序员可能会发现 XLinq 提供了最自然的编程模型以编程方式使用 XHTML。 鍙 ﹀ 的方式  可以直接定位一个进一步增强的编程模型更加轻松一些有用的专门针对 XHTML 的扩展方法的 XLinq。

本文,我将使用一组 XLinq 我已在可下载的示例代码中包括的扩展方法。 这些扩展为您提供的内容可能的一个好主意。 下面的代码演示如何使用书签 XHTML 显示以前使用 XLinq 以及这些扩展:

var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");
Console.WriteLine(bookmarkDetails["bookmark-id"].Value);
Console.WriteLine(bookmarkDetails["bookmark-url"].Value);
Console.WriteLine(bookmarkDetails["bookmark-title"].Value);

现在,如果您希望提高该书签在浏览器中的呈现方式,您可以添加一个级联样式表 (CSS) 来控制特定于浏览器的呈现的详细信息,或添加某些其他 UI 元素和文本不会危及安全提取感兴趣的数据使用者的能力。 渚嬪的方式  以下的 XHTML 会更容易为人们处理,,但您仍可使用上面的.NET 代码示例以处理信息而无需任何进行修改:

<h1>Bookmark Details: 3</h1>
<div class="bookmark">
  BookmarkID: <span class="bookmark-id">25</span><br />
  Title: <span class="bookmark-title">Aaron's Blog</span><br />
  Url: <a class="bookmark-url-link" href="http://pluralsight.com/aaron" 
  >http://pluralsight.com/aaron</a><br />
  Username: <span class="bookmark-username">skonnard</span></a><br />
</div>

资源的集合并不难模型。 您可以对书签的 <ol>,组合的列表来表示 <li>,和 <a> 元素,如下所示:

<ol class="bookmark-list">
  <li><a class="bookmark-link" href="/bookmarks/1">Pluralsight Home</    a></li>
  <li><a class="bookmark-link" href="/bookmarks/2">Pluralsight On-    Demand!</a></li>
  <li><a class="bookmark-link" href="/bookmarks/3">Aaron's Blog</a></li>
  <li><a class="bookmark-link" href="/bookmarks/4">Fritz's Blog</a></li>
  <li><a class="bookmark-link" href="/bookmarks/5">Keith's Blog</a></li>
</ol>

下面的代码演示如何打印到控制台的书签的此列表:

var bookmarks = bookmarksDoc.Body().Ol("bookmark-list").Lis();
bookmarks.Each(bm => Console.WriteLine("{0}: {1}",
    bm.Anchor().AnchorText, bm.Anchor().AnchorLink));

请注意每个 <li> 如何包含链接到指定的书签的 <a> 元素。 如果要定位的一个定位元素,您将检索书签详细信息表示前面所示。 当您开始定义这样的资源之间的链接,您的服务启动成为网站的链接的资源。

的非常明显人们可以使用一个的 Web 浏览器,但如何使用应用程序的资源之间的导航如何? 应用程序只需要以编程方式定位感兴趣的定位元素,然后发出 GET 请求 URI"href"属性中指定的目标。 这些详细信息也隐藏 XLinq 扩展方法的封装定位导航。

下面的代码演示如何定位到书签列表中第一个书签,然后到目标书签 URL。 生成的 XHTML 被打印到控制台:

var bookmarkDoc = bookmarks.First().Anchor().Navigate();
var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");
var targetDoc = bookmarkDetails.Anchor("bookmark-url-link").Navigate();
Console.WriteLine(targetDoc);

一旦您开始考虑生成为资源的站点浏览您的服务使用者,您正在正式开始认为更 RESTful 的方式。

XHTML: 表示输入的窗体

现在让我们假设使用者想要在系统中创建一个新的书签。 如何不会使用者找出哪些数据发送和如何将其发送不 WSDL? 答案是很容易: XHTML 窗体。

使用者第一次检索创建书签窗体的 URI 发出 GET 请求。 该服务返回如下图所示的窗体:

<h1>Create Bookmark</h1>
<form action="/bookmark/create" class="create-bookmark-form" method="post">
  <p>
    <label for="bookmark-title">Title:</label><br />
    <input id="bookmark-title" name="bookmark-title" type="text" value="" />
  </p>
  <p>
    <label for="bookmark-url">Url:</label><br />
    <input id="bookmark-url" name="bookmark-url" type="text" value="" />
  </p>
  <p><input type="submit" value="Create" name="submit" /></p>
</form>

在窗体介绍了如何生成的 HTTP POST 请求创建新的书签。 在窗体表示您需要提供该书签的标题和 URL 的书签的字段。 在此示例中,书签 ID 创建,期间将自动生成,并且将从登录的用户标识派生书签的用户名。 窗体还告诉您需要发送的内容以及如何将其发送。

当在浏览器中呈现时此窗体时,一个人可以只需填写窗体并单击提交将创建一个新的书签。 应用程序基本上的实现方法以编程方式提交表单相同的操作。 再次,使用如下所示的一些基于窗体的扩展方法可以进行此过程更容易:

var createBookmarkForm = createBookmarkDoc.Body().Form("create-bookmark-  form");
createBookmarkForm["bookmark-title"] = "Windows Live";
createBookmarkForm["bookmark-url"] = "https://live.com/";
createBookmarkForm.Submit();

此代码在运行时将提交方法生成针对在"操作"URL 的 HTTP POST 请求并 URL 编码的字符串 (应用程序/x-www 的窗体的 urlencoded) 格式输入的字段组合在一起。在结束,它是不使用浏览器不同,结果是新的书签。

尽管今天的浏览器支持 GET 和 POST 仅对该窗体方法,执行任何操作停止您还指定 PUT 或面向 nonbrowser 使用者时删除为窗体"方法"。提交扩展方法执行同样适合于任何指定的 HTTP 方法。

了解 ASP.NET MVC 体系结构

ASP.NET MVC 体系结构基于已经数十年的常用模型-视图-控制器设计模式。图 1 显示了各种 ASP.NET MVC 组件及其如何与它们关联另一个。ASP.NET MVC 附带了一个位于其他 MVC 组件的前面,路由引擎。路由引擎接收传入的 HTTP 请求,并将它们路由到一个控制器的方法。路由引擎依赖于一组集中在 Global.asax 中定义的路由。

fig01.gif

图 1 ASP.NET MVC 的结构

集中式的路由定义 URL 模式和特定的控制器的方法和参数之间的映射。生成链接时, 使用这些路由生成适当的链接。这使得易于修改 URL 设计在整个开发过程中在一个中心位置。

是作业控制器从传入的请求中提取信息并与用户定义的模型层进行交互。模型层可以是任何内容 (Linq SQL,ADO.NET 实体框架、 NHibernate,等) — — 它是层执行业务逻辑和交流到基础数据库的。注意该模型不 System.Web.Mvc 命名空间内。完成该控制器已使用模型,它创建视图提供的呈现输出时使用该视图的模型数据视图。

在下面的部分中,我将介绍实现完整书签服务使用 ASP.NET MVC 体系结构的过程。服务支持多个用户和公钥和私钥的书签。用户可以浏览所有的公用书签和筛选器,它们根据用户名或标记,和它们可以完全管理 (CRUD) 自己的集合的专用书签。

若要开始,您需要创建一个 ASP.NET MVC 的项目。您可以在 Web 项目类型的列表中找到 ASP.NET MVC Web 应用程序项目模板。默认项目模板使您实际上可以通过按 F5 运行一个示例 MVC 起始应用程序。

请注意如何在解决方案结构目录为提供了模型、 视图,和控制器 — — 这是为这些不同的组件代码所在的位置。带有两个控制器的默认模板: 一个用于管理用户帐户 (AccountController),和另一个用于支持到主目录 (HomeController) 的请求。在书签服务中使用这两种。

实现模型

您应该关注的首先是为书签服务模型。构建包含三个表的管理书签信息的简单 SQL Server 数据库 — — 书签、 标记和 BookmarkTag (请参见 图 2 )) 和它们相当容易理解。

fig02.gif

图 2 书签服务 Linq 到 SQL 模型

在只有一点需要特别注意是示例依赖于内置 ASP.NET 表单身份验证和成员资格服务,默认项目,附带 AccountController 管理该服务的用户帐户,因此,信息将存储在不同的数据库 (aspnetdb.mdf),独立于该书签的信息的用户帐户提供的。用户名只存储在书签表中。

它是提供业务对象和逻辑数据库的模型的作业。此的示例我已定义该 Linq SQL 模型 图 2 所示。这种模型中 BookmarksModel.dbml,定义生成 BookmarksModel.designer.cs 中找到 C# 代码。您可以找到名为书签、 标记,和 BookmarkTag 的类。您还可以找到 BookmarksModelDataContext 类引导实体。

在此时您可以决定要直接使用 SQL 的类为您 MVC 模型层,在 Linq,或重您可以通过定义一个较高级别的储备库的类,定义业务逻辑操作并使从基础数据操作详细信息的更多控制器/视图转进一步。图 3 显示了在书签服务中使用 BookmarksRepository 类的定义。

图 3 BookmarksRepository 类

public class BookmarksRepository
{
    // generated Linq to SQL DataContext class
    BookmarksModelDataContext db = new BookmarksModelDataContext();

    // query methods
    public IQueryable<Bookmark> FindAllBookmarks() { ... }
    public IQueryable<Bookmark> FindAllPublicBookmarks() { ... }
    public IQueryable<Bookmark> FindBookmarksByUser(string username)  { ... }
    public IQueryable<Bookmark> FindPublicBookmarksByUser(string username) { ... }
    public IQueryable<Bookmark> FindBookmarksByTag(string tag) { ... }
    public Bookmark FindBookmarkById(int id) { ... }
    public IQueryable<string> FindUsers(){ ... }
    public IQueryable<Tag> FindTags() { ... }
    public Tag FindTag(string tag) { ... }

    // insert/delete methods
    public void AddBookmark(Bookmark bm) { ... }
    public void AddTag(Tag t) { ... }
    public void AddTagForBookmark(string tagText, Bookmark bm) { ... }
    public void DeleteBookmark(Bookmark bm) { ... }

    // persistence
    public void Save() { ... }
}

实现控制器

该控制器是代码的负责管理 HTTP 请求生命周期段。 请求时 ASP.NET MVC 路由引擎确定要使用的控制器 (基于 URL),然后将请求路由控制器通过在其上调用特定的方法。 因此,编写一个控制器时, 您正在编写由路由引擎将调用的入口点。

在书签服务我们要允许已通过身份验证的用户创建、 编辑,和删除的书签。 在创建书签时用户应该能够将其标记为 (共享) 的公共或私有。 所有用户都应该能够浏览公用书签和筛选这些用户名或标记。 专用的书签但是,应仅对所有者可见。 使用者也应该能够检索特定的书签详细信息假定他们有权执行此操作。 我们应该还使浏览系统中的所有用户和标记,作为一种定位公用书签与它们相关联的方式。

图 4 显示 BookmarkController 类需要支持服务要求刚才介绍的方法。 第三个查询方法使得检索所有的公用书签,书签用户或书签标记的。 类还包括用于检索用户和标记和检索特定书签实例的详细信息的方法。 所有这些方法响应 HTTP GET 的请求,但每个将绑定到不同的 URI 模板时定义路由。

图 4 BookmarkController 类

[HandleError]
public class BookmarkController : Controller
{
    // underlying model
    BookmarksRepository bmRepository = new BookmarksRepository();

    // query methods
    public ActionResult BookmarkIndex() { ... }
    public ActionResult BookmarksByUserIndex(string username) { ... }
    public ActionResult BookmarksByTagIndex(string tag) { ... }
    public ActionResult UserIndex() { ... }
    public ActionResult TagIndex() { ... }
    public ActionResult Details(int id) { ... }

    // create boomark
    [Authorize]
    public ActionResult Create() { ... }
    [Authorize]
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Create(FormCollection collection) { ... }

    // update bookmark    
    [Authorize]
    public ActionResult Edit(int id) { ... }
    [Authorize]
    [AcceptVerbs(HttpVerbs.Put | HttpVerbs.Post)]
    public ActionResult Edit(int id, FormCollection collection)

    // delete bookmark
    [Authorize]
    public ActionResult Delete(int id) { ... }
    [Authorize]
    [AcceptVerbs(HttpVerbs.Delete | HttpVerbs.Post)]
    public ActionResult Delete(int id, FormCollection collection) { ... }
}

该方法的其余部分是用于创建、 编辑,和删除书签实例。 注意有两种用于每个逻辑操作的方法 — — 一个用于检索在输入窗体和另一个用于响应窗体提交 —,这些方法中的每个需要授权。 授权属性可确保调用方是经过身份验证和授权访问控制器的方法。 (该属性还允许您指定的用户和角色)。 将如果一个未经身份验证或未经授权的用户尝试访问控制器的方法与 [授权] 批注,授权筛选器自动用户重定向到 AccountController 的登录方法向使用者的"登录"视图。

您可以使用 AcceptVerbs 属性来指定一个特定的控制器的方法将处理的 HTTP 动作 (默认值为 GET)。 一个方法可以处理 ORing 由多个谓词 HttpVerb 值组合在一起。 这是因为我绑定了第二个 Edit 方法 PUT 和 POST 以适应浏览器。 这种配置允许浏览器调用使用 POST,nonbrowser 使用者可以使用 (即多正确) PUT 时操作。 我做类似删除第二个方法,删除和 POST 绑定。 仍有一定要小心我的方法实现确保 idempotency,这是要求的 PUT 和删除。

看看如何实现这些方法的一些有。 首先是 BookmarkIndex:

public ActionResult BookmarkIndex()
{
    var bms = bmRepository.FindAllPublicBookmarks().ToList();
    return View("BookmarkIndex", bms);
}

这种实现只需检索的公用书签实体从存储库,并返回视图称为 BookmarkIndex (列表中的书签实体传递)。 该视图是负责显示书签实体由控制器提供给它的列表。

在详细信息的方法查找目标书签,并返回 404 未找到错误,如果它不存在。 然后,它确保用户有权查看该书签。 如果这样,则将返回在详细信息视图提供标识的书签实体。 否则将返回给使用者的未经授权的响应。

public ActionResult Details(int id)
{
    var bm = bmRepository.FindBookmarkById(id);
    if (bm == null)
        throw new HttpException(404, "Not Found");
    if (!bm.Shared)
    {
        if (!bm.Username.Equals(HttpContext.User.Identity.Name))
            return new HttpUnauthorizedResult();
    }
    return View("Details", bm);
}

负一最终绀緥一下这两种创建方法。 第一个是实际上非常简单 — — 它将返回创建视图,以显示窗体创建新的书签的说明:

[Authorize]
public ActionResult Create()
{
    return View("Create");
} 

图 5 显示了其响应窗体提交请求,第二个 Create 方法。 它在传入 FormCollection 对象中找到该书签信息创建一个新的书签实体,并再将其保存到数据库。 它还与书签关联的任何新的标记中更新数据库,然后将用户重定向到他们的个人书签,以指示成功的列表。

图 5 对窗体提交请求的响应的创建方法

[Authorize]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(FormCollection collection)
{
    try
    {
        Bookmark bm = new Bookmark();
        bm.Title = collection["bookmark-title"];
        bm.Url = collection["bookmark-url"];
        bm.Shared = collection["bookmark-shared"].Contains("true");
        bm.LastModified = DateTime.Now;
        bm.Username = HttpContext.User.Identity.Name;
        bm.Tags = collection["bookmark-tags"];

        bmRepository.AddBookmark(bm);
        bmRepository.Save();

        ... // create any new tags that are necessary

        return RedirectToAction("BookmaryByUserIndex", 
            new { username = HttpContext.User.Identity.Name });
    }
    catch
    {
        return View("Error");
    }
}

没有空间来覆盖在整个控制器实现,但这些代码示例应使您体验为您在控制器中编写的代码的类型。

路由设计 URI

接下来您要做的只是定义 URL 映射到各种 BookmarkController 方法的路由。 在定义您的应用程序路由 Global.asax RegisterRoutes 方法内。 首次创建一个 MVC 项目时, 您 Global.asax 将包含默认的路由代码 图 6 所示。

图 6 默认路由的代码

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(
            "Default",                             // Route name
            "{controller}/{action}/{id}",          // URL with parameters
            new { controller="Home",               // Parameter defaults
                   action="Index", id="" } 
        );

    }

    protected void Application_Start()
    {
        RegisterRoutes(RouteTable.Routes);
    }
}

单个 MapRoute 调用创建一个像一个 catchall 对所有的 URI 的默认路由规则。 此路由概述的第一个路径段代表该控制器的名称,第二个路径段代表对策的名称 (控制器方法),第三个的路径段表示 ID 值。 此单个规则可以处理以下 URI,并将它们路由到相应的控制器方法:

/Account/Logon
/Bookmark/Create
/Bookmark/Details/25
/Bookmark/Edit/25

图 7 显示路由,我使用此书签服务示例。 在这些附加路由,与使用者可以浏览到"/ 用户可以检索列表的用户,"/ 标记"检索标记的列表或"/ 书签"检索的公用书签列表。 使用者还可以浏览到"/tags/ {tagname} 或"/users/ {用户名}"筛选器的书签标记或用户名,分别。 所有其他 URI 处理默认路由 图 6 所示。

图 7 书签服务路由

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    // customer routes
    routes.MapRoute("Users", "users",
        new { controller = "Bookmark", action = "UserIndex" });
    routes.MapRoute("Tags", "tags",
        new { controller = "Bookmark", action = "TagIndex" });
    routes.MapRoute("Bookmarks", "bookmarks",
        new { controller = "Bookmark", action = "BookmarkIndex" });
    routes.MapRoute("BookmarksByTag", "tags/{tag}",
        new { controller = "Bookmark", action = "BookmarksByTagIndex", tag = "" });
    routes.MapRoute("BookmarksByUser", "users/{username}",
        new { controller="Bookmark", action="BookmarksByUserIndex", username="" });
    // default route
    routes.MapRoute("Default", "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = ""});
}

实现视图

此时,我们所做的大多数同时适用于 MVC"站点"和"服务"。 MVC 的所有应用程序需要模型、 控制器,和路由。 在视图中找到大多数有关生成"服务"MVC 不同的是什么。 而不是产生消耗人工传统的 HTML 视图,服务必须生成一个适当的人员和编程消耗的视图。

我们将使用我们的默认服务表示 XHTML,并应用前面所述的书签数据映射到 XHTML 技术。 我们将映射到 <div> 数据实体和 <span> 的元素,并且我们将表示使用组合的 <ol> 和 <li> 的集合。 我们还将批注这些元素与"类"使用者提供其他类型的元数据属性。

ASP.NET MVC"视图"只是.aspx 页面,这些定义一个视图的模板的页面。 在.aspx 页组织的控制器名称,该视图目录中。 每个视图可以与 ASP.NET 母版页来维护一个一致的模板相关联。 图 8 显示书签服务的母版页的页面。

图 8 书签服务母版页

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html >
<head runat="server">
    <title><asp:ContentPlaceHolder ID="Title" runat="server" /></title>
</head>
<body>
    <div style="text-align:right">
        <% Html.RenderPartial("LogOnUserControl"); %>
    </div>
    <h1><asp:ContentPlaceHolder ID="Heading" runat="server" /></h1>
    <hr />
    <asp:ContentPlaceHolder ID="MainContent" runat="server" />
    <hr />
    <div class="nav-links-footer">
        <%=Html.ActionLink("Home", "Index", "Home", null, 
           new { @class = "root-link" } )%> |
        <%=Html.ActionLink("Public Bookmarks", "BookmarkIndex", "Bookmark", null, 
           new { @class = "public-bookmarks-link" } )%> |
        <%=Html.ActionLink("User Bookmarks", "BookmarksByUserIndex", "Bookmark", 
           new { username = HttpContext.Current.User.Identity.Name }, 
           new { @class = "my-bookmarks-link" })%> |
        <%=Html.ActionLink("Users", "UserIndex", "Bookmark", null, 
           new { @class = "users-link" } )%> |
        <%=Html.ActionLink("Tags", "TagIndex", "Bookmark", null, 
           new { @class = "tags-link" } )%> 
    </div>    
</body>
</html>

主控页定义的三个占位符: 一个用于页标题,另一个用于 <h1> 的标题,另一个用于主要内容区域。 这些占位符将填写的每个单独的视图。 鍙 ﹀ 的方式  母版页的页面顶部的页中,显示登录控件,并提供包含根服务链接以简化导航页脚。 请注意如何我使用 Html.ActionLink 方法来生成基于预定义的路由和控制器操作这些链接。

图 9 显示了主的书签的索引视图,您将获得时,浏览到"/ 书签"。 它显示的 <ol>,结合使用的书签列表 <li>,和 <a> 元素。 <ol> 元素与类批注 ="书签的列表"和 <a> 的每个元素与类批注 = 书签链接。 此视图还提供了检索创建书签窗体说明 (右) 列表上方的一个链接。 如果导航到链接, 图 10 所示的创建视图进入操作。

图 9 书签索引视图

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.
Master" Inherits="System.Web.Mvc.ViewPage
<IEnumerable<MvcBookmarkService.Models.Bookmark>>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="Title" runat="server">
Public Bookmarks</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Heading" runat="server">
Public Bookmarks</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <%= Html.ActionLink("Create bookmark", "Create", "Bookmark", new { id = "" },
           new { @class = "create-bookmark-form-link" } )%>
    <ol class="bookmark-list">
        <% foreach (var item in Model) { %>
            <li><%= Html.ActionLink(Html.Encode(item.Title), "Details", "Bookmark", 
                    new { id = item.BookmarkID }, new { @class = "bookmark-link" })%></li>
        <% } %>        
    </ol>
</asp:Content>

图 10 书签创建视图

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
    Inherits="System.Web.Mvc.ViewPage<MvcBookmarkService.Models.Bookmark>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="Title" runat="server">
Create Bookmark</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Heading" runat="server">
Create Bookmark</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <% using (Html.BeginForm("Create", "Bookmark", FormMethod.Post, 
       new { @class = "create-bookmark-form" } ))
       {%>
        <p>
            <label for="Title">Title:</label><br />
            <%= Html.TextBox("bookmark-title")%>
        </p>
        <p>
            <label for="Url">Url:</label><br />
            <%= Html.TextBox("bookmark-url")%>
        </p>
        <p>
            <label for="Tags">Tags:</label><br />
            <%= Html.TextBox("bookmark-tags")%>
        </p>
        <p>
            <label for="Shared">Share with public: </label>
            <%= Html.CheckBox("bookmark-shared")%>
        </p>
        <p>
            <input type="submit" value="Create" name="submit" />
        </p>
    <% } %>
</asp:Content>

创建视图生成一个简单的 XHTML 窗体,但 <form> 元素与类批注 ="创建的书签的表格",并且每个 <input> 的元素赋予一个上下文的名称/ID 值,用于标识每个书签的字段。 此表单提供使用者完整 XHTML 说明如何以编程方式创建新的书签使用我们的服务 (只需将提交窗体)。

负一个最终绀緥 图 11 显示了书签详细信息视图的开头。 此处我使用一个 <div> 元素表示书签结构 (类 ="书签") 一起 <span> 和 <a> 元素来表示书签字段。 每个都带有一个"类"属性来指定字段名称。

图 11 书签的详细信息视图

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
   Inherits="System.Web.Mvc.ViewPage<MvcBookmarkService.Models.Bookmark>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="Title" runat="server">
Bookmark Details: <%= Model.BookmarkID %></asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="Heading" runat="server">
Bookmark Details: <%= Model.BookmarkID %></asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <br />
    <div class="bookmark">
     BookmarkID: <span class="bookmark-id"><%= Html.Encode(Model.BookmarkID) %></span><br />
     Title: <span class="bookmark-title"><%= Html.Encode(Model.Title)    %></span><br />
     Url: <a class="bookmark-url-link" href="<%= Html.Encode(Model.Url) %>">
     <%= Html.Encode(Model.Url) %></a><br />
     Username: <%=Html.ActionLink(Model.Username, "BookmarksByUserIndex", "Bookmark", 
     new { username=Model.Username }, new { @class="bookmark-username-link" }) %><br />
...

同样,没有空间来查看详细信息,在所有视图示例,但我希望这阐释了如何可以生成很容易为应用程序和人们使用干净 XHTML 结果集。

使用书签服务

最简单的方法使用书签服务是通过 Web 浏览器。由于将在 XHTML 设计您应该能够浏览到该服务的根的 URL,并开始从此处导航。图 12 显示了浏览器浏览到该书签服务的根和登录时。可以单击定位到列表中所有的公用书签,然后导航到指定的书签列表中的公用书签。如果您单击编辑,您实际上可以编辑书签详细信息 (请参见 图 13 )。该服务是从任何 Web 浏览器完全可用。

fig10.gif

图 12 到 MVC 书签服务浏览

fig13.gif

图 13 编辑特定书签

而您正在周围服务选择查看源文件,有时会在您的浏览器浏览您将看到如何简单,生成 XHTML 外观,这再次便于对进行程序设计。

图 14 显示了为一个完整.NET 客户端的应用程序使用书签服务代码。它使用我先前介绍来简化 XHTML 处理详细信息的 HTTP XLinq 扩展方法的设置。什么有趣的有关此示例是它可以用作多一个人 — — 它需要仅根定位到书签服务公开感兴趣的其他所有内容的 URI。

图 14 编写.NET 客户端使用 XLinq 书签服务

class Program
{
    static void Main(string[] args)
    {
        // navigate to the root of the service
        Console.WriteLine("Navigating to the root of the service...");
        Uri uri = new Uri("http://localhost:63965/");
        CookieContainer cookies = new CookieContainer();
        var doc = XDocument.Load(uri.ToString());
        doc.AddAnnotation(uri);

        // navigate to public bookmarks
        Console.WriteLine("Navigating to public bookmarks...");
        var links = doc.Body().Ul("nav-links").Lis();
        var bookmarksLink = links.Where(l => l.HasAnchor("public-bookmarks-link")).First();
        var bookmarksDoc = bookmarksLink.Anchor().Navigate();
        bookmarksDoc.AddAnnotation(cookies);

        // display list of bookmarks
        Console.WriteLine("\nPublic bookmarks found in the system:");
        var bookmarks = bookmarksDoc.Body().Ol("bookmark-list").Lis();
        bookmarks.Each(bm => Console.WriteLine("{0}: {1}",
            bm.Anchor().AnchorText, bm.Anchor().AnchorLink));

        // navigate to the first bookmark in the list
        Console.WriteLine("\nNavigating to the first bookmark in the list...");
        var bookmarkDoc = bookmarks.First().Anchor().Navigate();
        var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");

        // print the bookmark details out to the console window
        Console.WriteLine("Bookmark details:");
        Console.WriteLine("bookmark-id: {0}", bookmarkDetails["bookmark-id"].Value);
        Console.WriteLine("bookmark-url-link: {0}", 
            bookmarkDetails["bookmark-url-link"].Value);
        Console.WriteLine("bookmark-title: {0}", bookmarkDetails["bookmark-title"].Value);
        Console.WriteLine("bookmark-shared: {0}", bookmarkDetails["bookmark-shared"].Value);
        Console.WriteLine("bookmark-last-modified: {0}", 
            bookmarkDetails["bookmark-last-modified"].Value);

        // retrieving login form
        Console.WriteLine("\nRetrieving login form...");
        Uri logonUri = new Uri("http://localhost:63965/Account/Logon");
        var logonDoc = XDocument.Load(logonUri.ToString());
        logonDoc.AddAnnotation(logonUri);
        logonDoc.AddAnnotation(cookies);

        // logging on as skonnard
        Console.WriteLine("Logging in as 'skonnard'");
        var logonForm = logonDoc.Body().Form("account-logon-form");
        logonForm["username"] = "skonnard";
        logonForm["password"] = "password";
        logonForm.Submit();
        Console.WriteLine("Login successful!");

        // create a new bookmark as 'skonnard'
        var createBookmarkDoc = bookmarksDoc.Body().Anchor(
            "create-bookmark-form-link").Navigate();
        createBookmarkDoc.AddAnnotation(cookies);
        var createBookmarkForm = createBookmarkDoc.Body().Form("create-bookmark-form");
        createBookmarkForm["bookmark-title"] = "Test from console!";
        createBookmarkForm["bookmark-url"] = "https://live.com/";
        createBookmarkForm["bookmark-tags"] = "Microsoft, Search";
        createBookmarkForm.Submit();
        Console.WriteLine("\nBookmark created!");
    }
}

客户端启动通过导航到该根地址,然后它查找链接到公共的书签。下一步,它定位到公用书签列表中,标识 (在此例第一个) 感兴趣的特定书签。下一步,它定位到书签详细信息并显示到控制台窗口。然后检索登录窗体并执行使用一组凭据登录。一旦登录,应用程序将检索创建书签窗体、 填写,并提交新系统的书签。

有几个键的观察值在此时进行。首先,控制台应用程序是能够执行一个人可以通过 Web 浏览器中执行的所有内容。这是此 XHTML 设计样式的大失所望功能。第二个,使用者只需要是对根 URI 公开的服务的硬编码。所有其他 URI 均可发现在运行时通过导航链接,XHTML 中找到。并且最后,处理 XHTML 结构不的其他很多不同,这只是数据。以及此类型的代码只获取更容易移动时向动态语言在将来的.NET 版本。

提供最终,ASP.NET MVC 了一个本身 RESTful 框架,用于实现可由人类和应用程序同时 XHTML 基于资源的站点。您可以下载整个示例应用程序从 MSDN Magazine 网站本文所示。

另一个完整的示例基于 XHTML 的 RESTful 服务的现实世界中找到,浏览到该Microsoft/TechNet 发布系统 (MTPS) 内容服务. 此服务使用许多我已在本文介绍了做法。

确认

由于 Tim Ewald 和 Craig Andera,在此区域中的创造性思维提供燃料为我的项目。Tim 还提供了在附带的示例应用程序中找到该 XLinq 扩展方法。

李 Skonnard Pluralsight,Microsoft 培训提供程序提供两个由教师指导和点播的开发人员课程的一个 cofounder。这些天李花费大部分记录 Pluralsight 的请求他时间 !课程侧重于云计算 Windows Azure,WCF,REST。您可以与他在http://pluralsight.com/aaronhttp://twitter.com/skonnard.