1 / 90

Protips for Windows Azure Mobile Services

Protips for Windows Azure Mobile Services. Chris Risner Technical Evangelist 3-543. Introduction. Windows Azure Technical Evangelist. @ chrisrisner. Mobile Developer. http:// chrisrisner.com. Former .NET developer. Live in Washington. Grew up in Michigan.

abena
Download Presentation

Protips for Windows Azure Mobile Services

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Protips for Windows Azure Mobile Services Chris Risner Technical Evangelist 3-543

  2. Introduction Windows Azure Technical Evangelist @chrisrisner Mobile Developer http://chrisrisner.com Former .NET developer Live in Washington Grew up in Michigan Co-Organizer of Seattle GDG

  3. Agenda Mobile Services Recap Tips Tricks Tips Tricks Questions

  4. Mobile Services Recap

  5. Windows Azure Mobile Services Auth Data Notifications Logging & Diag Server Logic Scheduler Scale

  6. Multi-Platform Apps

  7. You don’t need a different Mobile Service for each platform!

  8. Connect them all!

  9. Cross-Platform Support

  10. Cross-Platform Support

  11. Multi-Device Push

  12. Single Platform Push Notifications Windows Store push.wns.sendToastText04(item.channel, {text1: text}, … ); Windows Phone push.mpns.sendFlipTile(item.channel, {title: text}, …); iOS push.apns.send(item.token, { alert: text, payload: { inAppMessage: Details }}, …); Android push.gcm.send(item.registrationId, item.text, …);

  13. Multi-Platform Push Notifications function sendNotifications() { vardeviceInfoTable = tables.getTable('DeviceInfo'); deviceInfoTable.where({ userId : user.userId }).read({ success: function(deviceInfos){ deviceInfos.forEach(function(deviceInfo){ if (deviceInfo.uuid != request.parameters.uuid) { if (deviceInfo.pushToken != null && deviceInfo.pushToken != 'SimulatorToken') { if (deviceInfo.platform == 'iOS') { push.apns.send(deviceInfo.pushToken, { alert: "New something created" } , { //success / error block}); } else if (deviceInfo.platform == 'Android') { push.gcm.send(deviceInfo.pushToken, "New something created", { success / error block}); } } } }); } }); }

  14. Don’t forget to check the response on error (or getFeedback for APNS)Also, check out Delivering Push Notifications to Millions of Devices – Friday @12pm

  15. Virtual Tables

  16. Create a tableUse it’s endpointDon’t call request.Execute

  17. Custom API

  18. Custom API • Non-table based scripts • Accessible from • GET • POST • PUT • PATCH • DELETE • Permissions based

  19. Custom API Demo

  20. Talking to Azure Storage

  21. It’s doableIt’s not perfect Scripts and the Azure module

  22. Reading Tables varazure = require('azure'); function read(query, user, request) { varaccountName = 'accountname'; varaccountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.table.core.windows.net'; vartableService = azure.createTableService(accountName, accountKey, host); tableService.queryTables(function (error, tables) { if (error) { request.respond(500, error); } else { request.respond(200, tables); } }); }

  23. Reading Table Rows var azure = require('azure'); function read(query, user, request) { varaccountName = 'accountname'; varaccountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.table.core.windows.net'; vartableService = azure.createTableService(accountName, accountKey, host); vartq = azure.TableQuery .select() .from(request.parameters.table); tableService.queryEntities(tq, function (error, rows) { if (error) { request.respond(500, error); } else { request.respond(200, rows) } }); }

  24. Creating Containers var azure = require('azure'); function insert(item, user, request) { varaccountName = 'accountname'; varaccountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.blob.core.windows.net'; varblobService = azure.createBlobService(accountName, accountKey, host); if (request.parameters.isPublic == 1) { blobService.createContainerIfNotExists(item.containerName ,{publicAccessLevel : 'blob'} , function (error) { if (!error) { request.respond(200, item); } else { /* error */ request.respond(500);} }); } else { blobService.createContainerIfNotExists(item.containerName, function (error) { if (!error) { request.respond(200, item); } else { /*error */request.respond(500); } }); } }

  25. Reading and “Creating” Blobs var azure = require('azure'), qs= require('querystring'); function insert(item, user, request) { varaccountName = 'accountname'; varaccountKey = 'Accountkey------------nKHDsW2/0Jzg=='; var host = accountName + '.blob.core.windows.net'; varblobService = azure.createBlobService(accountName, accountKey, host); varsharedAccessPolicy = { AccessPolicy: { Permissions: 'rw', //Read and Write permissions Expiry: minutesFromNow(5) } }; varsasUrl = blobService.generateSharedAccessSignature(request.parameters.containerName, request.parameters.blobName, sharedAccessPolicy); varsasQueryString = { 'sasUrl' : sasUrl.baseUrl + sasUrl.path + '?' + qs.stringify(sasUrl.queryString) }; request.respond(200, sasQueryString); } function minutesFromNow(minutes) { var date = new Date() date.setMinutes(date.getMinutes() + minutes); return date; }

  26. Storage Demo

  27. Talking REST

  28. The REST API Base REST API Endpoint URL http://Mobileservice.azure-mobile.net/tables/* Data Operations and their REST Equivalents

  29. JSON to SQL Type Mappings

  30. Postman &Runscope Demo

  31. Sending Emails

  32. Sending an Email //var crypto = require('crypto'); //item.tempId = new Buffer(crypto.randomBytes(16)).toString('hex'); function sendEmail(item) { varsendgrid = new SendGrid('myaccount@azure.com', 'mypassword'); var email = { to : item.email, from : 'from-me@chrisrisner.com', subject : 'Welcome to MyApp', text: 'Thanks for installing My App! Click this link to verify:\n\n' + 'http://myapp.azurewebsites.net/activate.html?id=' + item.id + '&tid=' + item.tempId, createDate : new Date() }; sendgrid.send({ to: item.email, from: email.from, subject: email.subject, text: email.text }, function(success, message) { // If the email failed to send, log it as an error so we can investigate if (!success) { console.error(message); } else { saveSentEmail(email); } }); }

  33. Setting up SendGrid Demo

  34. The CLI

  35. SOME It’s awe

  36. CLI Demo

  37. Service Filters and DelegatingHandlers

  38. Client sideIntercepts requestsIntercepts responses

  39. Sending Version Info with Each Request - (void)handleRequest:(NSURLRequest *)request next:(MSFilterNextBlock)next response:(MSFilterResponseBlock)response { MSFilterResponseBlockwrappedResponse = ^(NSHTTPURLResponse *innerResponse, NSData *data, NSError *error) { response(innerResponse, data, error); }; // add additional versioning information to the querystring for versioning purposes NSString *append = [NSStringstringWithFormat:@"build=%@&version=%@", self.build, self.version]; NSURL *url = nil; NSRange range = [request.URL.absoluteStringrangeOfString:@"?"]; if (range.length > 0) { url = [NSURL URLWithString:[NSStringstringWithFormat:@"%@&%@&p=iOS", request.URL.absoluteString, append]]; } else { url = [NSURL URLWithString:[NSStringstringWithFormat:@"%@?%@&p=iOS", request.URL.absoluteString, append]]; } NSMutableURLRequest *newRequest = [request mutableCopy]; newRequest.URL = url; next(newRequest, wrappedResponse); }

  40. DelegatingHandlers are Service Filters public static MobileServiceClientMobileService = new MobileServiceClient( "https://<your subdomain>.azure-mobile.net/", "<your app key>", new VersionHandler() ); using System; using System.Net.Http; using System.Threading.Tasks; namespace WindowsStore { public class VersionHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationTokencancellationToken) { request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.ToString() + "?version=v2"); return base.SendAsync(request, cancellationToken); } } }

  41. Script Versioning

  42. Checking the Version in Scripts function insert(item, user, request) { if (request.parameters.build < 2.0) { item.description = 'Not entered'; } request.execute({ success : function() { if (request.parameters.build < 2.0) { delete item.description; } request.respond(); } }); }

  43. For more on versioning, check outGoing Live and Beyond with Windows Azure Mobile ServicesFriday @ 10:30 am

  44. Talking Twitter

  45. v1 is deadv1.1 is hard

  46. Part 1: The Helpers function generateOAuthSignature(method, url, data){ var index = url.indexOf('?'); if (index > 0) url = url.substring(0, url.indexOf('?')); varsigningToken = encodeURIComponent('Your Consumer Secret') + "&" + encodeURIComponent('Your Access Token Secret'); var keys = []; for (var d in data){ if (d != 'oauth_signature') { console.log('data: ' , d); keys.push(d); } } keys.sort(); var output = "GET&" + encodeURIComponent(url) + "&"; varparams = ""; keys.forEach(function(k){ params += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(data[k]); }); params = encodeURIComponent(params.substring(1)); return hashString(signingToken, output+params, "base64"); } function hashString(key, str, encoding){ varhmac = crypto.createHmac("sha1", key); hmac.update(str); return hmac.digest(encoding); } function generateNonce() { var code = ""; for (vari = 0; i < 20; i++) { code += Math.floor(Math.random() * 9).toString(); } return code; }

  47. Part 2: The Work (part 1) var crypto = require('crypto'); varquerystring = require('querystring'); function read(query, user, request) { var result = { id: query.id, identities: user.getIdentities(), userName: '' }; var identities = user.getIdentities(); varuserId = user.userId; vartwitterId = userId.substring(userId.indexOf(':') + 1); //API 1.0 //url = 'https://api.twitter.com/1/users/show/' + twitterId + '.json'; //API 1.1 varurl = 'https://api.twitter.com/1.1/users/show.json?user_id=' + twitterId; var key = 'This is your consumer key'; var nonce = generateNonce(); varsigmethod = 'HMAC-SHA1'; var version = '1.0'; vartwitterAccessToken = identities.twitter.accessToken; varoauth_token = 'The Access Token'; var seconds = new Date() / 1000; seconds = Math.round(seconds); varrequestType = 'GET'; varoauthData = { oauth_consumer_key: key, oauth_nonce: nonce, oauth_signature:null, oauth_signature_method: sigmethod, oauth_timestamp: seconds, oauth_token: oauth_token, oauth_version: version }; varsigData = {}; for (var k in oauthData){ sigData[k] = oauthData[k]; } sigData['user_id'] = twitterId;

  48. Part 2.2: The Work var sig = generateOAuthSignature('GET', url, sigData); oauthData.oauth_signature = sig; varoauthHeader = ""; for (k in oauthData){ oauthHeader += ", " + encodeURIComponent(k) + "=\"" + encodeURIComponent(oauthData[k]) + "\""; } oauthHeader = oauthHeader.substring(1); varauthHeader = 'OAuth' + oauthHeader; //Generate callback for response from Twitter API varrequestCallback = function (err, resp, body) { if (err || resp.statusCode !== 200) { console.error('Error sending data to the provider: ', err); request.respond(statusCodes.INTERNAL_SERVER_ERROR, body); } else { try { varuserData = JSON.parse(body); if (userData.name != null) result.UserName = userData.name; else result.UserName = "can't get username"; request.respond(200, [result]); } catch (ex) { console.error('Error parsing response from the provider API: ', ex); request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex); } } } //Create the request and execute it varreq = require('request'); varreqOptions = { uri: url, headers: { Accept: "application/json" } }; if (authHeader != null) reqOptions.headers['Authorization'] = authHeader; req(reqOptions, requestCallback); }

More Related