1 posts categorized "Web API"

12/04/2012

Picture Search Part 1: Searching with Bing

Picture Search is a Windows Store sample app that is used to search pictures with Bing or other services such as Flickr. With this blog you will find an article series that goes through all the different aspects of the application such as defining the user interface to show a grid of pictures, starting the search with the help of the search contract, sharing pictures, and organizing the pictures in the UI with the grid view control using grouping and a collection view source.

The first part of this article series shows the implementation of the server part using the ASP.NET Web API hosted within a Windows Azure Web site. The service offered here will be used from the Windows Store app later on.

Attendees of my Windows 8 Camp at Microsoft or from the Windows 8 Deep Dive at Basta! Austria already know this Windows Store sample app – Picture Search. Please find the source code here!

Creating the service using the ASP.NET Web API

Let’s start with an ASP.NET MVC 4 Web Application.

image

The ASP.NET MVC 4 Web Application offers the Web API Project template.

image

This project type defines a ValuesController type in the Controllers folder. However, this one is not used for the sample app. A new controller named BingSearchController is created for the Picture Search app.

image

The BingSearchController defines the Get method for doing a HTTP GET request passing a search term, and retrieving a list of PictureInformation objects. This controller method makes use of the SearchManager that instantiates the request to the Bing service.

        public async Task<IEnumerable<PictureInformation>> Get(string id)
        {
          SearchManager manager = new SearchManager();
          IEnumerable<PictureInformation> result = await manager.SearchImagesAsync(id);
          return result;
        }

By defining the Get method in the BingSearchController class, the URL to retrieve the returned items is http://<server>/api/bingsearch/<searchTerm>.

For the implementation of the controller, the SearchManager and PictureInformation types are needed. These types are defined next.

The Data Transfer Object

The PictureInformation class is a simple data holder that contains the title, Url, and ThumbnailUrl of the image.

  public class PictureInformation
  {
    public string Title { get; set; }
    public string Url { get; set; }
    public string ThumbnailUrl { get; set; }
    public string Source { get; set; }
  }

Contract for Image Requests

The picture search service can use different picture search providers, e.g. Bing and Flickr. In the book Professional C# 2012 and .NET 4.5 you can find implementations calling both of these services. The sample here just makes use of Bing search.

For creating different implementations, the interface IImageRequest is defined. With this interface, the search provider returns a Url property dependent on the provider that contains the search term. Both of these providers return XML content with the search request. This content is parsed by calling the Parse method of the IImageRequest interface. This returns a collection of PictureInformation objects.

  public interface IImageRequest
  {
    string SearchTerm { get; set; }
    string Url { get; }

    IEnumerable<PictureInformation> Parse(string xml);

    ICredentials Credentials { get; }
  }

Bing Search Request

The class BingRequest is the implementation of the IImageRequest interface to do the search via Bing. For using this class you need to add the application identifier. You can get this identifier by registering with the Bing Search API available at the Windows Azure Marketplace: https://datamarket.azure.com/dataset/bing/search. Up to 5000 transactions per month are free.

The major parts of this class are the Url property that returns the URL link to access the service including the search term, and the Parse method that makes use of LINQ to XML to convert the XML content to an object collection.

  public class BingRequest : IImageRequest
  {
    private const string AppId = "enter your BING app-id here!";

    public BingRequest()
    {
      Count = 50;
      Offset = 0;
    }

    private string searchTerm;
    public string SearchTerm
    {
      get { return searchTerm; }
      set { searchTerm = value; }
    }

    public string Url
    {
      get
      {
        return string.Format("https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/Image?Query=%27{0}%27&$top={1}&$skip={2}&$format=Atom", SearchTerm, Count, Offset);
      }
    }

    public int Count { get; set; }
    public int Offset { get; set; }

    public IEnumerable<PictureInformation> Parse(string xml)
    {
      XElement respXml = XElement.Parse(xml);
      XNamespace d = XNamespace.Get("http://schemas.microsoft.com/ado/2007/08/dataservices");
      XNamespace m = XNamespace.Get("http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");

      return (from item in respXml.Descendants(m + "properties")
              select new PictureInformation
              {
                Title = new String(item.Element(d + "Title").Value.Cast<char>().Take(50).ToArray()),
                Url = item.Element(d + "MediaUrl").Value,
                ThumbnailUrl = item.Element(d + "Thumbnail").Element(d + "MediaUrl").Value,
                Source = "Bing"
              }).ToList();
    }

    public ICredentials Credentials
    {
      get
      {
        return new NetworkCredential(AppId, AppId);
      }
    }
  }

Making the Call to Bing Search

The SearchManager class that is called from the Web API controller makes an asynchronous GET request to the Bing service. The request is done by the HttpClient class. This class is new with .NET 4.5 and fully based on asynchronous features.

  public class SearchManager
  {
    public async Task<IEnumerable<PictureInformation>> SearchImagesAsync(string searchTerm)
    {
      var request = new BingRequest();
      request.SearchTerm = searchTerm;
      
      var client = new HttpClient(new HttpClientHandler
        {
          Credentials = request.Credentials
        });
      HttpResponseMessage response = await client.GetAsync(request.Url);
      string resp = await response.Content.ReadAsStringAsync();
      IEnumerable<PictureInformation> images = null;
      await Task.Run(() =>
      {
        images = request.Parse(resp);
      });
      return images;
    }
  }

Instead of invoking GetAsync and then ReadAsStringAsync with the returned HttpResponseMessage, the implementation could be simplified by invoking GetStringAsync. I’ve decided for the former version to show more functionality of this class.

Calling the Search Service

With all this in place, the service can be invoked to make picture search requests. This can be a simple test from a Web browser, e.g. calling http://<server>/api/bingsearch/Ferrari. Calling this from IE returns JSON data, e.g. this collection as defined by the PictureInformation class:

[{"Title":"Voici une belle photo de Ferrari de qualité",
   "Url":"http://images-et-photos.com/files/ferrari1.jpg",
   "ThumbnailUrl":"http://ts1.mm.bing.net/th?id=H.4902048858114356&pid=15.1",
   "Source":"Bing"},
  {"Title":"Name: ferrari pictures.jpgViews: 110204Size:337.4",
   "Url":"http://www.whitegadget.com/attachments/pc-wallpapers/75212d1315377041-ferrari-ferrari-pictures.jpg",
   "ThumbnailUrl":"http://ts2.mm.bing.net/th?id=I.4894034425284685&pid=15.1&W=160&H=120",
   "Source":"Bing"},
  {"Title":"Ferrari Enzo Ferrari Photos:",
   "Url":"http://world-viewer.com/data_images/ferrari-enzo-ferrari/ferrari-enzo-ferrari-04.jpg",
   "ThumbnailUrl":"http://ts3.mm.bing.net/th?id=I.4935837306587618&pid=15.1&W=160&H=120",
   "Source":"Bing"},

 

In the next article of this series I’m creating a Windows runtime component to call this Web API service.

More information on the Web API and Windows Store apps can be found in my workshops and in my book Professional C# 2012 and .NET 4.5.

Christian

CN innovation