HttpClient
In this section, we present a Flickr Viewer app that allows you to search for photos on Flickr (flickr.com
)—one of the first photo-sharing websites—then browse through the results. The app uses an asynchronous method to invoke a Flickr web service—that is, a software component that can receive method calls over the Internet using standard web technologies.
Many web services return data in XML (Extensible Markup Language) format. XML is a widely supported standard for describing data that is commonly used to exchange that data between applications over the Internet. XML describes data in a way that both human beings and computers can understand.
The Flickr web-service method we use in this example returns XML by default. We’ll use LINQ to XML, which is built into the .NET platform, to process the data returned by Flickr. We’ll explain in Section 23.6.3 the small amount of XML and LINQ to XML needed—in the online XML and LINQ to XML chapter, we’ll explain these technologies in detail.
Flickr provides a so-called REST web service that can receive method calls via standard web technologies. As you’ll see, the app invokes the a Flickr web-service method via a URL, just as you’d use to access a web page from a web browser. REST—which stands for Representational State Transfer—is discussed in our online REST Web Services chapter.
Because there can be unpredictably long delays while await
ing a web-service response, asynchronous Task
s are frequently used in GUI apps that invoke web services (or perform network communication in general) to ensure that the apps remain responsive to their users.
To run this example on your computer, you must obtain your own Flickr API key at
https://www.flickr.com/services/apps/create/apply
and use it to replace the words YOUR API KEY HERE
inside the quotes in line 18 of Fig. 23.4. This key is a unique string
of characters and numbers that enables Flickr to track your usage of its APIs. Be sure to read the Flickr API’s Terms of Use carefully.
Our Flickr Viewer app (Fig. 23.4) allows you to search by tag for photos that users worldwide have uploaded to Flickr. Tagging—or labeling content—is part of the collaborative nature of social media. A tag is any user-supplied word or phrase that helps organize web content. Tagging items with meaningful words or phrases creates a strong identification of the content. Flickr uses the tags to improve its photo-search service, giving users better results.
As shown in the Fig. 23.4, you can type one or more tags (e.g., “pdeitel flowers”) into the TextBox
. When you click the Search Button
, the application invokes the Flickr web service that searches for photos, which returns an XML document containing links to the first 500 (or fewer if there are not 500) results that match the tags you specify. We use LINQ to XML to parse the results and display a list of photo titles in a ListBox
. When you select an image’s title in the ListBox
, the app uses another asynchronous Task
to download the full-size image from Flickr and display it in a PictureBox
.
HttpClient
to Invoke a Web ServiceThis app uses class HttpClient
(namespace System.Net.Http
) to interact with Flickr’s web service and retrieve photos that match the search tags. Line 21 creates the static HttpClient
object flickrClient
that’s used in this app to download data from Flickr. Class HttpClient
is one of many .NET classes that support asynchronous programming with async
and await
. In searchButton_Click
(lines 32–96), we use class HttpClient
’s GetStringAsync
method to start a new Task
(line 65). When we create that Task
, we assign it to instance variable flickrTask
(declared in line 23) so that we can test whether the Task
is still executing when the user initiates a new search.
An HttpClient
object is typically declared static
so it can be used by all of an app’s threads. According to the HttpClient
documentation, a static HttpClient
object can be used from multiple threads of execution.
flickr.photos.search
MethodMethod searchButton_Click
(lines 32–96) initiates the asynchronous Flickr search, so it’s declared as an async
method. First, lines 35–51 check whether you started a search previously and, if so, whether that search has already completed (lines 34–35). If an existing search is still being performed, we display a dialog asking if you wish to cancel the search (lines 37–40). If you click No, the event handler simply returns. Otherwise, we call the HttpClient
’s CancelPendingRequests
method to terminate the search (line 49).
Lines 54–57 create the URL that invokes Flickr’s flickr.photos.search
web-service method, which searches for photos, based on the provided parameters. You can learn more about this method’s parameters and the format of the URL for invoking the method at
https://www.flickr.com/services/api/flickr.photos.search.html
In this example, we specify values for the following flickr.photos.search
parameters:
api_key
—Your Flickr API key. Remember that you must obtain your own key from https://www.flickr.com/services/apps/create/apply
.
tags
—A comma-separated list of the tags for which to search. In our sample executions it was “pdeitel,flowers”
. If the user separates the tags with spaces, the app replaces the spaces with commas.
tag_mode
—We use the all
mode to get results that match all the tags specified in the search. You also can use any
to get results that match one or more of the tags.
per_page
—The maximum number of results to return (up to 500). If this parameter is omitted, the default is 100.
privacy_filter
—1
indicates only publicly accessible photos should be returned.
Line 65 calls class HttpClient
’s GetStringAsync
method, which uses the URL specified as the string
argument to request information from a web server. Because this URL represents a call to a web-service method, calling GetStringAsync
will invoke the Flickr web service to perform the search. GetStringAsync
returns a Task<string>
representing a promise to eventually return a string
containing the search results. Line 68 then await
s the Task
’s result. At this point, if the Task
is complete, method searchButton_Click
’s execution continues at line 71; otherwise, program control returns to method searchButton_Click
’s caller until the results are received. This allows the GUI thread of execution to handle other events, so the GUI remains responsive while the search is ongoing. Thus, you could decide to start a different search at any time (which cancels the original search in this app).
When the Task
completes, program control continues in method searchButton_Click
at line 68 where the app begins processing the XML returned by the web service. A sample of the XML is shown in Fig. 23.5.
XML represents data as elements, attributes and text. XML delimits elements with start tags and end tags. A start tag consists of the element name, possibly followed by attribute-Name=
value pairs, all enclosed in angle brackets. For example, line 1 in the sample XML
<rsp stat="ok">
is the start tag for an rsp
element containing the entire web-service response. This tag also contains the attribute stat
(for “status”)—the value "ok"
indicates that the Flickr web-service request was successful. An end tag consists of the element name preceded by a forward slash (/
) in angle brackets (for example, </rsp>
in line 19, which denotes “end of response”).
An element’s start and end tags enclose
text that represents a piece of data or
other nested elements—for example, the rsp
element contains one photos
element (lines 2–12) and the photos
element contains five photo
elements (lines 3–11) representing the photos that were found by the web service.
An element is empty if it does not contain text or nested elements between its start and end tags. Such an element can be represented by a start tag that ends with />
. For example, lines 3–5 define an empty photo
element with several attribute=
value pairs in the start tag.
Namespace System.Xml.Linq
contains the classes used to manipulate XML using LINQ to XML—we use several of these classes to process the Flickr response. Once the app receives the XML search results, line 68 (Fig. 23.4) uses XDocument
method Parse
to convert into an XDocument
object the string
of XML returned by the await
expression. LINQ to XML can query an XDocument
to extract data from the XML.
Lines 71–83 use LINQ to XML to gather from each photo
element in the XDocument
the attributes required to locate the corresponding photo on Flickr:
XDocument
method Descendants
(line 72) returns a list of XElement
objects representing the elements with the name specified as an argument—in this case, the photo
elements.
Lines 73–77 use XElement
method Attribute
to extract XAttribute
s representing the element’s id
, title
, secret
, server
and farm
attributes from the current photo XElement
.
XAttribute
property Value
(lines 73–77) returns the value of a given attribute.
For each photo, we create an object of class FlickrResult
(located in this project’s FlickrResult.cs
file) containing:
A Title
property—initialized with the photo
element’s title
attribute and used to display the photo’s title in the app’s ListBox
.
A URL
property—assembled from the photo
element’s id
, secret
, server
and farm
(a farm is a collection of servers on the Internet) attributes. The format of the URL for each image is specified at
http://www.flickr.com/services/api/misc.urls.html
We use a FlickrResult
’s URL in imagesListBox_SelectedIndexChanged
(Section 23.6.5) to download the corresponding photo when the user selects it in the ListBox
.
If there are any results (line 87), lines 89–90 bind the results’ titles to the ListBox
. You cannot bind a LINQ query’s result directly to a ListBox
, so line 89 invokes LINQ method ToList
on the flickrPhotos
LINQ query to convert it to a List
first, then assigns the result to the ListBox
’s DataSource
property. This indicates that the List
’s data should be used to populate the ListBox
’s Items
collection. The List
contains FlickrResult
objects, so line 90 sets the ListBox
’s DisplayMember
property to indicate that each FlickrResult
’s Title
property should be displayed in the ListBox
.
Method imagesListBox_SelectedIndexChanged
(lines 99–115) is declared async
because it await
s an asynchronous download of a photo. Lines 104–105 get the URL
property of the selected ListBox
item. Then lines 108–109 invoke HttpClient
’s GetByteArrayAsync
method, which gets a byte
array containing the photo. The method uses the URL specified as the method’s string
argument to request the photo from Flickr and returns a Task<byte[]>
—a promise to return a byte[]
once the task completes execution. The event handler then await
s the result. When the Task
completes, the await
expression returns the byte[]
. Line 112 creates a MemoryStream
from the byte[]
(which allows reading byte
s as a stream from an array in memory), then line 113 uses the Image
class’s static FromStream
method to create an Image
from the byte
array and assign it to the Picture-Box
’s Image
property to display the selected photo.