Migrating to Windows Azure Bing Search Api with jQuery JSONP

回答済み Migrating to Windows Azure Bing Search Api with jQuery JSONP

  • Tuesday, July 10, 2012 9:48 PM
     
     

    I'm trying to migrate my application to the new Windows Azure Bing Search API from the old Bing Search API.  My current application uses a jQuery ajax call with jsonp to retrieve the search results.

    However, when I attempt to use the new Search API, a browser popup asks for a username and password.  I've attempted to pass in the username and password using jQuery's .ajax password/username fields, but that doesn't seem to work. There is still a popup asking for a username and password.

    I'm trying to find a way to use jQuery to access the API without having a popup.  Any advice?  Thanks in advance.

All Replies

  • Wednesday, July 11, 2012 5:55 AM
    Moderator
     
     Answered Has Code

    Hi,

    Would you like to try these code snippets, i tried it and it works good for me:

    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.js" type="text/javascript">

    </script> <script type="text/javascript"> function setHeader(xhr) { xhr.setRequestHeader('Authorization', 'Basic <Your Azure Marketplace Key(Remember

    add colon character at before the key, then use Base 64 encode it'); } function GetBing() { //Build up the URL for the request var requestStr = "https://api.datamarket.azure.com/Data.ashx/Bing/

    Search/Web?Query=%27hi%27&$top=50&$format=Atom"; //Return the promise from making an XMLHttpRequest to the server $.ajax({ url: requestStr, beforeSend: setHeader }) .done( //Callback for success function (request) { var results = [], count; // Use the JSON parser on the results, safer than eval var obj = JSON.parse(request.responseText); // Verify if the service has returned images if (obj.d !== undefined) { var items = obj.d.results; // Data adapter results needs an array of items of the shape: for (var i = 0, itemsLength = items.length; i < itemsLength; i++) { var dataItem = items[i]; results.push({ key: (fetchIndex + i).toString(), data: { title: dataItem.Title, thumbnail: dataItem.Thumbnail.MediaUrl, width: dataItem.Width, height: dataItem.Height, linkurl: dataItem.MediaUrl } }); } } else { } }); } </script>


    Hope this helps.

    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework




  • Friday, July 13, 2012 7:46 PM
     
     Answered

    Thanks for the response.  I got it working.  2 comments that might help others reading this:

    1) JSONP is not needed for the new search API, use JSON instead

    2) I was initially a little confused on exactly what to base64 encode. 

    My final code looks like this:

            $.ajax({
                type: 'GET',
                url: searchUri,
                dataType: "json",
                context: this,
                beforeSend: function(xhr){
                    //base64 encoded: ignore:key
                    xhr.setRequestHeader('Authorization', 'Basic <encoded_username_and_app_key>);
                },
                success: function(data,status){
                    //parse data...               
                }
            });

    Replace <encoded_username_and_app_key> with a base64 encoded string, where string is composed of your app key prefixed by a colon

    psuedo code for creating <encoded_username_and_app_key>

    base64_encode(':' + <appKey>)


  • Wednesday, July 25, 2012 8:43 PM
     
     

    It seems to me that the code sample has a major security flaw. Since the account key is included in client side code, it (the key) is exposed to the users.  At this point, they could harvest it and use it to make queries in their apps against your account quota.

  • Thursday, July 26, 2012 5:02 AM
    Moderator
     
     

    Hi Victor,

    Yes, the account key may be expose because JQuery script is running on the client side application, the more secure way is use Server code to do this (HttpWebRequest). Or you may need to use Azure ACS for this, set Realm with your application endpoint url, the ACS will check which domain send this request, if not match, ACS will reject these requests.

    BR,
    Arwind


    Please mark the replies as answers if they help or unmark if not. If you have any feedback about my replies, please contact msdnmg@microsoft.com Microsoft One Code Framework

  • Thursday, July 26, 2012 1:46 PM
     
     

    Hi,

    I used the same code, but getting a null value instead of the data.  But the same url returns a json when accessed in browser.  No errors in the error console also.

    I don't no what I am doing wrong.  Any ideas??

  • Thursday, July 26, 2012 2:44 PM
     
     

    Hi,

    I have fixed this, I was using "Authentication" instead of "Authorization" in the header.

    Now I m getting an object.

    Thanks for the code.

  • Tuesday, July 31, 2012 12:09 AM
     
     

    Thanks for this, its a great example and has steered me down the right path. But using the same code I am getting "No Transport" errors, however when i provide a callback and move to JSONP I get a response but of course cannot pass the credentials due to that limitation with JSONP. The SetRequestHeader is succesfully encoded into Base64, and everything is correct. the URL I am providing passes information to the browser if i manually enter the App Key in the Password.

    Any help you can offer on this would be gold! Thanks!

  • Tuesday, July 31, 2012 2:08 PM
     
     

    j_kirkwood, I ran into the same situation, it seems that it is not possible in an easy way. Next I will try to create a local php file that calls the api and jquery only has to call the php file on the same webserver without the credential problem or jsonp, all this is handled in the php file.

    Looking on oauth possibilities - I found this link, maybe it helps (but I think we still get the same errors).

    http://msdn.microsoft.com/en-us/library/windowsazure/e01288e7-ed70-4702-8141-53b2289dbbf1#JSONPSupport

    Regards Yukiko

  • Tuesday, July 31, 2012 7:25 PM
     
     

    yukiko7,

    Thanks for the response. I have tracked down the issue (I am currently using jQuery 1.7.2 in my application) and it seems as though while Firefox and Chrome have built in support for Cross-Origin Resource Sharing, IE8 and IE9 have a seperate object that is not supported in jQuery called XDomainRequest which it uses rather than XMLHTTPRequest (Support for this using CORS will be returned in IE10). As such JSON requests will not work through jQuery, passing the Authorization through HTTP Headers.

    The only option for cross-browser support seems to be your solution (a proxy) or potentially using OAuth with JSON/P. But the information that is posted in the documentation for OAUTH is rather confusing. I'm not sure its possible to implement a solution using JSON/P and OAuth without bringing the user into the experience, when all I want to do is provide my consumer secret key to Microsoft, trade it for an access key and get access to the data so I can use it in IE7+ browsers.

    If anyone has any direction regarding OAuth, I would be grateful, the closest I've found to the kind of implementation that I need to do is regarding getting an OAuth token for the translator API.

    Thanks in Advance! :)

  • Sunday, January 13, 2013 8:58 AM
     
      Has Code

    I ran into several of these issues and put together the comments here into an example that works on IE, Chrome, and Opera.

    <HTML>
    <HEAD>
    <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.7.2.js" type="text/javascript"></script>
    <script type="text/javascript">
    	
    var accountKey = your_azure_primary_account_key;
    var accountKeyEncoded = base64_encode(":" + accountKey);
    
    jQuery.support.cors = true;
    
    function setHeader(xhr) {
      xhr.setRequestHeader('Authorization', "Basic " + accountKeyEncoded);
      //'Basic <Your Azure Marketplace Key(Remember add colon character at before the key, then use Base 64 encode it');
    }
    
    function GetBing() {
      //Build up the URL for the request
      var requestStr = "https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/Image?Query=%27Seattle%27&$top=50&$format=json";
    
      //Return the promise from making an XMLHttpRequest to the server
      $.ajax({
        url: requestStr,
        beforeSend: setHeader,
        context: this,
    	type: 'GET',
        success: function (data, status) {
          var results = data;
          var imgSrc = data.d.results[0].MediaUrl;
    	  var imgElement = document.getElementById("theImage");
    	  imgElement.src = imgSrc;
    //	  imgElement.width = 200;
        },
    	error: function (jqXHR, textStatus, errorThrown) {
    	  alert (textStatus);
    	}
      });
    }
    
    function base64_encode(data) {
      // http://kevin.vanzonneveld.net
      // +   original by: Tyler Akins (http://rumkin.com)
      // +   improved by: Bayron Guevara
      // +   improved by: Thunder.m
      // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
      // +   bugfixed by: Pellentesque Malesuada
      // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
      // +   improved by: Rafal Kukawski (http://kukawski.pl)
      // *     example 1: base64_encode('Kevin van Zonneveld');
      // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
      // mozilla has this native
      // - but breaks in 2.0.0.12!
      //if (typeof this.window['btoa'] == 'function') {
      //    return btoa(data);
      //}
      var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
        ac = 0,
        enc = "",
        tmp_arr = [];
    
      if (!data) {
        return data;
      }
    
      do { // pack three octets into four hexets
        o1 = data.charCodeAt(i++);
        o2 = data.charCodeAt(i++);
        o3 = data.charCodeAt(i++);
    
        bits = o1 << 16 | o2 << 8 | o3;
    
        h1 = bits >> 18 & 0x3f;
        h2 = bits >> 12 & 0x3f;
        h3 = bits >> 6 & 0x3f;
        h4 = bits & 0x3f;
    
        // use hexets to index into b64, and append result to encoded string
        tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
      } while (i < data.length);
    
      enc = tmp_arr.join('');
    
      var r = data.length % 3;
    
      return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
    
    }
    	
    </script>
    </HEAD>
      
    <BODY onLoad="GetBing();">
      <img id="theImage"/>
    </BODY>
    </HTML>