Übersetzung vorschlagen
 
Andere Vorschläge:

progress indicator
Keine anderen Vorschläge
MSDN Magazin > Home > Ausgaben > 2009 > MSDN Magazin Juli 2009 >  Rest XHTML: RESTful Services mit ASP.NET MVC
Inhalt anzeigen:  Englisch mit deutscher ÜbersetzungInhalt anzeigen: Englisch mit deutscher Übersetzung
Dies sind maschinell übersetzte Inhalte, die von den Mitgliedern der Community bearbeitet werden können. Sie können die Übersetzung verbessern, indem Sie auf den jeweils zum Satz gehörenden Link "Bearbeiten" klicken.Mithilfe des Dropdown-Steuerelements "Inhalt anzeigen" links oben auf der Seite können Sie zudem bestimmen, ob nur der englische Originaltext, nur die deutsche Übersetzung oder beides nebeneinander angezeigt werden.
RESTful XHTML
RESTful Services With ASP.NET MVC
Aaron Skonnard
Code download available from the MSDN Code Gallery
Browse the Code Online

This article discusses:
  • REST
  • XHTML
  • ASP.NET MVC
This article uses the following technologies:
REST, XHTML, ASP.NET
A RESTful service is a web of resources that programs can navigate. When designing a RESTful service, you have to think carefully about how your web will work. This means designing resource representations with links that facilitate navigation, describing service input somehow, and considering how consumers will navigate around your service at run time. Getting these things right is often overlooked, but they're central to realizing the full potential REST has to offer.
Today, humans navigate sites using Web browsers that know how to render HTML and other popular content types. HTML provides the syntax and semantics for establishing links between resources (<a> element) and for describing and submitting application input (<form> and <input> elements).
When a user clicks on an <a> element in the rendered page, the browser knows to issue an HTTP GET request for the target resource and render the response. When a browser encounters a <form> element, it knows how to render the form description into a user interface that the user can fill out and submit using either a GET or POST request. When the user presses a submit button, the browser encodes the data and sends it using the specified request. These two features are largely responsible for the success of the Web.
Using links in conjunction with the universal HTTP interface makes it possible to redirect requests to new locations over time and change certain aspects of security on the fly without changing the client code. A standard approach for forms means that you can add or remove input properties and change default values, again without changing the client code. Both features are very useful for building applications that evolve over time.
Your RESTful services should also somehow provide these two features through whatever resource representation you decide to use. For example, if you're designing a custom XML dialect for your service, you should probably come up with your own elements for establishing links and describing service input that will guide consumers through your web. Or you can simply use XHTML.
Most developers don't immediately consider XHTML as an option for "services," but that's actually one of the ways it was intended to be used. XHTML documents are by definition well-formed XML, which allows for automated processing using standard XML APIs. And since XHTML is also HTML, it comes with <a>, <form>, and <input> elements for modeling link navigation and service input as I described earlier. The only thing that's a little strange at first is how you model user-defined data structures—however, you can model classes and fields with <div> and <span> elements and collections of entities with <ol> and <li> elements. I'll walk through how to do this in more detail later in the article.
To summarize, there are several reasons to consider XHTML as the default representation for your RESTful services. First, you can leverage the syntax and semantics for important elements like <a>, <form>, and <input> instead of inventing your own. Second, you'll end up with services that feel a lot like sites because they'll be browsable by both users and applications. The XHTML is still interpreted by a human—it's just a programmer during development instead of a user at runtime. This simplifies things throughout the development process and makes it easier for consumers to learn how your service works. And finally, you can leverage standard Web development frameworks to build your RESTful services.
ASP.NET MVC is one such framework that provides an inherently RESTful model for building XHTML-based services. This article walks through some XHTML design concepts and then shows you how to build a complete XHTML-based RESTful service that you can download from the MSDN Magazine site.

XHTML: Representing Data and Links
Before I dive into the details of ASP.NET MVC, let's first look at how you can represent common data structures and collections in XHTML. This approach isn't the only way to accomplish this, but it's a fairly common practice in XHTML-based services today.
Throughout this article, I'll describe how to implement a simple bookmark service. The service allows users to create, retrieve, update, and delete bookmarks and navigate a web of bookmarks in a variety of ways. Suppose you have a C# class representing a bookmark that looks like this:
public class Bookmark
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Url { get; set; }
    public string User { get; set; }
}
The first question is how can you represent a Bookmark instance in XHTML? One approach is to combine <div>, <span>, and <a> elements, where <div> elements represent structures, <span> elements represent fields, and <a> elements represent identity and links to other resources. In addition, you can annotate these elements with the XHTML "class" attribute to provide additional type metadata. Here's a complete example:
<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>
The next question is how will consumers process this information? Since it's well-formed XML, consumers can use any XML API to extract the bookmark information. Most .NET programmers will probably find that XLinq provides the most natural programming model for consuming XHTML programmatically. In addition, you can go one step further by enhancing XLinq with some helpful XHTML-focused extension methods that make the programming model even easier.
Throughout this article, I'll use a set of XLinq extension methods that I've included in the downloadable sample code. These extensions give you a good idea of what's possible. The following code shows how to consume the bookmark XHTML shown previously using XLinq and some of these extensions:
var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");
Console.WriteLine(bookmarkDetails["bookmark-id"].Value);
Console.WriteLine(bookmarkDetails["bookmark-url"].Value);
Console.WriteLine(bookmarkDetails["bookmark-title"].Value);
Now, if you want to improve how this bookmark renders in a browser, you can add a Cascading Stylesheet (CSS) to control browser-specific rendering details or add some additional UI elements and text that don't compromise the consumer's ability to extract the data of interest. For example, the following XHTML will be easier for humans to process, but you can still use the previous .NET code sample to process the information without any modification:
<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>
Collections of resources aren't hard to model either. You can represent a list of bookmarks with a combination of <ol>, <li>, and <a> elements as shown here:
<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>
The following code shows how to print this list of bookmarks to the console:
var bookmarks = bookmarksDoc.Body().Ol("bookmark-list").Lis();
bookmarks.Each(bm => Console.WriteLine("{0}: {1}",
    bm.Anchor().AnchorText, bm.Anchor().AnchorLink));
Notice how each <li> contains an <a> element that links to a specific bookmark. If you were to navigate one of the anchor elements, you would retrieve the bookmark details representation shown earlier. As you begin to define links between resources like this, your service starts becoming a web of linked resources.
It's pretty obvious how humans can navigate between resources using a Web browser, but how about consuming applications? A consuming application just needs to programmatically locate the anchor element of interest and then issue a GET request targeting the URI specified in the "href" attribute. These details can also be hidden behind an XLinq extension method that encapsulates anchor navigation.
The following code shows how to navigate to the first bookmark in the bookmark list and then to the target bookmark URL. The resulting XHTML is printed to the console:
var bookmarkDoc = bookmarks.First().Anchor().Navigate();
var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");
var targetDoc = bookmarkDetails.Anchor("bookmark-url-link").Navigate();
Console.WriteLine(targetDoc);
Once you start thinking about building consumers that navigate your service as a web of resources, you're officially starting to think in a more RESTful way.

XHTML: Representing Input with Forms
Now let's say a consumer wants to create a new bookmark in the system. How does the consumer figure out what data to send and how to send it without WSDL? The answer is easy: XHTML forms.
The consumer first issues a GET request to the URI for retrieving the create bookmark form. The service returns a form that looks something like this:
<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>
The form describes how to build an HTTP POST request for creating a new bookmark. The form indicates that you need to provide the bookmark-title and bookmark-url fields. In this example, bookmark-id will be autogenerated during creation, and bookmark-username will be derived from the logged-in user identity. The form also tells you what you need to send and how to send it.
When this form is rendered in a browser, a human can simply fill out the form and click Submit to create a new bookmark. A consuming application basically does the same thing by submitting the form programmatically. Again, this process can be made easier by using some form-based extension methods, shown here:
var createBookmarkForm = createBookmarkDoc.Body().Form("create-bookmark-  form");
createBookmarkForm["bookmark-title"] = "Windows Live";
createBookmarkForm["bookmark-url"] = "http://live.com/";
createBookmarkForm.Submit();
When this code runs, the Submit method generates an HTTP POST request targeting the "action" URL, and the input fields are formatted together as a URL-encoded string (application/x-www-form-urlencoded). In the end, it's no different from using the browser—the result is a new bookmark.
Although today's browsers support GET and POST only for the form method, nothing is stopping you from also specifying PUT or DELETE as the form "method" when targeting nonbrowser consumers. The Submit extension method performs equally well for any HTTP method you specify.

Understanding the ASP.NET MVC Architecture
The ASP.NET MVC architecture is based on the popular model-view-controller design pattern that has been around for decades. Figure 1 illustrates the various ASP.NET MVC components and how they relate to one another. ASP.NET MVC comes with a routing engine that sits in front of the other MVC components. The routing engine receives incoming HTTP requests and routes them to a controller method. The routing engine relies on a centralized set of routes that you define in Global.asax.
Figure 1 ASP.NET MVC Architecture
The centralized routes define mappings between URL patterns and specific controller methods and arguments. When you generate links, you use these routes to generate the links appropriately. This makes it easy to modify your URL design throughout the development process in one central location.
It's the job of the controller to extract information from the incoming request and to interact with the user-defined model layer. The model layer can be anything ( Linq to SQL, ADO.NET Entity Framework, NHibernate, and so on)—it's the layer that performs business logic and talks to the underlying database. Notice how the model is not within the System.Web.Mvc namespace. Once the controller has finished using the model, it creates a view, supplying the view with model data for the view to use while rendering the output.
In the following sections, I'll walk through the process of implementing a complete bookmark service using the ASP.NET MVC architecture. The service supports multiple users and both public and private bookmarks. Users can browse all public bookmarks and filter them based on username or tags, and they can fully manage (CRUD) their own collection of private bookmarks.
To get started, you need to create an ASP.NET MVC project. You'll find the ASP.NET MVC Web Application project template in the list of Web project types. The default project template gives you a sample MVC starter application that you can actually run by pressing F5.
Notice how the solution structure provides directories for Models, Views, and Controllers—this is where you place the code for these different components. The default template comes with two controllers: one for managing user accounts (AccountController), and another for supporting requests to the home directory (HomeController). Both of these are used in the bookmark service.

Implementing the Model
The first thing you should focus on is the model for the bookmark service. I've built a simple SQL Server database that contains three tables for managing bookmark information—Bookmark, Tag, and BookmarkTag (see Figure 2)—and they're pretty self-explanatory.
Figure 2 Bookmark Service Linq to SQL Model
The only caveat is that the example relies on the built-in ASP.NET forms authentication and membership service, which is provided by the default AccountController that comes with the project, to manage the service user accounts, Hence, user account information will be stored in a different database (aspnetdb.mdf), separate from the bookmark information. The username is simply stored in the Bookmark table.
It's the job of the model to provide business objects and logic on top of the database. For this example, I've defined the Linq to SQL model shown in Figure 2. This model, defined in BookmarksModel.dbml, generates the C# code found in BookmarksModel.designer.cs. You'll find classes named Bookmark, Tag, and BookmarkTag. You'll also find a BookmarksModelDataContext class, which bootstraps the entities.
At this point, you can decide to work directly with the Linq to SQL classes as your MVC model layer, or you can go a step further by defining a higher-level repository class that defines the logical business operations and shields the controller/view from even more of the underlying data manipulation details. Figure 3 shows the definition for the BookmarksRepository class used in the bookmark service.
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() { ... }
}

Implementing the Controller
The controller is the piece of code responsible for managing the HTTP request life cycle. When a request arrives, the ASP.NET MVC routing engine determines which controller to use (based on the URL) and then routes the request to the controller by calling a specific method on it. Hence, when you write a controller, you're writing the entry points that will be called by the routing engine.
For the bookmark service, we want to allow authenticated users to create, edit, and delete bookmarks. When creating bookmarks, users should be able to mark them as public (shared) or private. All users should be able to browse public bookmarks and filter them by username or tags. Private bookmarks, however, should be visible only to the owner. Consumers should also be able to retrieve the details for a particular bookmark, assuming they are authorized to do so. We should also make it possible to browse all users and tags in the system as a way to navigate the public bookmarks associated with them.
Figure 4 shows the methods the BookmarkController class needs to support the service requirements I just described. The first three query methods make it possible to retrieve all public bookmarks, bookmarks by user, or bookmarks by tag. The class also includes methods for retrieving users and tags and for retrieving the details of a specific bookmark instance. All these methods respond to HTTP GET requests, but each one will be bound to a different URI template when the routes are defined.
[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) { ... }
}
The rest of the methods are for creating, editing, and deleting bookmark instances. Notice that there are two methods for each logical operation—one for retrieving the input form, and another for responding to the form submission—and each one of these methods requires authorization. The Authorize attribute ensures that the caller is authenticated and authorized to access the controller method. (The attribute also allows you to specify users and roles.) If an unauthenticated or unauthorized user attempts to access a controller method annotated with [Authorize], the authorization filter automatically redirects the user to the AccountController's Logon method, which presents the "Logon" view to the consumer.
You use the AcceptVerbs attribute to specify which HTTP verbs a particular controller method will handle (the default is GET). A single method can handle multiple verbs by ORing the HttpVerb values together. The reason I've bound the second Edit method to both PUT and POST is to accommodate browsers. This configuration allows browsers to invoke the operation using POST, while nonbrowser consumers can use PUT (which is more correct). I've done something similar on the second Delete method, binding it to both DELETE and POST. I still have to be careful that my method implementations ensure idempotency, which is a requirement for both PUT and DELETE.
Let's look at how a few of these methods have been implemented. First is BookmarkIndex:
public ActionResult BookmarkIndex()
{
    var bms = bmRepository.FindAllPublicBookmarks().ToList();
    return View("BookmarkIndex", bms);
}
This implementation simply retrieves the list of public Bookmark entities from the repository and then returns a view called BookmarkIndex (passing in the list of Bookmark entities). The view is responsible for displaying the list of Bookmark entities supplied to it by the controller.
The Details method looks up the target bookmark and returns a 404 Not Found error if it doesn't exist. Then it ensures that the user is authorized to view the bookmark. If so, it returns the Details view, supplying the identified Bookmark entity. Otherwise it returns an Unauthorized response to the consumer.
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);
}
As a final example, let's look at the two Create methods. The first is actually quite simple—it returns the Create view to present the form description for creating a new bookmark:
[Authorize]
public ActionResult Create()
{
    return View("Create");
} 
Figure 5 shows the second Create method, which responds to the form submission request. It creates a new Bookmark entity from the bookmark information found in the incoming FormCollection object and then saves it to the database. It also updates the database with any new tags that were associated with the bookmark and then redirects users to their lists of personal bookmarks to indicate success.
[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");
    }
}
I don't have space to cover the entire controller implementation, but these code samples should give you a taste for the kind of code you write in the controller.

Designing URIs with Routes
The next thing you need to do is define URL routes that map to the various BookmarkController methods. You define your application routes in Global.asax within the RegisterRoutes method. When you first create an MVC project, your Global.asax will contain the default routing code shown in Figure 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);
    }
}
The single call to MapRoute creates a default routing rule that acts like a catchall for all URIs. This route outlines that the first path segment represents the controller name, the second path segment represents the action name (controller method), and the third path segment represents an ID value. This single rule can handle the following URIs and route them to the appropriate controller method:
/Account/Logon
/Bookmark/Create
/Bookmark/Details/25
/Bookmark/Edit/25
Figure 7 shows the routes I'm using for this bookmark service example. With these additional routes in place, consumers can browse to "/users" to retrieve the list of users, "/tags" to retrieve the list of tags, or "/bookmarks" to retrieve the list of public bookmarks. Consumers can also browse to "/tags/{tagname}" or "/users/{username}" to filter bookmarks by tag or username, respectively. All other URIs are handled by the default route shown in Figure 6.
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 = ""});
}

Implementing the Views
Up to this point, most of what we've done applies to both MVC "sites" and "services." All MVC applications need models, controllers, and routes. Most of what's different about building MVC "services" is found in the view. Instead of producing a traditional HTML view for human consumption, a service must produce a view that's appropriate for both human and programmatic consumption.
We're going to use XHTML for our default service representation and apply the techniques described earlier for mapping bookmark data to XHTML. We'll map data entities to <div> and <span> elements, and we'll represent collections using a combination of <ol> and <li>. We'll also annotate these elements with the "class" attribute to provide consumers with additional type metadata.
ASP.NET MVC "views" are just .aspx pages that define a view template. The .aspx pages are organized by controller name within the Views directory. Each view can be associated with an ASP.NET master page to maintain a consistent template. Figure 8 shows the master page for the bookmark service.
<!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>
The master page defines three placeholders: one for the page title, another for the <h1> heading, and another for the main content area. These placeholders will be filled in by each individual view. In addition, the master page displays a login control at the top of the page, and it provides a footer containing the root service links to simplify navigation. Notice how I'm using the Html.ActionLink method to generate these links based on the predefined routes and controller actions.
Figure 9 shows the main Bookmark Index view you get back when you browse to "/bookmarks". It displays the list of bookmarks using a combination of <ol>, <li>, and <a> elements. The <ol> elements are annotated with class="bookmark-list", and each <a> element is annotated with class="bookmark-link". This view also provides a link to retrieve the Create bookmark form description (right above the list). If you navigate to the link, the Create view shown in Figure 10 comes into action.
<%@ 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>
<%@ 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>
The Create view produces a simple XHTML form, but the <form> element is annotated with class="create-bookmark-form", and each <input> element has been given a contextual name/ID value that identifies each bookmark field. This form gives consumers a complete XHTML description of how to programmatically create a new bookmark using our service (by simply submitting the form).
As a final example, Figure 11 shows the beginning of the Bookmark Details view. Here I'm using a <div> element to represent the bookmark structure (class="bookmark") along with <span> and <a> elements to represent the bookmark fields. Each carries a "class" attribute to specify the field name.
<%@ 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 />
...
Again, I don't have space to look at all the view examples in detail, but I hope this illustrates how you can produce clean XHTML result sets that are easy for both applications and humans to consume.

Consuming the Bookmark Service
The easiest way to consume the bookmark service is through a Web browser. Thanks to the XHTML design, you should be able to browse to the service's root URL and begin navigation from there. Figure 12 shows what the browser looks like when you browse to the root of the bookmark service and log in. You can click Public Bookmarks to navigate to the list of all public bookmarks, and then navigate to a specific bookmark in the list. If you click Edit, you can actually edit the bookmark details (see Figure 13). The service is fully usable from any Web browser.
Figure 12 Browsing to the MVC Bookmark Service
Figure 13 Editing a Specific Bookmark
While you're browsing around the service, select View Source occasionally in your browser, and you'll notice how simple the resulting XHTML looks, which again makes it easy to program against.
Figure 14 shows the code for a complete .NET client application that consumes the bookmark service. It uses the set of XLinq extension methods I described earlier to simplify the XHTML and HTTP processing details. What's interesting about this sample is that it acts more like a human—it needs only the root URI to navigate to everything else of interest exposed by the bookmark service.
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"] = "http://live.com/";
        createBookmarkForm["bookmark-tags"] = "Microsoft, Search";
        createBookmarkForm.Submit();
        Console.WriteLine("\nBookmark created!");
    }
}
The client starts by navigating to the root address, and then it looks for the link to the public bookmarks. Next it navigates to the public bookmark list and identifies a specific bookmark of interest (in this case, the first one). Next it navigates to the bookmark details and displays them to the console window. Then it retrieves the login form and performs a login using a set of credentials. Once logged in, the application retrieves the create bookmark form, fills it out, and submits a new bookmark to the system.
There are a few key observations to make at this point. First, the console application is capable of doing everything a human can do through the Web browser. That's the killer feature of this XHTML design style. Second, consumers need only to be hard-coded against the root URIs exposed by the service. All other URIs are discoverable at run time by navigating links found within the XHTML. And finally, processing XHTML structures isn't that much different from anything else—it's just data. Plus, this type of code only gets easier as you move toward dynamic languages in future versions of .NET.
Ultimately, ASP.NET MVC provides an inherently RESTful framework for implementing a web of XHTML-based resources that can be consumed by both humans and applications simultaneously. You can download the entire sample application shown in this article from the MSDN Magazine Web site.
For another complete example of an XHTML-based RESTful service found in the real world, browse to the Microsoft/TechNet Publishing System (MTPS) Content Services. This service uses many of the practices I've outlined in this article.

Acknowledgments
Thanks to both Tim Ewald and Craig Andera, whose creative thinking in this area provided fuel for my article. Tim also provided the XLinq extension methods found in the accompanying sample application.

Aaron Skonnard is a cofounder of Pluralsight, a Microsoft training provider offering both instructor-led and on-demand developer courses. These days Aaron spends most of his time recording Pluralsight On-Demand! courses focused on Cloud Computing, Windows Azure, WCF and REST. You can reach him at http://pluralsight.com/aaron and http://twitter.com/skonnard.

RESTful XHTML
RESTful Services mit ASP.NET MVC
Aaron Skonnard
Herunterladen von Code von der MSDN Code Gallery verfügbar
Code online durchsuchen

Themen in diesem Artikel:
  • REST
  • XHTML
  • XML-Webdienste MVC
In diesem Artikel werden die folgenden Technologien verwendet:
REST, XHTML XML-Webdienste
A RESTful service ist ein Web von ressourcen, die programme können navigieren. Beim Entwerfen eines REST-Diensts müssen Sie genau überlegen, wie Ihr Web funktioniert. Dies bedeutet entwerfen Ressource Darstellungen mit Verknüpfungen, die die Navigation, Eingabe irgendwie und berücksichtigen wie Consumer den Dienst zur Laufzeit navigieren werden Dienst beschreibt erleichtern. Diese Dinge rechten Abrufen häufig übersehen, jedoch sind zentrale Realisierung das volle Potenzial, das Rest zu bieten hat.
Heute navigieren Menschen Websites mithilfe von Webbrowsern, die wissen, wie HTML und andere beliebten Inhaltstypen zu rendern. HTML enthält die Syntax und Semantik zum Einrichten von Verknüpfungen zwischen Ressourcen (< a > Element) und für beschreiben und Senden von Anwendung Eingaben (<form> und <input> Elemente).
Wenn ein Benutzer auf ein <a> klicktElement in der gerenderten Seite weiß der Browser eine HTTP GET-Anforderung für die Zielressource ausstellen und Rendern der Antwort. Wenn ein Browser < Formular > entdecktElement, er weiß, wie die Formularbeschreibung in einer Benutzeroberfläche darstellen, die der Benutzer ausfüllen und Absenden mit einem kann Get- oder POST-Anforderung. Wenn der Benutzer eine Absenden-Schaltfläche klickt, wird der Browser die Daten codiert und sendet die angegebene Anforderung. Diese beiden Funktionen sind größtenteils für den Erfolg der im Web verantwortlich.
Mithilfe von Verknüpfungen in Verbindung mit der universellen HTTP-Schnittstelle ermöglicht es zum Umleiten von Anforderungen an neue Speicherorte über einen Zeitraum und bestimmte Aspekte der Sicherheit im Handumdrehen ändern, ohne den Clientcode. Eine Standardmethode für Formulare bedeutet, dass Sie können hinzufügen oder input Eigenschaften entfernen und Standardwerte, erneut ohne den Clientcode geändert. Beide Funktionen sind sehr nützlich zum Erstellen von Anwendungen, die Zeit weiterentwickeln.
Die REST-Dienste sollten auch irgendwie diese beiden Funktionen über alle Ressource-Darstellung bereitstellen, die Sie verwenden möchten. Z. B. Wenn Sie eine benutzerdefinierte XML-Dialekt für Ihren Dienst entwerfen, sollten Sie wahrscheinlich Lieferumfang Einrichten Ihrer eigenen Elemente für Verknüpfungen einrichten und beschreiben Dienst Eingaben, die Kunden über Ihre Website führt. Oder Sie können einfach XHTML verwenden.
Die meisten Entwickler nicht sofort XHTML als eine Option für "Dienste", in Betracht ziehenaber das ist tatsächlich eine der Möglichkeiten für ihn vorgesehen wurde. XHTML-Dokumente sind definitionsgemäß wohlgeformtes XML, die automatische Verarbeitung mithilfe von standardmäßigen XML-APIs ermöglicht. Und da XHTML auch HTML ist, es kommt mit < a >, < Formular > und < input >Elemente für die Modellierung Link Navigations- und Dienst eingeben wie zuvor beschrieben. Das einzige, was, das etwas seltsam an zuerst ist wie benutzerdefinierte Datenstrukturen modellieren, jedoch können Sie modellieren, Klassen und Felder mit < Div >und < span >Elemente und Auflistungen von Entitäten mit < Ol >und < li >Elemente. Ich durch dazu weiter unten in Artikel ausführlicher alle werde nötig sind.
Um zusammenzufassen, gibt es mehrere Gründe, XHTML als Standard-Darstellung für den Rest-Dienste in Betracht ziehen. Erstens können Sie die Syntax nutzen und Semantik für wichtige Elemente wie < a >, < Formular > und < input >anstelle von inventing eigene. Zweitens müssen Sie mit Diensten landen, die viel wie Sites fühlen, da Sie browsebar von Benutzern und Anwendungen werden. XHTML-wird noch von einer Person interpretiert, es ist lediglich ein Programmierer während der Entwicklung anstelle eines Benutzers zur Laufzeit. Dies vereinfacht Dinge im gesamten Entwicklungsprozess und erleichtert es Kunden erfahren Sie, wie der Dienst funktioniert. Und schließlich können Sie standard Web Development Frameworks, erstellen die Rest-Dienste nutzen.
XML-Webdienste MVC ist eine solche Framework, die ein grundsätzlich Rest Modell zum Erstellen von XHTML-basierte Dienste bereitstellt. In diesem Artikel einige XHTML-Entwurfskonzepten durchläuft und zeigt dann zum Erstellen eines vollständigen XHTML-basierte REST-Diensts, das Sie von der Website MSDN Magazin herunterladen können.

XHTML: Darstellung von Daten und links
Bevor ich die Einzelheiten von XML-Webdienste MVC eintauchen, zunächst sehen wir uns wie allgemeine Datenstrukturen und Auflistungen in XHTML darstellen kann. Dieser Ansatz ist nicht die einzige Möglichkeit, dies zu erreichen, aber es ist relativ allgemein üblich in XHTML-basierten Diensten heute.
In diesem Artikel werde ich zum Implementieren eines Dienstes einfache Textmarke beschreiben. Der Dienst ermöglicht Benutzern erstellen, abrufen, aktualisieren, und Löschen von Textmarken und navigieren Sie ein Web von Lesezeichen in einer Vielzahl von Möglichkeiten. Genommen Sie an, Sie haben eine C#-Klasse darstellt, eine Textmarke, die wie folgt aussieht:
public class Bookmark
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Url { get; set; }
    public string User { get; set; }
}
Ist die erste Frage, wie Sie eine Textmarke Instanz in XHTML darstellen können? Ein Ansatz besteht darin kombinieren < Div >, < span > und < >Elemente, wobei < Div >Elemente Strukturen darstellen, < span >Elemente repräsentieren Felder und < >Elemente werden Identität und Verknüpfungen zu anderen Ressourcen darstellen. Zusätzlich können Sie diese Elemente mit XHTML "Klasse" versehen.Attribut, das zusätzliche Typmetadaten bereitzustellen. Hier ist ein vollständiges Beispiel:
<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>
Ist die nächste Frage, verarbeitet der Consumer für diese Informationen? Da es wohlgeformtes XML Consumer können beliebige XML-API die Textmarke Informationen extrahieren. Die meisten .NET Programmierer werden wahrscheinlich feststellen, dass XLinq das am häufigsten natürliche Programmiermodell für XHTML programmgesteuert verbrauchen bietet. Darüber hinaus können Sie eine Schritt weitere wechseln, indem erweitern XLinq mit einige hilfreiche XHTML ausgerichteten Erweiterungsmethoden, die das Programmiermodell noch leichter machen.
In diesem Artikel werde ich eine Reihe von XLinq Erweiterungsmethoden verwenden, die ich im herunterladbaren Beispielcode enthalten haben. Diese Erweiterungen erhalten Sie eine gute Vorstellung von was möglich ist. Der folgende Code zeigt wie das Lesezeichen XHTML, die zuvor mit XLinq und einige der Erweiterungen angezeigt:
var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");
Console.WriteLine(bookmarkDetails["bookmark-id"].Value);
Console.WriteLine(bookmarkDetails["bookmark-url"].Value);
Console.WriteLine(bookmarkDetails["bookmark-title"].Value);
Nun, wenn verbessern, wie dieses Lesezeichen in einem Browser gerendert werden soll, können Sie hinzufügen ein Cascading Stylesheet (CSS) zum Steuern von Rendering der browserspezifischen Details oder einige zusätzliche UI-Elementen und Text, die der Consumer Möglichkeit zum Extrahieren der Daten von Interesse gefährden nicht hinzufügen. Beispielsweise werden folgende XHTML einfacher für Menschen zum verarbeiten, jedoch noch können Sie das vorherige Codebeispiel .NET um Informationen ohne jegliche Änderung zu verarbeiten:
<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>
Auflistungen von Ressourcen nicht schwer zu entweder Modell. Sie können eine Liste der Textmarken mit einer Kombination von < Ol > darstellen < li > und < >Elemente wie hier gezeigt:
<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>
Der folgende Code veranschaulicht die Liste der Textmarken in der Konsole zu drucken:
var bookmarks = bookmarksDoc.Body().Ol("bookmark-list").Lis();
bookmarks.Each(bm => Console.WriteLine("{0}: {1}",
    bm.Anchor().AnchorText, bm.Anchor().AnchorLink));
Beachten Sie, wie jede < li >enthält eine < a >Element, das mit einer bestimmten Textmarke verknüpft. Wenn Sie eines der Anchor-Elemente zu navigieren, würden Sie die Textmarke Details Darstellung zuvor gezeigten abrufen. Sie beginnen, Verknüpfungen zwischen Ressourcen wie folgt definiert, startet der Dienst immer ein Web von verknüpften Ressourcen.
Ist es ziemlich offensichtlich, wie Menschen zwischen Ressourcen einen Webbrowser verwenden, aber wie verbrauchen Anwendungen wechseln können? Eine verbrauchende Anwendung muss nur programmgesteuert suchen Sie das Anchor-Element von Interesse und anschließend eine GET-Anforderung Zielgruppenadressierung in "Href" angegebene URIDas Attribut. Diese Details können auch hinter eine Erweiterungsmethode XLinq ausgeblendet werden, die Anchor Navigation kapselt.
Der folgende Code veranschaulicht die auf die erste Textmarke in der Liste Textmarke und dann auf die Textmarke Ziel-URL zu navigieren. Die resultierende XHTML wird an die Konsole gedruckt:
var bookmarkDoc = bookmarks.First().Anchor().Navigate();
var bookmarkDetails = bookmarkDoc.Body().Struct("bookmark");
var targetDoc = bookmarkDetails.Anchor("bookmark-url-link").Navigate();
Console.WriteLine(targetDoc);
Sobald Sie denken zum Erstellen von Consumern, die den Dienst als Web von Ressourcen zu navigieren, beginnen Sie sind offiziell in eine weitere Rest Möglichkeit vorstellen.

XHTML: Eingabe mit darstellt
Nachdem wir sagen ein Consumer ein neues Lesezeichen im System erstellen möchte. Wie der Consumer ermitteln, welche Daten senden und wie ohne WSDL zu senden? Die Antwort ist einfach: XHTML-Formulare.
Der Consumer stellt eine GET-Anforderung zuerst der URI zum Abrufen von Formular Lesezeichen erstellen. Der Dienst gibt ein Formular, die etwa folgendermaßen aussieht:
<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>
Das Formular beschreibt, wie eine HTTP POST-Anforderung für ein neues Lesezeichen erstellen erstellt wird. Das Formular gibt an, dass Sie die Lesezeichen-Titel und Url Textmarke Felder bieten müssen. In diesem Beispiel Lesezeichen-Id wird automatisch während der Erstellung und Lesezeichen-Benutzername wird von die Identität des angemeldeten Benutzers abgeleitet werden. Das Formular teilt Sie auch, um senden, benötigen Sie und wie Sie Sie senden.
Wenn dieses Formular in einem Browser gerendert wird, kann einfach einen Menschen füllen Sie das Formular und klicken Sie auf senden, um ein neues Lesezeichen erstellen. Eine verbrauchende Anwendung wird im Wesentlichen das gleiche Absenden des Formulars programmgesteuert. Erneut kann diesen Vorgang einfacher mithilfe erstellt werden einige formularbasierte Erweiterungsmethoden, die hier gezeigte:
var createBookmarkForm = createBookmarkDoc.Body().Form("create-bookmark-  form");
createBookmarkForm["bookmark-title"] = "Windows Live";
createBookmarkForm["bookmark-url"] = "http://live.com/";
createBookmarkForm.Submit();
Wenn dieser Code ausgeführt wird, generiert die Submit-Methode eine HTTP POST-Anforderung "Aktion" ZielgruppenadressierungURL und die Eingabefelder werden zusammen als eine URL-codierte Zeichenfolge (Application/X-Www-Form-Urlencoded) formatiert. Am Ende ist es nicht anders mithilfe des Browsers, das Ergebnis ist ein neues Lesezeichen.
Obwohl heutigen Browsern nur für die Formularmethode GET und POST unterstützen, wird nichts Sie von der Angabe auch PUT beendet oder das Formular "Methode" DELETEWenn Nonbrowser Consumer abzielt. Die Übermittlung Erweiterung-Methode führt gleichermaßen gut für alle HTTP-Methode Sie angeben.

Informationen zum Verständnis der ASP.NET MVC-Architektur
Die XML-Webdienste MVC-Architektur basiert auf das beliebte Modell-Ansicht-Controller-Entwurfsmuster, die für die Jahrzehnte um wurde. Abbildung 1 veranschaulicht die verschiedenen XML-Webdienste MVC-Komponenten und deren Beziehungen zu anderen. Im Lieferumfang von XML-Webdienste MVC ist ein Routingmodul, die vor der anderen MVC-Komponenten befindet. Das Routingmodul empfängt eingehende HTTP-Anforderungen und leitet diese an eine Controller-Methode. Das Routingmodul beruht auf einem zentralen Satz von Routen, die Sie in der Datei Global.asax definieren.
Abbildung 1 XML-Webdienste MVC Architektur
Die zentralisierten Routen definieren Zuordnungen zwischen URL-Muster und bestimmte Domänencontroller Methoden und Argumente. Beim Generieren von Hyperlinks verwenden Sie diese Routen, um die Verknüpfungen entsprechend zu generieren. Dies erleichtert den URL-Entwurf im gesamten Entwicklungsprozess an einem zentralen Ort ändern.
Es ist die Auftrag des Domänencontrollers, um Informationen aus der eingehenden Anforderung zu extrahieren und die user-defined Modell Schicht interagieren. Die Modell-Schicht kann nichts (XML-Webdienste SQL, ADO.NET Entity Framework, NHibernate und usw.) – Dies ist die Ebene, die Geschäftslogik ausgeführt und kommuniziert mit der zugrunde liegenden Datenbank. Beachten Sie, wie das Modell nicht innerhalb des System.Web.Mvc-Namespace ist. Nach der Controller mithilfe des Modells wird eine Ansicht, Angabe der Ansicht mit Modelldaten für die Ansicht verwenden, während die Ausgabe rendern erstellt.
In den folgenden Abschnitten werde ich durch alle Schritte implementieren einen vollständigen Textmarke Dienst XML-Webdienste MVC-Architektur, die nötig. Der Dienst unterstützt mehrere Benutzer und öffentlichen und privaten Lesezeichen. Benutzer können wechseln, alle öffentlichen Lesezeichen und Filtern Sie anhand der Benutzername oder Tags, und Sie können eigene Sammlung von privaten Lesezeichen (CRUD) vollständig verwalten.
Um zu Beginn müssen Sie ein XML-Webdienste MVC-Projekt erstellen. Finden Sie XML-Webdienste MVC Webanwendung Projekt Vorlage in der Liste der Web-Projekttypen. Die Standard-Projekt Vorlage bietet Ihnen eine MVC Starter Beispielanwendung auf, der Sie tatsächlich führen können, indem Sie F5 drücken.
Beachten Sie, wie die Lösungsstruktur Verzeichnisse für Modelle, Ansichten und Domänencontroller bereitstellt – Dies ist, platzieren Sie den Code für diese verschiedenen Komponenten. Standard-Vorlage wird mit zwei Domänencontrollern geliefert: eine für das Verwalten von Benutzerkonten (AccountController) und andere für die Unterstützung der Anforderungen an das Basisverzeichnis (HomeController). Beide werden in den Lesezeichen-Dienst verwendet.

Implementieren des Modells
Als Erstes auf konzentrieren sollten ist das Modell für den Dienst Textmarke. Ich eine einfache SQL Server-Datenbank mit drei Tabellen zum Verwalten von Lesezeichen-Informationen erstellt haben – Lesezeichen, Tags und BookmarkTag (siehe Abbildung 2), und Sie sind relativ selbsterklärend.
Abbildung 2 Lesezeichen Service LINQ to SQL-Modell
Der einzige Nachteil ist, dass das Beispiel stützt sich auf den integrierten ASP.NET Formulare Authentifizierungs- und Mitgliedschaft Dienst, der standardmäßig AccountController, die im Lieferumfang des Projekts enthalten ist bereitgestellt wird, den Service-Benutzer zu verwalten, daher Konten Benutzerkonto Informationen in einer anderen Datenbank (aspnetdb.mdf), getrennt von der Textmarke Informationen gespeichert werden. Der Benutzername ist einfach in der Tabelle Lesezeichen gespeichert.
Es ist die Auftrag des Modells Geschäftsobjekte und Logik on top of der Datenbank bereitstellen. In diesem Beispiel habe ich definiert die XML-Webdienste SQL-Modell im Abbildung 2 angezeigt. Dieses Modell, definiert in BookmarksModel.dbml, erzeugt der C#-Code, die in BookmarksModel.designer.cs gefunden. Hier finden Sie Klassen mit dem Namen Lesezeichen, Tags und BookmarkTag. Finden Sie auch eine BookmarksModelDataContext-Klasse, die die Entitäten verknüpft.
Zu diesem Zeitpunkt können Sie direkt mit dem LINQ to SQL-Klassen als der MVC-Modell-Ebene arbeiten, oder Sie können eine Schritt weitere wechseln, indem eine auf höherer Ebene Repository-Klasse, die logische Geschäftsvorgänge definiert und abschirmt Controller/Ansicht aus der zugrunde liegenden Daten Manipulation Details noch mehr definieren. Abbildung 3 zeigt die Definition für die BookmarksRepository-Klasse, die in den Lesezeichen-Dienst verwendet.
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() { ... }
}

Implementieren des Controllers
Der Controller ist der Teil des Codes für die Verwaltung des Lebenszyklus von HTTP-Anforderung zuständig ist. Wenn eine Anforderung eintrifft, wird das XML-Webdienste MVC-Routingmodul bestimmt die zu verwendenden Controller (basierend auf den URL) und leitet dann die Anforderung an den Controller durch Aufrufen von einer bestimmten-Methode. Beim Schreiben von eines Domänencontrollers sind Sie daher die Einstiegspunkte schreiben, die vom Routingmodul aufgerufen wird.
Für den Dienst Textmarke möchten wir authentifizierte Benutzern erstellen, bearbeiten und Löschen von Textmarken ermöglichen. Wenn Lesezeichen zu erstellen, sollten Benutzer um Sie als Public (freigegeben) oder Private markieren können. Alle Benutzer sollten möglicherweise öffentliche Lesezeichen durchsuchen und Filtern Sie nach Benutzernamen oder Tags. Private Lesezeichen sollte jedoch nur für den Besitzer sichtbar sein. Verbraucher sollten auch Lage Abrufen der Details für eine bestimmte Textmarke vorausgesetzt Sie dazu autorisiert sind. Wir sollten auch durchsuchen alle Benutzer und -Tags im System als eine Möglichkeit, die öffentlichen Lesezeichen zugeordnet navigieren können.
Abbildung 4 zeigt die Methoden die BookmarkController-Klasse muss die Anforderungen unterstützen beschriebenen. Die ersten drei Abfragemethoden ermöglichen das Abrufen aller öffentlichen Lesezeichen, Lesezeichen durch Benutzer oder Lesezeichen nach Tag. Die Klasse enthält auch Methoden zum Abrufen der Benutzer und -Tags und zum Abrufen der Details einer bestimmten Textmarke-Instanz. Alle diese Methoden auf HTTP GET-Anforderungen reagieren, aber jeweils wird an einen anderen URI-Vorlage gebunden werden, wenn die Routen definiert sind.
[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) { ... }
}
Die verbleibenden Methoden handelt es sich zum Erstellen, bearbeiten und Löschen von Lesezeichen-Instanzen. Beachten Sie, dass zwei Methoden für jedes logische Operation sind – eine für das Abrufen im Eingabeformular und andere für die Reaktion auf die Übertragung des Formulars – und jeweils dieser Methoden erfordert Autorisierung. Das Autorisieren Attribut gewährleistet, dass der Aufrufer authentifiziert und autorisiert, auf die Domänencontroller-Methode zuzugreifen. (Das Attribut ermöglicht auch Sie Benutzer und Rollen angeben.) Wenn ein nicht authentifizierter oder nicht autorisierter Benutzer, versucht auf eine Controller-Methode, mit [autorisieren] Anmerkungen zuzugreifen, leitet der Autorisierungsfilter den Benutzer automatisch an die AccountController-Anmeldung-Methode, die "Logon" stelltan den Consumer anzeigen.
Sie das AcceptVerbs-Attribut verwenden, um die HTTP-Verben anzugeben, die eine bestimmter Domänencontroller-Methode behandelt (der Standardwert ist GET). Eine einzelne Methode kann mehrere Verben durch OR zusammen HttpVerb Werte behandeln. Der Grund habe ich die zweite bearbeiten-Methode sowohl einfügen und POST gebunden ist um Browsern aufzunehmen. Diese Konfiguration ermöglicht Browsern rufen Sie den Vorgang mit POST, während Nonbrowser Consumer PUT verwenden können (was mehr richtigen ist). Ich haben ungefähr auf der zweiten Methode löschen es sowohl DELETE als auch POST binden. Ich habe vorsichtig sein, dass meine Methodenimplementierungen Idempotency, Sicherstellen der ist eine Voraussetzung für PUT und DELETE.
Sehen Sie sich wie ein paar Methoden implementiert wurden. Zunächst ist BookmarkIndex:
public ActionResult BookmarkIndex()
{
    var bms = bmRepository.FindAllPublicBookmarks().ToList();
    return View("BookmarkIndex", bms);
}
Diese Implementierung ruft einfach die Liste der öffentlichen Lesezeichen Entitäten aus dem Repository ab und gibt dann eine Ansicht namens BookmarkIndex (in der Liste Textmarke Entitäten übergeben) zurück. Die Ansicht ist verantwortlich für die Anzeige der Textmarke Entitäten, die vom Controller bereitgestellt.
Die Details-Methode sucht die Ziel-Textmarke und gibt Fehler 404 Not Found zurück, wenn er nicht vorhanden ist. Und es wird sichergestellt, dass der Benutzer zum Anzeigen der Textmarke autorisiert ist. Wenn dies der Fall ist, gibt es die Detailansicht, Angabe der identifizierten Lesezeichen Entität zurück. Andernfalls wird eine nicht autorisierte Antwort an den Consumer zurückgegeben.
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);
}
Als letzten Beispiel betrachten die beiden Methoden erstellen. Der erste ist tatsächlich ziemlich einfach – wird die Ansicht erstellen, um die Formularbeschreibung für das Erstellen eines neuen Lesezeichens vorhanden:
[Authorize]
public ActionResult Create()
{
    return View("Create");
} 
Abbildung 5 zeigt die zweite erstellen-Methode, die auf die Formular absenden Anforderung reagiert. Erstellt eine neue Textmarke Entität aus den Lesezeichen-Informationen in das eingehende FormCollection-Objekt gefunden und speichert es in der Datenbank. Es wird die Datenbank auch mit jeder neuen Tags, die die Textmarke zugeordnet waren aktualisiert, und leitet Benutzer auf Ihre Listen persönliche Lesezeichen um Erfolg anzuzeigen.
[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");
    }
}
Es nicht Speicherplatz für die gesamte Controller-Implementierung abgedeckt ist, aber diese Codebeispiele sollten Ihnen einen Eindruck für die Art von Code in der Steuerung schreiben.

Entwerfen von URIs mit Routen
Der nächste Aspekt Sie müssen ist URL-Routen definieren, die die verschiedenen BookmarkController Methoden zugeordnet. Ihre Anwendung Routen werden in Global.asax definiert, innerhalb der RegisterRoutes-Methode. Wenn Sie zuerst ein MVC-Projekt erstellen, enthält Ihr Global.asax den routing Standardcode in Abbildung 6 angezeigt.
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);
    }
}
Der einzige Aufruf MapRoute erstellt eine Standard-Routingregel, die wie ein Catchall für alle URIs fungiert. Diese Route werden, dass das erste Pfadsegment den Controllernamen gibt, des zweiten Pfadsegments den Namen Aktion (Domänencontroller-Methode gibt) und dritten Pfadsegment stellt einen ID-Wert dar beschrieben. Diese Regel kann behandelt die folgenden URIs und leiten diese an die entsprechenden Domänencontroller-Methode:
/Account/Logon
/Bookmark/Create
/Bookmark/Details/25
/Bookmark/Edit/25
Abbildung 7 zeigt den Routen ich beispielsweise Dienst dieses Lesezeichen mit bin. Mit dieser zusätzlichen Routen können Consumer wechseln Sie zu "/ Benutzer"Abrufen die Liste von Benutzern, "/ tags"Abrufen die Liste der Tags, oder "/ Lesezeichen"Um die Liste der öffentlichen Lesezeichen abzurufen. Verbraucher können auch auf "/tags/ {Tagname}" suchen.oder "/users/ {Benutzername}"Um Lesezeichen nach Tag oder Benutzername, bzw. zu filtern. Alle anderen URIs werden von der Standardroute im Abbildung 6 angezeigten behandelt.
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 = ""});
}

Implementieren der Ansichten
Bis zu diesem Zeitpunkt gilt die meisten wir haben für beide MVC "Sites"und "Dienste". Alle MVC-Anwendungen benötigen Modelle, Domänencontroller und Routen. Die meisten unterschiedliche zum Erstellen von MVC "Dienste Neuigkeiten"befindet sich in der Ansicht. Statt eine herkömmliche HTML-Ansicht für Benutzer zu erstellen, muss ein Dienst eine Sicht erstellen, die für Menschen und programmgesteuerte Verbrauch geeignet ist.
Wir XHTML für unsere Standard Service Darstellung und die für die Zuordnung Textmarke Daten zu XHTML beschriebenen Techniken anwenden. Wir müssen Datenentitäten, < Div > zuordnen.und < span >Elemente und wir werden Auflistungen mithilfe einer Kombination aus < Ol > darstellen.und < li >. Wir müssen auch diese Elemente mit "Klasse" versehen.Attribut, das zusätzliche Typmetadaten Consumer bereitzustellen.
XML-Webdienste MVC "Ansichten"sind Sie gerade ASPX-Seiten, die eine Ansicht Vorlage definieren. Die .aspx-Seiten sind nach Name des Domänencontrollers innerhalb des Verzeichnisses Ansichten angeordnet. Jede Ansicht kann eine ASP.NET-Masterseite zum Verwalten eines konsistenten Vorlage zugeordnet werden. Abbildung 8 zeigt die master Seite für den Dienst Textmarke.
<!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>
Die Masterseite definiert drei Platzhaltern: eine für den Seitentitel, ein weiteres für < h1 >Überschrift, und andere für den Hauptinhaltsbereich. Diese Platzhalter werden durch jede einzelne Ansicht gefüllt werden. Darüber hinaus die Masterseite zeigt ein Anmeldesteuerelement am oberen Rand der Seite und bietet eine Fußzeile enthält die Stamm Dienst Verknüpfungen zum Vereinfachen der Navigation. Beachten Sie, wie ich die HTML.ActionLink-Methode verwende diese Verknüpfungen basierend auf den vordefinierten Routen und Controller-Aktionen zu generieren.
Abbildung 9 zeigt die wichtigste Lesezeichen Index-Ansicht, die Sie wieder, erhalten Wenn Sie wechseln zu "/bookmarks". Es zeigt die Liste der Textmarken, die mithilfe einer Kombination aus <ol>, <li> und <a> Elemente. <ol> Elemente werden mit-Klasse Anmerkungen = "Lesezeichen-Liste", und jede <a> Element ist mit-Klasse Anmerkungen = "Lesezeichen-Link". Diese Ansicht bietet auch eine Verknüpfung zu die Formularbeschreibung Textmarke erstellen (rechts über der Liste) abrufen. Wenn Sie auf die Verknüpfung navigieren, stammt die erstellen-Ansicht in Abbildung 10 dargestellt in Aktion.
<%@ 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>
<%@ 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>
Die Ansicht erstellen erstellt ein einfaches XHTML-Formular, aber das < Formular >Element ist mit-Klasse Anmerkungen = "erstellen-Textmarke-Form", und jede < >Element hat einen kontextbezogenen Namen-ID-Wert zugewiesen wurde, der jedes Textmarkenfeld gibt. Dieses Formular bietet Consumer eine vollständige Beschreibung XHTML wie programmgesteuert ein neues Lesezeichen über unseren Dienst (indem Sie einfach das Formular absenden) erstellt.
Abbildung 11 zeigt als letzte beispielsweise den Anfang der Textmarke Detailansicht. Hier verwende ich ein < Div >Element, um die Textmarke Struktur darzustellen (Klasse = "Lesezeichen") zusammen mit < Span >und < >Elemente, die die Textmarke Felder darstellen. Jeder führt "Klasse"Attribut, das den Feldnamen des angeben.
<%@ 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 />
...
Auch nicht genug Speicherplatz für alle Ansicht Beispiele im Detail betrachten, aber ich hoffe, dies veranschaulicht, wie saubere XHTML-Resultsets erzeugen können, die leicht für Anwendungen und Menschen nutzen sind.

Verarbeiten des Bookmark-Dienstes
Lesezeichen-Dienstes am einfachsten über einen Webbrowser werden. Dank an den XHTML-Entwurf sollten Sie möglicherweise wechseln Sie zu der Dienst Stamm-URL und Navigation von dort aus zu starten. Abbildung 12 zeigt, wie der Browser beim Navigieren Sie zum Stammverzeichnis des Lesezeichens-Dienstes und melden Sie sich aussieht. Sie können Öffentliche Lesezeichen zu der Liste der öffentlichen alle Lesezeichen navigieren und navigieren Sie zu einer bestimmten Textmarke in der Liste klicken. Wenn Sie auf Bearbeiten klicken, können Sie tatsächlich die Textmarke Details bearbeiten (siehe Abbildung 13). Der Dienst ist vollständig in einem beliebigen Webbrowser verwendet werden.
Abbildung 12 dem MVC Lesezeichen Service durchsuchen
Abbildung 13 bearbeiten eine bestimmte Textmarke
Während Sie, um den Dienst aktivieren Datenquelle gelegentlich in Ihrem Browser durchsuchen gerade, und Sie sehen, wie einfach die resultierende XHTML sucht, wodurch erneut zum Programmieren mit erleichtert.
Abbildung 14 zeigt den Code für eine vollständige .NET Clientanwendung, die den Lesezeichen-Dienst nutzt. Er verwendet den Satz von XLinq Erweiterungsmethoden, die zuvor zum Vereinfachen der XHTML und HTTP-Verarbeitungsdetails beschrieben. Das interessante an diesem Beispiel ist, dass es mehr wie ein Mensch fungiert – benötigt nur den Stamm-URI Navigieren zu ansonsten von Interesse von Lesezeichen-Dienst verfügbar gemacht werden.
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"] = "http://live.com/";
        createBookmarkForm["bookmark-tags"] = "Microsoft, Search";
        createBookmarkForm.Submit();
        Console.WriteLine("\nBookmark created!");
    }
}
Der Client beginnt durch navigieren zu der Stamm-Adresse, und sucht dann für die Verknüpfung zu der öffentlichen Lesezeichen. Nächstes navigiert zur Liste öffentlichen Lesezeichen und identifiziert eine bestimmte Textmarke Zinsen (in diesem Fall die erste Datei). Als Nächstes es navigiert zu der Textmarke Details und im Konsolenfenster angezeigt. Anschließend ruft das Anmeldeformular und führt eine Anmeldung mit einem Satz von Anmeldeinformationen. Einmal angemeldet, wird die Anwendung ruft Formular Textmarke erstellen, füllt es aus und ein neues Lesezeichen an das System sendet.
Es gibt einige wichtige Beobachtungen zu diesem Zeitpunkt. Zunächst ist die Konsolenanwendung in der Lage auf diese Weise alles, was einen Menschen über den Webbrowser ausführen kann. Das ist der diesem Design XHTML-killer Funktion. Zweitens müssen Consumer nur gegen den Stamm hartcodierte URIs durch den Dienst verfügbar gemacht werden. Alle anderen URIs werden zur Laufzeit durch navigieren Links innerhalb der XHTML gefunden sichtbar. Und schließlich XHTML-Strukturen verarbeitet, viel anders etwas – es ist nur die Daten. Plus, diese Art von Code ruft nur einfacher, Sie gegen dynamischer Sprachen in zukünftigen Versionen von .NET verschieben.
Schließlich bietet XML-Webdienste MVC ein grundsätzlich Rest Framework für implementieren ein Web von XHTML-basierte Ressourcen, die von Menschen und Anwendungen gleichzeitig verwendet werden können. Sie können die gesamte Beispielanwendung in diesem Artikel aus der MSDN Magazin-Website downloaden.
Ein weiteres vollständiges Beispiel für ein XHTML-basierte REST-Diensts in der realen Welt gefunden Sie, suchen Sie die Content Services von Microsoft-TechNet Publishing System (MTPS). Dieser Dienst verwendet viele der Methoden, die ich in diesem Artikel skizziert haben.

Danksagung
Dank Tim Ewald und Craig Andera bereitgestellt, deren kreative Denken in diesem Bereich Treibstoff für meine Artikel. Tim bereitgestellt auch die XLinq Erweiterungsmethoden in der zugehörigen Beispielanwendung gefunden.

Aaron Skonnard ist Mitbegründer von Pluralsight, einem Microsoft-Schulung-Anbieter beide Trainern und on-Demand-Entwickler-Kurse anbieten. Heutzutage verbringt er Großteil seiner Zeit aufzeichnen Pluralsight bei Bedarf! Kurse auf Wolke Computing, Windows Azure, WCF und REST konzentriert. Sie erreichen ihn unter http://pluralsight.com/aaron und http://twitter.com/skonnard.

Page view tracker