Upload
natalia-efimtseva
View
145
Download
1
Embed Size (px)
DESCRIPTION
Windows Azure Mobile Services – быстрый и функциональный мобильный бэкенд
Citation preview
5 июля 2013
Создание современных приложений с Visual Studio 2013
Ефимцева Наталия, [email protected]Работа c облачным бэкендом мобильных приложений
Mobile Services – быстрый и функциональный мобильный бэкенд
Windows Azure Mobile Services
Data
Notifications
Auth
Server Logic
Scheduler
Logging & Diag
Scale
Mobile Service: что нового? Продакшен – 99.9% SLA
БД 20МБ бесплатно CLI Планировщик Custom API Source Control (git) Общий код
NPM Android SDK HTML + JS SDK Поддержка WP7.x
Приложение My TODO
Мультиплатформенность
Вам не нужно создавать отдельный мобильный сервис к каждой платформе!
Подключайте их все!
Мультиплатформенность
Смешарики
• Используют Mobile Services в Windows 8, Windows Phone, iOS, Android приложениях
Push-нотификации
Единая платформа для pushWindows 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, …);
Единая платформа для pushfunction sendNotifications() {
var deviceInfoTable = 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});
}
}
}
});
}
});
}
Не забывайте проверять ответ при возврате от notification-сервера ошибке (или getFeedback для APNS)
Добавим toast-уведомления
Виртуальные таблицы
Создание таблицыЕе использование как endpoint’аТ.е. нет вызова request.Execute
Собственный код\API
• Скрипты, не привязанные к таблице• Доступ как• GET• POST• PUT• PATCH• DELETE
• Разделение прав
Собственный код\API
Обновим тайл через Custom API
Работа с хранилищем (Azure Storage)
Это возможноЭто еще не совершенноРеализуется через серверные скрипты и модули
Чтением данных из таблицыvar azure = require('azure');
function read(query, user, request) {
var accountName = 'accountname';
var accountKey = 'Accountkey------------nKHDsW2/0Jzg==';
var host = accountName + '.table.core.windows.net';
var tableService = azure.createTableService(accountName, accountKey, host);
tableService.queryTables(function (error, tables) {
if (error) {
request.respond(500, error);
} else {
request.respond(200, tables);
}
});
}
Работа с BLOB’амиvar azure = require('azure'), qs = require('querystring');
function insert(item, user, request) {
var accountName = 'accountname';
var accountKey = 'Accountkey------------nKHDsW2/0Jzg==';
var host = accountName + '.blob.core.windows.net';
var blobService = azure.createBlobService(accountName, accountKey, host);
var sharedAccessPolicy = {
AccessPolicy: {
Permissions: 'rw', //Read and Write permissions
Expiry: minutesFromNow(5)
}
};
var sasUrl = blobService.generateSharedAccessSignature(request.parameters.containerName,
request.parameters.blobName, sharedAccessPolicy);
var sasQueryString = { '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;
}
Работа с BLOB’ами
Работа с email
Отправка сообщения//var crypto = require('crypto');
//item.tempId = new Buffer(crypto.randomBytes(16)).toString('hex');
function sendEmail(item) {
var sendgrid = new SendGrid('[email protected]', 'mypassword');
var email = {
to : item.email,
from : '[email protected]',
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);
}
});
}
Отправим письмо
DelegatingHandler’ы
На стороне клиентаПерехватывают запросы и ответы
Добавляет версию к каждому запросуpublic static MobileServiceClient MobileService = 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.CancellationToken cancellationToken) { request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.ToString() + "?version=v2"); return base.SendAsync(request, cancellationToken); } }}
Работа с версиями в серверном скрипте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();
}
});
}
Версионность
Авторизация: кэширование токена
Кэширование в .NETpublic static class CredentialLocker { private const string RESOURCE= "MobileServices"; public static void AddCredential(string username, string password) { var vault = new PasswordVault(); var credential = new PasswordCredential(RESOURCE, username, password); vault.Add(credential); } public static PasswordCredential GetCredential() { PasswordCredential credential = null; var vault = new PasswordVault(); try { credential = vault.FindAllByResource(RESOURCE).FirstOrDefault(); if (credential != null){ credential.Password = vault.Retrieve(RESOURCE, credential.UserName).Password; } }
catch (Exception) { //creds not found }
return credential; }
public static void RemoveCredential(string username) { var vault = new PasswordVault(); try { // Removes the credential from the password vault. vault.Remove(vault.Retrieve(RESOURCE, username)); } catch (Exception) { //creds not stored } } }
Получение токенаSetting:
MoblieServiceUser user;user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook);CredentialLocker.AddCredential(user.userId, user.MobileServiceAuthenticationToken);
Getting:
var credential = CredentialLocker.GetCredential();if (credential != null){ MobileService.CurrentUser = new MobileServiceUser(credential.UserName); MobileService.CurrentUser.MobileServiceAuthenticationToken =
credential.Password; }
Авторизуемся с Google
Авторизация: обновление токена
Обновление токенаЗапрос (request)
Проверка ответа (response) на 401 код ошибки Повторный login
пользователя
Обновление запроса (request) новым токеном
Повторная отправка запроса (request)
Обновление UI
DelegationHandlers (снова)public class VersionHandler : DelegatingHandler{ protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
System.Threading.CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); while (response.StatusCode == HttpStatusCode.Unauthorized) { try { await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook); request.Headers['X-ZUMO-AUTH'] =
App.MobileService.CurrentUser.MobileServiceAuthenticationToken; } catch (Exception ex) {}
response = await base.SendAsync(request, cancellationToken); } } }
Получение данных постранично
На клиентеНа сервере
На клиентеC#:IMobileServiceTableQuery<TodoItem> query = todoTable .Where(todoItem => todoItem.Complete == false) .Skip(3) .Take(3);items = await query.ToCollectionAsync();ListItems.ItemsSource = items;
iOS:NSPredicate * predicate = [NSPredicate predicateWithFormat:@"complete == NO"];MSQuery * query = [self.table queryWithPredicate:predicate];query.includeTotalCount = YES; // Request the total item count
query.fetchOffset = 3;query.fetchLimit = 3;
[query readWithCompletion:^(NSArray *results, NSInteger totalCount, NSError *error) { …
Android:mToDoTable.where().field("complete").eq(false).skip(3).top(3) .execute(new TableQueryCallback<ToDoItem>() {
На сервереOption 1:query.where({complete: false}) .take(3) .skip(3);
Option 2:var q = query.getComponents();q.take = 3;q.skip = 1;query.setComponents(q);
Option 3:query.where(function() {
return this.complete == false}) .take(3) .skip(3);
Поддержка Source Control
Включается на порталеСоздается git репозиторийКопируем локально, работаем, обновляем удаленный репозиторийДобавление NPM модулей
Интеграция с Git и добавление node-uuid
Windows Azure Mobile Services
© 2012 Microsoft Corporation. All rights reserved. Microsoft, Windows, and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.
Ефимцева Наталия[email protected]@nefimtseva
Команда Windows Azure в России – [email protected]