Partager via


Chapter 16: Using the Windows 7 Web Services API

The Hilo Browser application allows you to share your photos via Flickr by using the Share dialog. The previous chapter showed how the Share dialog uses the Windows HTTP Services API to upload the selected photos to Flickr using a multi-part HTTP POST request. Before the photo can be uploaded the Hilo Browser must first be authenticated with Flickr by obtaining a session token (called a frob), and then authorized to upload photos by obtaining an access token. To accomplish these two steps, Hilo Browser uses the Windows 7 Web Services Application Programming Interface (WWSAPI) to access Flickr using web services. In this chapter we will explore how the Hilo Browser uses this library.

Accessing Flickr through Web Services

In the last chapter you saw how Hilo uploaded photos to the Flickr web server through a HTTP POST request. A key parameter of this request is the access token that indicates that the Hilo Flickr application is authorized to access the user’s Flickr account. In addition, the Hilo Flickr account has to be authenticated with the Flickr web server to prove its identity, and from this Hilo obtains a session key called a frob. These two actions, obtaining the frob and the access token, are performed through web service calls.

Web services are a mechanism where client applications can communicate with web servers across the internet to make requests and receive data as a response. The key part of web services is the contract, a description of how the client makes a request and how the server responds, and the possible responses. Typically web services are implemented using XML messages over HTTP with the messages formatted as SOAP but there are other options. The contract between the client and server is immutable, if the server developers want to improve the service by adding more parameters or changing parameter types they have to provide a new web service with another contract, while still supporting the older web service.

This contract may be provided as a published specification and web service client developers must follow this specification to the letter. However, in some cases the contract may be provided as Web Services Description Language (WSDL) which is a standard XML description of a web service. WSDL can be used by code generation tools to generate proxy code. The proxy code does the work of constructing the packets sent to the service and decoding the packets returned from the service.

The Flickr web service is described on the Flickr API website. Flickr does not provide WSDL, instead it describes the request SOAP packet, the response SOAP packet and gives generic descriptions of the various API methods. From this it is possible to create a WSDL file, and the Hilo developers have done this to create the file Flickr.wsdl in the Browser project. The properties page for this file has a custom build step that uses a tool called wsutil.exe (Figure 1). This tool is part of the Windows 7 Software Development Kit (SDK) and so to use this tool you must have the SDK installed and the path to the SDK bin direct added to the project’s executable path. The wsutil tool reads the WSDL file and generates C header and source files for accessing the web service.

Figure 1 Custom build steps for WSDL file

Gg288974.788a345c-6f8c-4b56-8a05-7ab5533df744-thumb(en-us,MSDN.10).png

Authenticating a Flickr API Call

The Flickr API allows an application to access a Flickr account. There are two players in this transaction: the Flickr application and the user account, and both need to have a Flickr account. In Hilo this means that you have to create a Flickr account for Hilo and obtain a Flickr API key that identifies Hilo to Flickr. Hilo uploads photos to a user specified account and as part of this mechanism the user has to authorize Hilo and indicate to Flickr that Hilo can access the specified account. This involves several authentication and authorization steps.

When you apply for a Flickr API key (see Chapter 12) you will be given two strings: one is the API key and the other is a secret;. The API key identifies the user: the Hilo Flickr application. The secret is known only by you and Flickr and is used to create message digests (as explained in the last chapter) so that Flickr can verify that the parameters were not corrupted (either maliciously, or by accident) during transmission.

Before Hilo can upload a photo it has to logon to Flickr using its API key. To do this Hilo has to call the flickr.auth.getFrob web service method with the API key as the parameter. This method returns a logon session key called a frob. The frob identifies the Hilo application just for the current session and is only valid for 60 minutes, but rather than holding onto a frob until it becomes invalid, Hilo takes the simpler path of obtaining a new frob every time the user wishes to upload a photo.

Hilo will upload photos to a Flickr account and so Flickr must be informed that all actions performed with the current session (identified through the frob) must occur on a specified account. The Hilo application does not know what Flickr account should be used when uploading photos, nor should it, Hilo should only upload using the current session and allow Flickr to determine where the data is stored. The way to do this is to create a Flickr logon URL containing the frob and the API key and launch this URL in a browser. The user is then able to log on to whatever account they wish and authorize the Hilo Browser to have access to the account in the current session. Once the user has authorized the Hilo Browser to access the account, you need to obtain an access token to upload photos. To do this you call the flickr.auth.getToken web service method passing the API key and the frob as parameters and receive back an access token.

Using the Windows 7 Web Services API

Hard coding the access to web services and the decoding of responses as illustrated by FlickrUploader::UploadPhotos and FlickrUploader::GetPhotoId methods described in the last chapter, is brittle. As the developer you have to be aware of every detail of the protocol so that the request is made in exactly the right format because a slight deviation in the format will result in the web service rejecting the request. There is also the question of what happens if the protocol changes. Once published, the interface of a web service should never change; however, during the development of a service the interface may change requiring changes to the client. This increases the chance of errors. In addition to the issue of writing the correctly formatted messages there is also the issue of managing the transport code.

The Windows 7 Web Services API provides a way to access web services without writing large amounts of code to create request packets, interpret response packets, or write transport code. The starting point of any web service client application is the WSDL specification for the service. The Hilo development team has created a WSDL file, Flickr.wsdl, using this information and you will find this file in the top level folder of the Browser project in Solution Explorer.

Examining the Web Services Description Language File

The WSDL document defines services as collections of network endpoints, or ports. Each port will have request messages in a specific format and will return the response and fault messages in a specific format. In defining the port you need to specify the format of the data used in a request and response, and you need to specify the actual endpoints that will be used. To build in flexibility, WSDL allows you to provide abstract entities that describe various aspects of the web service and use the abstract entities with real values and create one or more concrete entities. So a portType element is abstract and defines a web service method but not where the method is implemented; whereas a port element is a concrete entity because it uses the portType element and has the actual network endpoint where the web service method can be accessed.

The Flickr API defines a generic request format and response data format, and a generic data format for returning information about errors. These are described in the Flickr.wsdl file as the abstract element types: FlickrRequest, FlickrResponse, and FlickrFault. From these data types the Flickr.wsdl file declares the abstract message formats (the FlickrRequestMessage, FlickrResponseMessage, and FlickrFaultMessage elements) that will be used to make the request and receive the responses.

In the Flickr.wsdl file two ports are described. The abstract portType elements are FlickrFrobRequestPort and FlickrTokenRequestPort which give details of the request, response, and fault messages used when accessing the ports (these definitions are the same, since the same request and response messages are used by both web service methods). In addition to the message formats, these portType elements give the name of the method to call (flickr.auth.getFrob and flickr.auth.getToken). These definitions are all abstract, they define the format of the request and response but they do not give details about the protocol, nor the actual network endpoint. The binding, port and service elements are concrete in that they contain information about the actual protocols and endpoints that will be used.

The binding elements, FlickrFrobRequestPortBinding and FlickrTokenRequestPortBinding reference the portType definition and provide information about the protocol that will be used. The binding is a concrete specification of the protocol and data format used for a particular portType. The service element contains the concrete definition of the collection of related endpoints, and each endpoint is given as a port element. In the Flickr.wsdl file the flickr.auth.getFrob and flickr.auth.getToken methods are declared as being part of two separate services, FlickrFrobRequestPortService and FlickrTokenRequestPortService respectively. Each service definition is a collection of port elements and each port element provides the binding element for a method and the endpoint where that web service method is implemented.

Generating the Proxy Code

The great advantage of using WSDL files is that they are compiled. You get two benefits here. First, the compiler will parse the WSDL file and issue errors if the WSDL code has invalid definitions. If you write the protocol code by hand you do not get this checking and so it is very easy to introduce logical errors that will be difficult to identify later. The second advantage is that the compiler will generate C code to access the web service and this reduces the amount of code that you have to write. Since the WSDL compiler, wsutil, generates C it means that you get high performance protocol binding code.

After you have created a WSDL file (or obtained one from a web service’s website) and added it to the project as mentioned above, you should compile it to generate the C source file and header file for the proxy code. You then add the C file to the project so that you can use the proxy code. There is no need to alter the compiler options for this file since it is safe to compile it as a C++ file. However, be aware that since the code is inherently C code, the definitions created will be structures, and functions will depend upon opaque handles.

The WSDL compiler creates structures for the data types, messages, bindings, and service descriptions and creates instances of these structures populated with the data from the concrete definitions in the WSDL file. In addition the compiler generates functions to allow you to create the channel and the protocol bindings for each web service defined in the WSDL. These functions create a proxy to the web service, that is, a function that your code calls as if it is the web service method. The name of the proxy function is generated from the name of a binding element appended with _CreateServiceProxy. Since there are two binding elements in Flickr.wsdl there are two functions generated: FlickrFrobRequestPortBinding_CreateServiceProxy and FlickrTokenRequestPortBinding_CreateServiceProxy. Both of these functions return a handle that is a pointer to a WS_SERVICE_PROXY structure. This structure is allocated by the system so this handle must be released when you are finished making calls with a call to the WsFreeServiceProxyWSSAPI function.

web services may return rich error information through an error object. If you chose to receive this information you create an error object by calling the WsCreateError function that returns an opaque handle, a WS_ERROR pointer, that you can deallocate when you have finished by calling the WsFreeError function. You pass this handle to any method that can return error information and use the WsGetErrorString and WsGetErrorProperty functions to get information about the error. The error object can be reused by calling the WsResetError function, so you should only need to allocate the error object once and then deallocate it once you have finished making web service calls.

When the WWSAPI accesses a web service it will need to allocate various buffers. Rather than giving the responsibility to you to make the individual memory allocations (with the danger that you may forget to deallocate memory and cause memory leaks) the WSSAPI will do the allocations for you with a memory heap object. Your only responsibility is to create the heap object before you make any web service calls by calling the WsCreateHeap function and to release the heap object with a call to the WsFreeHeap function when you have completed all web service calls.

The WSDL compiler creates functions to make the actual calls to the web service methods, with names derived from the binding elements and the method they bind to. For Flickr.wsdl the compiler creates two functions FlickrFrobRequestPortBinding_flickr_auth_getFrob and FlickrTokenRequestPortBinding_flickr_auth_getToken. These functions have parameters for the service proxy, the in parameters (used to make the request), pointers to buffers for the out parameters (for the response), handles for the heap object, and the error object that will be used.

Calling the Proxy Code

Hilo uses the WWSAPI in the FlickrUploader class, specifically the GetToken and ObtainFrob methods. To use WWSAPI you have to include the WebServices.h header file and link to the WebServices.lib library from the Windows 7 SDK, you also need to include the header file created by the wsutil tool from your WSDL file, and include the generated C file in the project.

Since there are two methods that use WWSAPI, the FlickrUploader class factors the code to initialize and clean up the proxy code in the private methods, CreateWebProxy and CloseWebProxy. CreateWebProxy creates a heap object and an error object and then creates and opens the proxy. To give more flexibility the CreateWebProxy method does not use the code generated by the wsutil tool to create the web proxy object, instead the WWSAPI functions are called directly. Although the CreateWebProxy method returns a handle to an error object and Hilo passes this to the web service proxy, the current version of Hilo only provides basic error handling and does not access the data in the error object.

The CreateWebProxy method creates the web service proxy by calling the WsCreateServiceProxy function indicating the type of channel to use, the relevant code is shown in Listing 1. The first parameter indicates that the proxy will be called to make requests and receive a response. The second parameter indicates that SOAP over HTTP will be used to access the service.

The WsCreateServiceProxy function can provide additional values through the sixth parameter which is an array of WS_CHANNEL_PROPERTY structures. Each of these has a property ID followed by a pointer to the property value and the size of the property. The CreateWebProxy method provides three properties, the first supplies the version of SOAP to use, the second indicates that no addressing-related headers are transmitted as part of the SOAP envelope. The final property specifies that all data is supplied as UTF8.

Listing 1 Creating a proxy

WS_ENVELOPE_VERSION soapVersion = WS_ENVELOPE_VERSION_SOAP_1_2;
WS_ADDRESSING_VERSION addressingVersion = WS_ADDRESSING_VERSION_TRANSPORT;
WS_ENCODING encoding = WS_ENCODING_XML_UTF8;

WS_CHANNEL_PROPERTY channelProperties[3] =
{
   {
      WS_CHANNEL_PROPERTY_ENVELOPE_VERSION,
      &soapVersion, sizeof(WS_ENVELOPE_VERSION)
   },
   {
      WS_CHANNEL_PROPERTY_ADDRESSING_VERSION,
      &addressingVersion, sizeof(WS_ADDRESSING_VERSION)
   },
   {
      WS_CHANNEL_PROPERTY_ENCODING,
      &encoding, sizeof(WS_ENCODING)
   }
};

if(SUCCEEDED(hr))
{
   hr = WsCreateServiceProxy(
      WS_CHANNEL_TYPE_REQUEST, WS_HTTP_CHANNEL_BINDING, 
      nullptr, nullptr, 0, 
      channelProperties, ARRAYSIZE(channelProperties),
      proxy, // out parameter, returns the proxy
      *error);
}

A web service is implemented on an endpoint so you have to call the WsOpenWebServiceProxy function to provide this endpoint to the proxy. The code to do this in the CreateWebProxy method is shown in Listing 2.

Listing 2 Opening the proxy

static const wchar_t* flickr_soap_endpoint_url = L"https://api.flickr.com/services/soap/";

WS_ENDPOINT_ADDRESS address = 
{
   {
      static_cast<unsigned long>(wcslen(flickr_soap_endpoint_url)), 
      const_cast<wchar_t*>(flickr_soap_endpoint_url)
   }
};

if(SUCCEEDED(hr))
{
   hr = WsOpenServiceProxy(*proxy, &address, nullptr, *error);
}

After you have created the web service proxy you can make calls to the web service by calling the WsCall function. The WsCall function has parameters that describe the web service operation to call and the parameters to be passed to the service. The function will return the results from calling the web service. The call to the WsCall method is provided by the functions generated by the wsutil tool from the WSDL file.

The CreateWebProxy method creates a heap and error object as well as the proxy object, and all of these are used to make calls to the web service. All of these objects allocate resources that must be released when you no longer need to use the proxy and this is done by calling the CloseWebProxy method, Listing 3.

Listing 3 Releasing resources

void FlickrUploader::CloseWebProxy (WS_HEAP** heap, WS_SERVICE_PROXY** proxy,  WS_ERROR** error)
{
    if (proxy != nullptr && *proxy != nullptr)
    {
        WsCloseServiceProxy(*proxy, nullptr, nullptr);
        WsFreeServiceProxy(*proxy);
    }

    if (heap != nullptr && *heap != nullptr)
    {
        WsFreeHeap(*heap);
    }

    if (error != nullptr && *error != nullptr)
    {
        WsFreeError(*error);
    }
}

Using the Windows 7 Web Services API in Hilo

The WWSAPI is used to make calls to the Flickr API web service to obtain the frob (the ObtainFrob method) and access token for the upload operation (the GetToken method). Listing 4 shows the basic code from the GetToken method to make the call: initialize the proxy; make the call; close the proxy.

Listing 4 The GetToken method

std::wstring FlickrUploader::GetToken(const std::wstring& frob)
{
   std::wstring outputString;
   WS_ERROR* error = nullptr;
   WS_HEAP* heap = nullptr;
   WS_SERVICE_PROXY* proxy = nullptr;

   HRESULT hr = CreateWebProxy(&heap, &proxy, &error);

   // Call Web Service…

   CloseWebProxy(&heap, &proxy, &error);
   return outputString;
}

The code to call the web service is shown in Listing 5. The parameters of the method are the name of the method (L" flickr.auth.getToken"), the API key, the signature, and the frob. These parameters are passed through an instance of the _FlickrRequest structure generated by the wsutil tool. The signature is generated by concatenating the API key, frob, and method name and then calculating the MD5 hash.

Listing 5 Code to obtain a token

if(SUCCEEDED(hr))
{
   _FlickrRequest request = 
   {
      (wchar_t*)(flickr_auth_getToken_method_name), 
      (wchar_t*) flickr_api_key, // api_key
      nullptr,                          // api_sig computed later
      (wchar_t*)frob.c_str()
   };

   std::wstring params = flickr_secret;
   params += L"api_key";
   params += request.api_key;
   params += L"frob";
   params += request.frob;
   params += L"method";
   params += request.method;

   std::wstring api_sig = CalculateMD5Hash(params);
   request.api_sig = const_cast<wchar_t*>(api_sig.c_str());
   wchar_t* token = nullptr;
   hr = FlickrTokenRequestPortBinding_flickr_auth_getToken(
      proxy, &request, &token, heap, nullptr, 0, nullptr, error);
   if (SUCCEEDED(hr))
   {
      bool errorFound = false;
      std::wstring value = GetXmlElementValueByName(token, L"token", &errorFound);
      if (!errorFound)
      {
         outputString = value;
      }
   }
}

The GetToken method then calls the FlickrTokenRequestPortBinding_flickr_auth_getToken function that was created by the wsutil tool. This function makes the call to the WSSAPI WsCall function and returns the results as a SOAP packet. Finally, the GetToken method calls the GetXmlElementValueByName method that uses XmlLite to obtain the value of the token element that is returned from the GetToken method.

Conclusion

In this chapter you saw how to use the Windows 7 Web Services API to make requests to the Flickr web services in order to authenticate and authorize the Hilo Browser so that it can upload the selected photos.

This chapter concludes this series of articles. In this series, we’ve described how the Hilo Browser and the Hilo Annotator were implemented using some of the powerful features and APIs provided by Windows 7. We’ve described how to design a touch-enabled user interface, and how to implement it using Direct2D, the Windows Animation Manager, and the Windows Ribbon. We’ve described how to integrate the application into the Windows Shell and how to use the Windows Imaging Component to manipulate images. And finally we’ve described how to share photos using HTTP and web services.

We hope you have enjoyed this series of articles, and we hope that they will help you to build rich, compelling Windows applications of your own.

Previous | Home