Пару недель назад я создал простое приложение для социальных сетей в рамках вебинара Codename One . Это приложение предоставило функциональность, аналогичную приложению Facebook.
- Отправить и принять запросы в друзья
- Опубликовать новости
- Просмотр новостей, опубликованных вами и вашими друзьями
Для сервера я использовал REST-интерфейс PHP / MySQL и написал очень тонкий клиент в Codename One. И сервер, и клиентский проект находятся на GitHub, поэтому вы можете просмотреть его и установить самостоятельно на свой сервер.
Некоторые скриншоты приложения, работающего в симуляторе Codename One:
Я решил портировать это приложение на Parse.com в качестве подтверждения концепции.
Вот 3-минутный скринкаст приложения от Parse.com:
Что такое Parse.com?
Parse.com похож на серверную часть в коробке. Вы получаете легко масштабируемый REST-сервер и базу данных NoSQL без необходимости управлять им самостоятельно. Это позволяет вам сосредоточиться на одном клиентском приложении Codename — сделать его красивым и красивым. Не беспокойтесь о масштабировании, исправлениях серверного программного обеспечения или любом другом шуме. Просто приложение.
Мне особенно нравится тот факт, что Parse.com бесплатен для приложений, которые получают небольшой объем трафика (до 30 запросов в секунду). По мере роста вашего приложения вы просто платите за добавленную пропускную способность.
С высоты птичьего полета процесс портирования
Я начал с полнофункционального приложения. Единственной частью приложения Codename One, которое требовало изменений, был класс SocialClient
, в котором происходило все взаимодействие с сервером.
На стороне сервера процесс был примерно:
- Зарегистрировать аккаунт на Parse.com
- Создать новый идентификатор приложения
- Настройте мою модель данных
- Реализуйте тонкий REST-интерфейс для моих данных, используя облачные функции Parse.
Клиентский API
Полный API для этого класса выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// Registers new user public void register(String username, String password) throws IOException; // Logs in as user public void login(String username, String password) throws IOException; // Logs current user out public void logout() throws IOException; // Gets list of friends of current user public List<Map> getFriends() throws IOException; // Finds users with username matching query string public List<Map> findUsers(String query) throws IOException; // Finds pending friend requests for current user public List<Map> getPendingFriendRequests() throws IOException; // Sends a friend request to given user public void sendFriendRequest(String username) throws IOException; // Accepts a friend request from a given user public void acceptFriendRequest(String username) throws IOException; // Decline a friend request from a given user public void declineFriendRequest(String username) throws IOException; // Gets the profile of a given user public Map getProfile(String username) throws IOException; // Updates the profile of a given user with specified values public void updateProfile(Map profile) throws IOException; // Posts a news item public long post(Map post) throws IOException; // Gets the news feed for the given user public List<Map> getFeed(Date olderThan) throws IOException; // Gets the username of the current user public String getUsername(); |
Для взаимодействия с серверной частью PHP / MySQL этот API был реализован непосредственно поверх ConnectionRequest
и NetworkManager
для отправки запросов HTTP GET и POST непосредственно на сервер. JSON использовался для передачи ответа от сервера клиенту, и он был преобразован в `Map`s и` List`s.
Примечание: вы заметите, что этот API не использует строгую типизацию Java … Я просто использую списки и карты. Это было для гибкости, пока я дорабатывал API. В какой-то момент в реальном приложении я, вероятно, реорганизовал бы использование некоторых пользовательских типов Java.
Для бэк-энда Parse мы могли бы просто написать тонкий REST-клиент поверх ConnectionRequest
, но есть более простой способ, благодаря Chidiebere Okwudire parse4cn1 cn1lib, который оборачивает Parse REST API, предоставляя Java-API, очень похожий на официальный Parse Java API .
Создание приложения Parse
- Войти в Parse.com
- Создать новое приложение
Создание модели данных
После того, как ваше приложение было создано, вам нужно создать модель данных. В Parse.com это делается путем определения набора классов. Это аналогично созданию таблиц в базе данных SQL. Чтобы мотивировать это упражнение, давайте взглянем на схему базы данных MySQL в предыдущей версии. Ниже приведен PHP (со встроенным кодом SQL), который использовался для создания базы данных:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<?php class conf_Installer { function update_1() { $q[] = "create table users ( username varchar( 32 ) primary key not null , password varchar( 64 ) )"; $q[] = "create table sessions ( username varchar( 32 ), token_id varchar( 64 ) primary key not null , expires INT( 11 ) )"; $q[] = "create table friends ( user1 varchar( 32 ), user2 varchar( 32 ), primary key (user1, user2 ) )"; $q[] = "create table friend_requests ( sender varchar( 32 ), receiver varchar( 32 ), primary key (sender, receiver) )"; $q[] = "create table profiles ( username varchar( 32 ) primary key not null , screen_name varchar( 100 ), avatar varchar( 100 ), avatar_mimetype varchar( 100 ) )"; $q[] = "create table posts ( post_id int ( 11 ) not null auto_increment primary key, username varchar( 32 ), date_posted INT( 11 ), photo varchar( 255 ), photo_mimetype varchar( 100 ), comment text )"; df_q($q); } } ?> |
Для модели данных Parse мой первый инстинкт был просто создать класс для каждой таблицы. Однако я обнаружил, что сопоставление «один к одному» не было идеальным. Частично это связано с тем, что Parse уже предоставляет некоторые функциональные возможности из коробки, а также то, что база данных Parse не реляционная, как MySQL.
Примечание: Parse поддерживает отношения, но они работают немного иначе, чем в реляционной базе данных, такой как MySQL. Мы рассмотрим различия более подробно позже.
В итоге я остановился на следующих классах для своего приложения:
-
User
— для учетных записей пользователей. Я свелprofile
таблицыprofile
в этот отдельный класс, чтобы упростить модель. -
Post
— для новостей, публикуемых пользователями.
Шаг за шагом: создание модели данных
- В разделе «Данные» вкладки «Core» нажмите «Добавить класс»:
- Выберите «Пользователь» в диалоговом окне и нажмите «Создать класс»:
- Создать столбец с именем экрана . Нажмите кнопку «+ Col» в верхнем меню.
Затем выберите type = «String» и name = «screen_name»:
- Создайте колонку аватара типа «Файл»:
- Добавьте отношения «друзья» и «pendingFriendRequests». , Добавьте их как столбцы типа «Отношение»:
Создание класса «Пост»
- Создайте новый класс с именем «Post»:
- Добавьте колонку «Комментарий».
- Добавьте столбец «фото» с типом «Файл»:
- Добавьте столбец «postsBy» как тип «Указатель» к классу «_User»:
Загрузка файлов
Я также закончил тем, что создал класс Upload
но это было чисто для того, чтобы помочь с загрузкой файлов, и не является частью концептуального дизайна.
Разве мы не пропустили много столов? !!
Как мы сократили базу данных с 6 таблиц до 2 классов? Что ж:
- Таблица
sessions
больше не нужна в Parse, потому что Parse заботится обо всех аспектах регистрации пользователей, входа в систему и управления сеансами. - Как упоминалось ранее, я просто сложил данные
profiles
непосредственно в классUser
. Это было сделано для простоты и для минимизации количества запросов данных для получения данных профиля. - Таблицы
friends
иfriend_requests
были объединенными таблицами, предназначенными для связи пользователей друг с другом. В Parse мы обрабатываем это, добавляя столбец типаRelation
к нашим классам. Например, классUser
имеет отношениеfriends
отношениеpendingFriendRequests
которые обеспечивают эквивалентную функциональность для таблицfriends
иfriend_requests
.
связи
Как я упоминал выше, отношения обрабатываются немного иначе в Parse, чем в реляционной базе данных. Parse предоставляет два типа столбцов для «указания» на другие записи в базе данных:
- Указатель — тип, который вы можете использовать для добавления ссылки на отдельную запись в этом столбце. Например, если вы хотите отследить, был ли пользователь родительским для другого пользователя, вы можете добавить столбец с именем «parent» в класс «User» с типом «Pointer».
- Отношение — тип, используемый для хранения ссылок на несколько записей в этом столбце. Например, если вы хотите отслеживать все дочерние элементы пользователя, вы можете добавить в класс «Пользователь» столбец с именем «children» с типом «Relation».
В нашей модели данных нам нужно было отследить две взаимосвязи между записями пользователей:
- Будь они друзьями
- Есть ли ожидающий запрос на дружбу от одного к другому.
Поэтому я добавил столбцы с именами «friends» и «pendingFriendRequests» в класс «User», оба типа «Relation».
Важно: отношения только односторонние. Например, если вы добавите «Стив» в отношение друзей «Дуг», то это автоматически не добавит «Дуг» в отношение друзей «Стив». Для отношений «pendingFriendRequests» это то, что мы хотим в любом случае, но для отношений «друзья» мы хотели, чтобы они были двусторонними, поэтому нам нужно добавить Стива к Дугу и Дуга к Стиву, когда они становятся друзьями.
Доступ к базе данных из кодового имени один
Теперь, когда наша база данных настроена, давайте попробуем подключиться к ней из нашего приложения Codename one.
Установка библиотеки parse4cn1
Первое, что нам нужно сделать, это загрузить и установить библиотеку parse4cn1
. Вы можете скачать его здесь .
Скопируйте файл parse4cn1.cn1lib
в каталог lib
вашего проекта, затем выберите «Обновить» библиотеки (т. parse4cn1.cn1lib
Щелкните правой кнопкой мыши проект> «Codename One»> «Обновить Libs»).
Вам также необходимо установить библиотеку CN1JSON, от которой зависит библиотека parse4cn1.
Инициализация API Parse4CN1
Прежде чем делать что-либо еще, нам нужно инициализировать Parse API, вызвав Parse.initialize()
. Я помещаю этот метод в конструктор для моего клиентского класса:
1
2
3
|
public SocialClientParse() { Parse.initialize( "<APP ID>" , "<CLIENT KEY>" ); } |
Идентификатор приложения и ключ клиента можно найти на вкладке «Ключи» при входе в Parse.com.
Предупреждение. Обязательно используйте «Ключ клиента», а не «Ключ API REST» или «Мастер-ключ» при подключении к Parse с клиентского устройства, как, скорее всего, в случае приложений Codename One. Ключ REST API и мастер-ключ предоставляют полные разрешения для вашей базы данных и не должны быть встроены в ваше приложение из соображений безопасности. Эти ключи предназначены для использования в безопасных настройках, таких как серверное приложение, которое подключается к вашему приложению для разбора.
Вход в систему
Реализация метода входа в систему для нашего REST-клиента заключается в следующем.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
ParseUser user; String token; ... public void login(String username, String password) throws IOException { try { user = ParseUser.create(username, password); user.login(); token = user.getSessionToken(); } catch (ParseException ex) { Log.e(ex); throw new IOException(ex.getMessage()); } } |
и выход из системы:
01
02
03
04
05
06
07
08
09
10
|
public void logout() throws IOException { try { user.logout(); user = null ; token = null ; } catch (ParseException ex) { Log.e(ex); throw new IOException(ex.getMessage()); } } |
Регистрация пользователя
Регистрация пользователя очень похожа на вход в систему. Он просто использует метод ParseUser.signUp()
вместо ParseUser.login()
.
01
02
03
04
05
06
07
08
09
10
11
12
|
public void register(String username, String password) throws IOException { try { ParseUser user = ParseUser.create(username, password); user.put( "screen_name" , username); user.signUp(); } catch (ParseException ex) { Log.e(ex); throw new IOException(ex.getMessage()); } } |
Остальная часть REST API
Parse API обеспечивает поддержку CRUD (Create-Read-Update-Delete) непосредственно от клиента к источнику данных. Для безопасности он поддерживает ACL как на уровне класса, так и на уровне объекта. Поэтому, если вы правильно настроите свои ACL, вы можете взаимодействовать с базой данных непосредственно из клиентского приложения Codename one. Вы можете увидеть примеры API на вики parse4cn1 .
Вот несколько причин, почему вы не должны делать это
- Предоставление клиенту прямого доступа к базе данных делает приложение очень трудным для защиты . Любой серверный инженер, достойный похвалы, знает, что ВЫ НЕ МОЖЕТЕ ДОВЕРЯТЬ КЛИЕНТУ Если вы хотите, чтобы действия были доступны для некоторых пользователей, но не для других — и они используют одно и то же клиентское приложение, вам нужно быть очень осторожным с ACL, которые вы используете в своей базе данных.
- Для некоторых операций может потребоваться несколько запросов к базе данных, что может замедлить работу приложения. Лучше просто отправить один запрос серверу разбора и позволить серверному коду обрабатывать несколько запросов.
Облачный код
Parse позволяет вам реализовывать серверные веб-сервисы REST, известные как «облачный код». Поскольку этот код выполняется на стороне сервера, вы можете разрешить им работать с главным ключом, поэтому вам не нужно полагаться на ACL для ограничения доступа к записям и классам. Вы можете использовать свою собственную логику, чтобы решить, кто что может делать. Эта модель более точно соответствует вашему собственному серверу и обеспечивает больший контроль. И это позволяет вам заблокировать вашу базу данных, чтобы вам не нужно было предоставлять прямой доступ клиентам.
В облачном коде используется API Parse Javascript, который эквивалентен API REST и Java. Чтобы использовать их, вам нужно установить инструменты командной строки parse .
Создание проекта Local Cloud Code
Он позволяет вам создавать локальную версию приложения Parse, поскольку это касается разработки облачного кода. Вот вывод команды parse new
для настройки проекта локальной разработки:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
Steves-iMac:social-network-parse shannah$ parse new Please log in to Parse using your email and password. Email: myemail @example .com Password (will be hidden): Would you like to create a new app, or add Cloud Code to an existing app? Type "(n)ew" or "(e)xisting" : e 1 : Social Demo Select an App to add to config: 1 Awesome! Now it's time to setup some Cloud Code for the app: "Social Demo" , Next we will create a directory to hold your Cloud Code. Please enter the name to use for this directory, or hit ENTER to use "Social Demo" as the directory name. Directory Name: social-demo-parse- 2 Your Cloud Code has been created at /Users/shannah/cn1_files/incubator/social-network-parse/social-demo-parse- 2 . Next, you might want to deploy this code with "parse deploy" . This includes a "Hello world" cloud function, so once you deploy you can test that it works, with: curl -X POST \ -H "X-Parse-Application-Id: xxxxxxxxxxxxxxxxxx" \ -H "X-Parse-REST-API-Key: xxxxxxxxxxxxxxxxxxxx \ -H "Content-Type: application/json" \ -d '{}' \ https: //api.parse.com/1/functions/hello |
Это создает основу для моего проекта приложения в каталоге «social-demo-parse-2». Структура каталогов:
1
2
3
4
5
6
|
./cloud ./cloud/main.js ./config ./config/global.json ./ public ./ public /index.html |
Здесь ./cloud/main.js
файл ./cloud/main.js
, в который будет включен весь облачный код. Он запускает вас с хорошим примером «привет» функции, которая может быть вызвана через REST API. Его содержание таково:
1
2
3
4
5
|
// Use Parse.Cloud.define to define as many cloud functions as you want. // For example: Parse.Cloud.define( "hello" , function(request, response) { response.success( "Hello world!" ); }); |
Это простая функция, которая просто возвращает строку «Hello world!». Вы можете вызвать эту функцию прямо из приложения Codename One, просто вызвав:
1
2
|
String result = (String)ParseCloud.callFunction( "hello" , null ); System.out.println(result); // Hello world! |
Написание конечных точек для REST API
Теперь нам просто нужно создать конечные точки для всех соответствующих функций нашего клиента. Давайте начнем с вставки заполнителей для наших конечных точек API:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
Parse.Cloud.define( "send_friend_request" , function(request, response) { ... }); Parse.Cloud.define( "accept_friend_request" , function(request, response) { ... }); Parse.Cloud.define( "decline_friend_request" , function(request, response) { }); Parse.Cloud.define( "get_pending_friend_requests" , function(request, response) { }); Parse.Cloud.define( "get_friends" , function(request, response) { }); Parse.Cloud.define( "post" , function(request, response) { }); Parse.Cloud.define( "update_profile" , function(request, response) { }); Parse.Cloud.define( "get_profile" , function(request, response) { }); Parse.Cloud.define( "get_feed" , function(request, response) { }); Parse.Cloud.define( "find_users" , function(request, response) { }); |
Чтобы оставаться совместимым с PHP / MySQL REST API в предыдущей версии, эти конечные точки всегда будут возвращать объект JSON в следующей форме:
1
2
3
4
5
|
//For errors: {code : 500 , message : "Some error message" , ...} // For successes {code : 200 , ... } |
Таким образом, клиент всегда может проверить свойство «code», чтобы узнать, было ли действие успешным.
Для успешных операций может быть три типа возвращаемых значений:
- Нет возвращаемого значения. Например,
accept_friend_request
,send_friend_request
. - Возвращает список объектов. Например,
find_users
,get_friends
и т. Д. - Возвращает один объект. Например,
get_profile
Написание кодового имени One Web Service Client
Итак, на стороне клиента (в нашем приложении Codename One) я создал 3 служебных оболочки для этих случаев:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
/** * Calls cloud code function with void return type. * @param funcName The name of the function to call. * @param params Parameters passed to the function. Accepts null. */ private void callFunc(String funcName, Map params) throws IOException { try { JSONObject response = (JSONObject)ParseCloud.callFunction(funcName, params); int code = response.getInt( "code" ); if (code != 200 ) { throw new IOException(response.getString( "message" )); } } catch (Throwable ex) { Log.e(ex); ex.printStackTrace(); throw new IOException(ex.getMessage()); } } /** * Calls a cloud code function whose result will be a list of objects. * @param funcName The name of the cloud code function to call. * @param params Parameters passed to the cloud code. Accepts null. * @param listKey The JSON key in the response object that contains the list of objects that * were returned by the cloud code. */ private List<Map> getList(String funcName, Map params, String listKey) throws IOException { try { JSONObject response = (JSONObject)ParseCloud.callFunction(funcName, params); System.out.println(response); int code = response.getInt( "code" ); if (code != 200 ) { throw new IOException(response.getString( "message" )); } else { ArrayList<Map> out = new ArrayList<Map>(); JSONArray posts = response.getJSONArray(listKey); int len = posts.length(); for ( int i= 0 ; i<len; i++) { JSONObject row = posts.getJSONObject(i); out.add(toMap(row)); } return out; } } catch (Throwable ex) { Log.e(ex); ex.printStackTrace(); throw new IOException(ex.getMessage()); } } /** * Calls cloud code function that returns a single object. * @param funcName The name of the cloud code function to call. * @param params Parameters passed to the cloud code function. Accepts null. * @param mapKey The JSON key of the response object that contains the object that * was returned from the cloud code function. */ private Map getMap(String funcName, Map params, String mapKey) throws IOException { try { JSONObject response = (JSONObject)ParseCloud.callFunction(funcName, params); int code = response.getInt( "code" ); if (code != 200 ) { throw new IOException(response.getString( "message" )); } else { JSONObject row = response.getJSONObject(mapKey); return toMap(row); } } catch (Throwable ex) { Log.e(ex); ex.printStackTrace(); throw new IOException(ex.getMessage()); } } |
Для удобства я также создал тонкие обертки вокруг этих служебных функций, чтобы можно было передавать объекты массива для параметров вместо карты.
Код клиента и сервера для get_feed
Теперь, когда у нас есть основа для серверной и клиентской сторон REST API, давайте еще немного уточним, как именно будет выглядеть код на стороне сервера и на стороне клиента. Возьмите, например, метод getFeed()
, который предназначен для возврата списка сообщений, которые должны появляться в ленте новостей текущего пользователя.
Код сервера будет выглядеть так:
1
2
3
4
5
6
7
8
|
Parse.Cloud.define( "get_feed" , function(request, response) { // ... some logic to retrieve the posts from the database if (success) { response.success({code : 200 , posts : [...]}); } else { response.success({code : 500 , message : 'Some error message' }); } }); |
Это исключает всю логику и в значительной степени упрощается, но ключ здесь в том, что он возвращает объект JSON с помощью обратного вызова response.success()
. Если ошибок не было, сообщения содержатся в массиве под ключом posts
полученного объекта JSON. Таким образом, клиент веб-службы будет использовать наш getList()
утилиты getList()
следующим образом:
1
2
3
|
public List<Map> getFeed(Date olderThan) throws IOException { return getList( "get_feed" , "posts" ); } |
Отправка, получение и принятие запросов друзей
Напомним из нашего дизайна базы данных, что запросы друзей и друзей поддерживаются через отношения «friends» и «pendingFriendRequests» в таблице users. Для отправки запроса на добавление в друзья пользователю необходимо добавить текущего пользователя в отношение pendingFriendRequests этого пользователя. Принятие запроса на добавление в друзья от пользователя включает добавление текущего пользователя в отношение «друзья» этого пользователя, добавление этого пользователя в отношение «друзья» текущего пользователя и удаление этого пользователя из отношения «pendingFriendRequests» текущего пользователя. Код для send_friend_request
выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
|
Parse.Cloud.define( "send_friend_request" , function(request, response) { Parse.Cloud.useMasterKey(); ( new Parse.Query(Parse.User)).equalTo( "username" , request.params.username).each(function(friend) { friend.relation( "pendingFriendRequests" ).add(Parse.User.current()); friend.save().then(function(result) { response.success({code: 200 , message: "Successfully sent friend request" }); }); }); }); |
Некоторые вещи, чтобы отметить здесь:
-
Parse.Cloud.useMasterKey()
дает нам карт-бланш на API разбора. У нас нет никаких ограничений безопасности. Без этого на весь доступ будут распространяться ограничения безопасности для текущего вошедшего в систему пользователя. -
(new Parse.Query(Parse.User)).equalTo("username", request.params.username).each(function(friend) {
Создает запрос в классе User для всех пользователей с именем пользователя, равным « Параметр username »передается как часть запроса. Затем методeach()
перебирает результаты с предоставленным обратным вызовом. -
friend.relation("pendingFriendRequests").add(Parse.User.current());
Добавляет текущего пользователя в отношение pendingFriendRequests найденного пользователя. -
friend.save().then(function(result) {
Мы сохраняем объект пользователя.save()
возвращает обещание, так что вызовthen()
даст нам возможность отложить то, что будет дальше, до завершения сохранения. Если вы Вы не знакомы с обещаниями, это просто симпатичный синтаксис для обратного вызова. -
response.success({code: 200, message: "Successfully sent friend request"});
В конечном итоге это возвращает ответ клиенту в виде объекта JSON.
Код для получения списка ожидающих запросов на добавление в друзья выглядит следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
Parse.Cloud.define( "get_pending_friend_requests" , function(request, response) { Parse.Cloud.useMasterKey(); var out = []; var user = Parse.User.current(); user.relation( "pendingFriendRequests" ).query().each(function(friend) { out.push({ sender : friend.get( "username" ), receiver : user.get( "username" ), avatar : friend.get( "avatar" ) ? friend.get( "avatar" ).url() : null , screen_name : friend.get( "screen_name" ) }); }) .then(function(result) { response.success({code: 200 , requests: out}); }); }); |
И принимать ожидающие запросы на добавление в друзья:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
Parse.Cloud.define( "accept_friend_request" , function(request, response) { Parse.Cloud.useMasterKey(); var currentUser = Parse.User.current(); var pendingRequests = currentUser.relation( "pendingFriendRequests" ); pendingRequests.query().equalTo( "username" , request.params.username).each(function(friend) { currentUser.relation( "friends" ).add(friend); pendingRequests.remove(friend); currentUser.save().then(function(result) { friend.relation( "friends" ).add(currentUser); return friend.save(); }, function(error) { response.success({code : 500 , message : error}); }).then(function(result) { response.success({code: 200 , message: "Friend request accepted" }); }, function(error) { response.success({code : 500 , message : error}); }); }); }); |
Полный исходный код облака
Вы можете просмотреть полный исходный код этого облачного кода здесь . Каждый метод следует примерно по одной схеме:
- Получить некоторые данные из базы данных
- Переберите результаты, чтобы получить ответ JSON.
Некоторые запросы, особенно запросы, включающие сложные отношения, такие как в get_feed
были немного сложны для get_feed
, но в итоге я был впечатлен гибкостью Parse в возможности поддерживать довольно сложные запросы. Я не буду вдаваться в подробности здесь, но отмечу, что документация Parse является исключительной, и, похоже, она имеет довольно большую базу пользователей, судя по количеству вопросов и ответов, связанных с анализом, которые уже доступны онлайн на их форумах и в другом месте. Мне едва приходилось тратить больше 5 минут на поиск в Google, чтобы найти ответ на мои вопросы, когда я застрял.
Создайте приложение самостоятельно
Полный проект, как клиентский проект Codename One, так и облачный код синтаксического анализа , размещены на Github, чтобы вы могли загрузить и собрать проект самостоятельно.
Установите приложение на Android
Я разместил Android-сборку этого приложения, чтобы вы могли установить его прямо на свой телефон, если хотите:
Ссылка: | Создание собственных мобильных приложений на базе облачных технологий с Parse.com и Codename One от нашего партнера по JCG Стива Ханны в блоге Codename One . |