UNIT TEST: HTTP STATUS CODES

Một phần của tài liệu Giáo trình lập trình ASP.NET Apress pro ASP NET MVC3 framework pre release (Trang 397 - 404)

The HttpStatusCodeResult class follows the pattern we have seen for the other result types, and makes its state available through a set of properties. In this case, the StatusCode property returns the numeric HTTP status code and the StatusDescription property returns the associated descriptive string. The following test method is for the action method in Listing 32:

[TestMethod]

public void StatusCodeResultTest() { // Arrange - create the controller

ExampleController target = new ExampleController();

// Act - call the action method

HttpStatusCodeResult result = target.StatusCode();

// Assert - check the result

Assert.AreEqual(404, result.StatusCode);

}

Creating a Custom Action Result

The built-in action result classes are sufficient for most situations and applications, but we can create our own custom action results for those occasions where we need something special. In this section we’ll demonstrate a custom action result that generates an RSS document from a set of objects – RSS is a format commonly used to publish frequently updated collections of items, such as headlines, to interested subscribers. Listing 34 shows our RssActionResult class.

Listing 34. Creating a custom action result using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using System.Xml.Linq;

namespace ControllersAndActions.Infrastructure { public abstract class RssActionResult : ActionResult { }

public class RssActionResult<T> : RssActionResult { public RssActionResult(string title, IEnumerable<T> data, Func<T, XElement> formatter) {

Title = title;

DataItems = data;

Formatter = formatter;

}

public IEnumerable<T> DataItems { get; set; } public Func<T, XElement> Formatter { get; set; } public string Title { get; set; }

public override void ExecuteResult(ControllerContext context) { HttpResponseBase response = context.HttpContext.Response;

// set the content type of the response

response.ContentType = "application/rss+xml";

// get the RSS content

string rss = GenerateXML(response.ContentEncoding.WebName);

// write the content to the client response.Write(rss);

}

private string GenerateXML(string encoding) {

XDocument rss = new XDocument(new XDeclaration("1.0", encoding, "yes"), new XElement("rss", new XAttribute("version", "2.0"),

new XElement("channel", new XElement("title", Title), DataItems.Select(e => Formatter(e)))));

return rss.ToString();

} } }

In fact, we have defined two classes – the first is an abstract class called

RssActionResult, which subclasses ActionResult. The second is a strongly-typed class classed RssActionResult<T> which is derived from RssActionResult. We have defined two classes so that we can create action methods that return the abstract RssActionResult class but which create instances of the strongly-typed subclass – it just makes our action result easier to use and produces more readable action methods.

The constructor of our custom action result, RssActionResult<T>, takes three parameters – the title for the RSS document we will generate, the set of data items that the document will contain, and a delegate which will be used to transform each data item into an XML fragment for inclusion in the RSS output.

Tip Notice that we have exposed the title, data items and delegate as public properties – this is to enable easy unit testing so that we can determine the status of the result without having to call the ExecuteResult method.

To derive from the abstract ActionResult class, we must provide an implementation of the ExecuteResult method - ours uses LINQ and the XDocument API to generate an RSS document, which is written to the Response object which accessible through the

ControllerContext parameter. Listing 35 shows an action method that uses our custom action result.

Listing 35. Using a custom action result public RssActionResult RSS() { StoryLink[] stories = GetAllStories();

return new RssActionResult<StoryLink>("My Stories", stories, e => { return new XElement("Storyitem",

new XAttribute("title", e.Title),

new XAttribute("description", e.Description), new XAttribute("link", e.Url));

});

}

To use our custom action result we simply create a new instance of the strongly-typed class and pass in the required parameters. In this example, we have used the StoryLink class from previous examples and the delegate that we defined (using a Func) generates the XML

required for each story in the dataset. If we start our application and navigate to this action method, we generate an RSS document which is recognized by the browser, as shown in Figure 6.

Figure 6. The output from a custom action result

Summary

Controllers are one of the key building blocks in the MVC design pattern. In this chapter, you have seen how to create “raw” controllers by implementing the IController interface and more convenient controllers by deriving from the Controller class. We saw the role that actions methods play in MVC framework controllers and how they ease unit testing.

In the next chapter, we will go deeper into the controller infrastructure, in order to customize the way that controllers are created and behave so that you can tailor the way that your application behaves.

n n n nn n

Filters

In this chapter, you’ll see how we can use filters to inject extra logic into the request processing pipeline, and how we can customize the mechanisms for locating and instantiating controllers and invoking their action methods.

Filters are a simple and elegant way to implement cross-cutting concerns – a term that refers to functionality that gets used all over an application and doesn’t fit neatly into any one place and would therefore break our separation of concerns pattern. Classic examples of cross- cutting concerns are logging, authorization and caching.

Filters are so-called because the same term is used for the same facility in other web application frameworks, including Ruby on Rails. However, MVC framework filters are entirely different from the ASP.NET platform’s Request.Filter and Response.Filter objects. You can use Request.Filter and Response.Filter in an MVC application (these objects perform transformations on the request and response streams – an advanced and infrequently performed activity), but in general, when an ASP.NET MVC programmer speaks of filters, it is the topic of this chapter that is being referred to.

In this chapter, we will show you the different categories of filter that the MVC framework supports, how to create and use filters and how to control their execution.

Using Filters

We have already seen an example of a filter in Chapter 9, when we applied authorization to the action methods of the SportsStore administration controller. We wanted the action method only to be used by users who had authenticated themselves, which presented is us with a choice. We could have checked the authorization status of the request in each and every action method, as shown in Listing 1.

Listing 1. Explicitly checking authorization in action methods namespace SportsStore.WebUI.Controllers {

public class AdminController : Controller {

// ... instance variables and constructor public ViewResult Index() {

if (!Request.IsAuthenticated) {

FormsAuthentication.RedirectToLoginPage();

}

// ...rest of action method }

public ViewResult Create() { if (!Request.IsAuthenticated) {

FormsAuthentication.RedirectToLoginPage();

}

// ...rest of action method }

public ViewResult Edit(int productId) { if (!Request.IsAuthenticated) {

FormsAuthentication.RedirectToLoginPage();

}

// ...rest of action method }

// ... other action methods }

}

You can see that there is a lot of repetition in this approach, which is why we decided to use a filter, as shown in Listing 2.

Listing 2. Applying a filter

namespace SportsStore.WebUI.Controllers { [Authorize]

public class AdminController : Controller { // ... instance variables and constructor public ViewResult Index() {

// ...rest of action method }

public ViewResult Create() { // ...rest of action method }

public ViewResult Edit(int productId) {

// ...rest of action method }

// ... other action methods }

}

Filters are .NET attributes that add extra steps to the request processing pipeline. We used the Authorize filter in Listing 2, which has the same effect as all of the duplicated checks in Listing 1.

Một phần của tài liệu Giáo trình lập trình ASP.NET Apress pro ASP NET MVC3 framework pre release (Trang 397 - 404)

Tải bản đầy đủ (PDF)

(603 trang)