UNIT TEST: CONTENT RESULTS

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 390 - 395)

Testing action methods that return ContentResult is reasonably simple, as long as we are able to meaningfully compare the text in the test method. Here is a simple test for the action method shown in Listing 24:

[TestMethod]

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

ExampleController target = new ExampleController();

// Act - call the action method ContentResult result = target.Index();

// Assert - check the result

Assert.AreEqual("text/plain", result.ContentType);

Assert.AreEqual("This is plain text", result.Content);

}

The ContentResult.Content property provides access to the content contained by the result, and the ContentType property can be used to get the MIME type for the data. The ContentResult class also defines a ContentEnconding property, but this is often omitted from unit tests because the value is often determined by the MVC framework based on information provided by the user’s browser.

Returning XML Data

Returning XML data from an action method is very simple, especially when we are using LINQ to XMSQL and the XDocument API to generate XML from objects. Listing 26 provides a demonstration.

Listing 26. Generating XML in an action method public ContentResult XMLData() {

StoryLink[] stories = GetAllStories();

XElement data = new XElement("StoryList", stories.Select(e => { return new XElement("Story",

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

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

}));

return Content(data.ToString(), "text/xml");

}

The StoryLink class that is sued to generate the XML is defined like this:

public class StoryLink{

public string Title {get; set;}

public string Description { get; set; } public string Url { get; set; } }

The result of the action method is an XML fragment:

<StoryList>

<Story title="First example story" description="This is the first example story" link="/Story/1" />

<Story title="Second example story" description="This is the second example story" link="/Story/2" />

<Story title="Third example story" description="This is the third example story" link="/Story/3" />

</StoryList>

If you are not familiar with LINQ to XML and the XDocument API, then it is worthy of investigation and is the simplest and most elegant way to work with XML that we have seen.

Adam covers the topic in depth in his book Pro LINQ in C# 2010.

Returning JSON Data

In recent years, the use of XML documents and XML fragments in web applications has waned, in favor of the JavaScript Object Notation (JSON) format, which is a lightweight, text-based format that describes hierarchical data structures. JSON data is valid JavaScript code, which means that it is natively supported by all the mainstream web browsers, making it more compact and easier to work with than XML. For more details about JSON see www.json.org. JSON is most commonly used to send data from the server to a client in response to AJAX queries, which we’ll see in depth in Chapter 19.

The MVC framework has a built-in JsonResult class which takes care of serializing .NET objects into the JSON format. We can create JsonResult objects using the Controller.Json convenience method, as shown in Listing 27.

Listing 27. Creating JSON data with the JsonResult class [HttpPost]

public JsonResult JsonData() { StoryLink[] stories = GetAllStories();

return Json(stories);

}

This example uses the same StoryLink class we saw previously, but we don’t have to manipulate the data since the serialization is taken care of by the JsonResult class. The response generated by the action method in Listing 27 is as follows:

[{"Title":"First example story",

"Description":"This is the first example story","Url":"/Story/1"}, {"Title":"Second example story",

"Description":"This is the second example story","Url":"/Story/2"}, {"Title":"Third example story",

"Description":"This is the third example story","Url":"/Story/3"}]

We have formatted the JSON data to make it more readable. Don’t worry if you are not familiar with JSON – we will return to this topic in Chapter 19 and provide some detailed examples.

Note For security reasons, the JsonResult will only generate a response for HTTP POST requests – this is to prevent data being exposed to third-parties via cross-site requests (we explain what these are in Chapter XXX21). We like to annotate JSON-generating action methods with the HttpPost as a reminder of this behavior, although this is not essential. We explain how to disable this behavior in Chapter XXX19.

Returning Files and Binary Data

FileResult is the abstract base class for all action results concerned with sending binary data to the browser. The MVC framework comes with three built-in concrete subclasses for you to use:

1. FilePathResult sends a file directly from the server file system.

2. FileContentResult sends the contents of an in-memory byte array.

3. FileStreamResult sends the contents of a System.IO.Stream object that we have already opened.

We don’t have to worry about which of these types we need to use, because they are created for us automatically by the different overloads of the Controller.File helper method. You can see demonstrations of these overloads in the following sections.

Sending a File

Listing 28 demonstrates how to send a file from the disk.

Listing 28. Sending a file

public FileResult AnnualReport() {

string filename = @"c:\AnnualReport.pdf";

string contentType = "application/pdf";

string downloadName = "AnnualReport2011.pdf";

return File(filename, contentType, downloadName);

}

This action method causes the browser to prompt the user to save the file, as shown in Figure 5. Different browsers handle file downloads in different ways – the figure shows the prompt that Internet Explorer 8 presents.

Figure 5. Browser open or save prompt

There are three parameters to the overload of the File method we using in the listing and they are described in Table 3.

Table 3. Parameters Passed to File() When Transmitting a File

Parameter Type Description

filename (required) string The path of the file (in the server’s file system) to be sent.

contentType (required) string The MIME type to use as the response’s content-type header.

The browser will use this MIME type information to decide how to deal with the file. For example, if we specify

application/vnd.ms-excel, then the browser may offer to open the file in Microsoft Excel. Likewise, application/pdf responses

should be opened in the user’s chosen PDF viewer.

fileDownloadName

(optional) string The content-disposition header value to send with the response.

When this parameter is specified, the browser should always pop up a save-or-open prompt for the downloaded file. The browser should treat this value as the file name of the downloaded file, regardless of the URL the file is being downloaded from.

If we omit fileDownloadName and the browser knows how to display the MIME type itself (for example, all browsers know how to display an image/gif file), then the browser will display the file itself.

If we omit fileDownloadName and the browser doesn’t know how to display the MIME type (for example, we might specify application/vnd.ms-excel), then the browser will pop up a save-or-open prompt, guessing a suitable file name based on the current URL (and in Internet Explorer’s case, based on the MIME type you’ve specified). However, the guessed file name will almost certainly make no sense to the user, as it may have an unrelated file name extension such as .mvc, or no extension at all. So, always be sure to specify fileDownloadName when you expect a save-or-open prompt to appear.

Caution The results are unpredictable ff if you specify a fileDownloadName that disagrees with the contentType parameter (e.g., if you specify a file name of AnnualReport.pdf along with a MIME type of application/vnd.ms-excel). If you don’t know which MIME type corresponds to the file you’re sending, you can specify application/octet-stream instead. This means “some unspecified binary file”—it tells the browser to make its own decision about how to handle the file, usually based on the file name extension.

Sending a Byte Array

If we already have binary data in memory, then we can transmit it to the browser using a different overload of the File method, as shown in Listing 29.

Listing 29. Sending a binary array

public FileContentResult DownloadReport() {

byte[] data = ... // Generate or fetch the file contents somehow return File(data, "application/pdf", "AnnualReport.pdf");

}

We used this technique at the end of Chapter 6 when sending image data retrieved from the database. Once again, we must specify a contentType, and can optionally specify a

fileDownloadName - the browser will treat these in exactly the same way as when we sent a disk file.

Sending the Contents of a Stream

If we are working with data that is available via an open System.IO.Stream, we can simply pass the stream to a version of the File menu – the contents of the stream will be read and sent to the browser. This is demonstrated by Listing 30.

Listing 30. Sending the contents of a stream public FileStreamResult DownloadReport() { Stream stream = ...open some kind of stream...

return File(stream, "text/html");

}

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 390 - 395)

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

(603 trang)