API » История » Редакция 7
Редакция 6 (Александр Кварацхелия, 07.04.2014 18:40) → Редакция 7/30 (Александр Кварацхелия, 07.04.2014 18:56)
h1. API
h2. Общие сведения об API Неба
Сервер Неба поддерживает взаимодействие с внешними приложениями с использованием REST-подобного API. По сути, это же API используется для работы клиентского (браузерного) приложения Неба, что позволяет внешним приложениям реализовать все те же действия, что и пользователь в основном интерфейсе Неба.
При использовании текущей версии API Неба необходимо осознавать, что оно предназначено в первую очередь для функционирования основного клиентского приложения Неба. Все спорные и неоднозначные моменты в данном API возникли из-за того, что в первую очередь максимизировалось удобства разработки и функционирования именно клиентского приложения.
Текущая версия API пока не является фиксированной. Возможны изменения в составе передаваемых данных без предварительного уведомления взаимодействующих внешних приложений. Фиксация API по разделам Неба и последующая его версионность будут реализованы в течение 2014 года.
Также помните, что на текущий момент времени "защита от дурака" в Небе равномерно распределена между клиентским и серверным приложением. Значительная часть ограничений на использование Неба (в основном, некритичных для безопасности и целостности данных) реализованы путем выбора специфической схемы организации клиентского приложения, без их дублирования на серверной части. Поэтому, перед использованием тех или иных операций желательно убедиться, что они не противоречат логике предметной области и текущего поведения Неба в целом (например, посмотрев как требуемая операция реализована в интерфейсе Неба).
h2. Аутентификация внешнего приложения
Для аутентификации пользователей применяется общепринятая для Web-приложений схема.
1. Внешнее приложение отсылает GET или POST запрос на URL @https://nebopro.ru/loging@ передавая в параметрах запроса следующие значения:
* @login@ - email зарегистрированного в Небе пользователя;
* @pass@ - его пароль
2. В ответе сервера будет находится JSON объект с @{"success": true}@ при успешной аутентификации, либо @{"success": false, "msg": "Текст с причиной, почему не удалось аутентифицировать пользователя"}@.
3. В заголовке ответа сервер проставляет cookie с именем @sessionid@, которое является сессионным ключом данного пользователя. Здесь же сервер указывает максимальный срок жизни сессии - параметр @expires@.
!loging-sessionid.png!
Полученный @sessionid@ необходимо указывать в cookie всех последующих HTTP-запросов к серверу.
h2. Пример аутентификации с последующим получением списка пользователей в аккаунте
Пример приведен на языке python
<pre>
<code class="python">
#coding:utf-8
import urllib, urllib2 # библиотека python, которая позволяет работать с HTTP запросами
import json
def authenticate(username, password):
u"""
Аутентифицирует пользователя в Небе, возвращая строку с sessionid. При неудачной попытке
возвращается None
"""
# формируем параметры запроса и энкодим их, чтобы они нормально ушли как параметры GET запроса
params = urllib.urlencode({
'login': username,
'pass': password
})
# объект запроса
req = urllib2.Request('https://nebopro.ru/loging', params)
# получаем ответ
resp = urllib2.urlopen(req)
# читаем содержимое ответа
data = json.loads(resp.read())
result = None
if data['success']:
# чтобы не уклоняться от темы, выделим sessionid таким решительным способом
result = unicode(resp.headers['Set-Cookie'], 'utf-8')[10:42]
return result
def get_users(sessionid):
u""" Возвращает JSON объект со списком пользователей """
req = urllib2.Request('https://nebopro.ru/core/nebo-user/list')
req.add_header('Cookie', "sessionid=" + sessionid + ';')
resp = urllib2.urlopen(req)
result = None
if resp.getcode() == 200 and resp.info().type == 'application/json':
# В некоторых случаях, в т.ч. при выполнении запроса с невалидным sessionid сервер вернет
# ответ с типом содержимого text/html. Поэтому, при анализе ответа необходимо проверять
# HTTP-статус ответа 200 и тип ответа application/json
result = json.loads(resp.read())['data']
return result
# получаем ключ доступа
sessionid = authenticate('akvarats@nebopro.ru', '*******')
# получаем список пользователей
users = get_users(sessionid)
</code>
</pre>
Для аккаунта с одним пользователем в @users@ будет следующий список из одного объекта
<pre>
<code class="json">
[{
"profile": {
"status": {
"name": "\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442",
"id": 3
},
"first_name": "akvarats@nebopro.ru",
"last_name": "",
"middle_name": "",
"uid": "*******************",
"is_active": true,
"id": 638,
"phone": "8 927 400 12 05",
"is_admin": true,
"full_name": "akvarats@nebopro.ru",
"phone_binding": {
"can_bind_phone": false,
"phone_bound": false
},
"auth_user": {
"id": 647
},
"email": "akvarats@nebopro.ru"
},
"cnt": {
"itsme": true,
"hd": false
},
"permissions": [{
"chapter": null,
"access_level": {
"name": "\u041f\u043e\u043b\u043d\u044b\u0439",
"id": 1
},
"company": {
"name": "akvarats@nebopro.ru",
"profiles": [],
"id": 668
}
}, {
"chapter": null,
"access_level": {
"name": "\u041f\u043e\u043b\u043d\u044b\u0439",
"id": 1
},
"company": {
"name": "\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u0438\u0437 47 \u0440\u0435\u0433\u0438\u043e\u043d\u0430",
"profiles": [],
"id": 9148
}
}, {
"chapter": null,
"access_level": {
"name": "\u041f\u043e\u043b\u043d\u044b\u0439",
"id": 1
},
"company": {
"name": "\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u0438\u0437 78 \u0440\u0435\u0433\u0438\u043e\u043d\u0430",
"profiles": [],
"id": 9149
}
}],
"user": {
"uid": "7951d057-a19f-47d8-afdd-63d66fe85b8e",
"user_fio": "akvarats@nebopro.ru",
"email": "akvarats@nebopro.ru",
"id": 728
},
"phone_binding": {
"can_bind_phone": false,
"phone_bound": false
}
}]
</code>
</pre>
h2. Особенности выполнения REST-операций
Для большинства серверных ресурсов (если рассматривать сервер Неба именно как REST-сервер) заданы следующие операции:
* @list@ получение списка экземпляров ресурса;
* @read@ получение единственного экземпляра ресурса по идентификатору (первичному ключу);
* @create@ - создание нового экземпляра;
* @update@ - изменение существующего экземпляра;
* @delete@ - удаление экземпляра.
Особенностью Неба является то, что требуемая от сервера операция над ресурсом задается не типом запроса (GET, POST, PUT, DELETE), а последней частью соответствующего ресурсу URL'а. Например, при работе с контрагентами мы имеем следующие URL'ы:
* /core/contragents/list
* /core/contragents/read
* /core/contragents/create
* /core/contragents/update
* /core/contragents/delete
Тип запроса (GET или POST) в подавляющем большинстве случаев влияет только на способ и вид передачи параметров: в заголовках (как в GET) или в теле (как в POST).
h3. Операция LIST
Принимает параметры:
* @offset@ (целое, необязательное) - параметр для постраничного вывода результата - смещение курсора в выборке;
* @limit@ (целое, необязательное) - параметр для постраничного вывода результата - количество записей в результате;
* @filter@ (json-объект, необязательно) - задает условие фильтрации (формат описания см. ниже);
* @order@ (строка, необязательно) - наименование поля, по которому сортируется результирующий список.
Возвращает JSON объект вида
<pre>
<code class="json">
{
"success": true,
"data": [{}, {}, {}],
"total": 100500
}
</code>
</pre>
Здесь:
* @success@ (true/false) указывает на успешность выполнения операции
* @data@ - результирующий список объектов
* @total@ - общее количество записей (используется при постраничном чтении данных)
Фильтр на записи задается в виде JSON объекта с деревом выражений-условий
<pre>
<code class="json">
{
"left": left_subexpression,
"op": operator,
"right": right_subexpression
}
</code>
</pre>
В качестве @left_subexpression@ могут выступать либо вложенные подвыражения-условия, либо имя поля, на которое накладывается условие. В @right_subexpression@ - либо подвыражения условия, либо значение, с которым сравнивается значение в поле. В случае, если слева-справа указаны подвыражения, то в @operator@ указывается логическое условие. Иначе, указывается оператор сравнения.
В условиях фильтра допустимо задавать следующие виды операторов:
* @and@ - *логическое И*. В @left@ и @right@ ожидаются вложенные объекты с условиями;
* @or@' - *логическое ИЛИ*. В @left@ и @right@ ожидаются вложенные объекты с условиями;
* @not@ - *логическое НЕ*. В @left@ ожидается условие, на которое накладывается отрицание. В @right@ ожидается null;
* @eq@ - значение в поле с именем из @left@ должно быть *равно* значению из @right@;
* @neq@ - значение в поле с именем из @left@ должно быть *не равно* значению из @right@;
* @lt@ - значение в поле с именем из @left@ должно быть *меньше* значения из @right@;
* @lte@ - значение в поле с именем из @left@ должно быть *меньше или равно* значения из @right@;
* @gt@ - значение в поле с именем из @left@ должно быть *больше* значения из @right@;
* @gte@ - значение в поле с именем из @left@ должно быть *больше или равно* значения из @right@;
* @icontains@ - значение в поле с именем из @left@ должно *регистронезависимо содержать* значения из @right@ (используется для сравнения строк);
* @iexact@ - значение в поле с именем из @left@ должно *регистронезависимо совпадать* со значения из @right@ (используется для сравнения строк);
* @in@ - значение в поле с именем из @left@ должно *содержаться* в списке значений из @right@;
* @startswith@ - значение в поле с именем из @left@ должно *начинаться* значением из @right@ (используется для сравнения строк);
* @istartswith@ - значение в поле с именем из @left@ должно *регистронезависимо начинаться* значением из @right@ (используется для сравнения строк);
* @endswith@ - значение в поле с именем из @left@ должно *заканчиваться* значением из @right@ (используется для сравнения строк);
* @iendswith@ - значение в поле с именем из @left@ должно *регистронезависимо заканчиваться* значением из @right@ (используется для сравнения строк);
* @fts@ - оператор псевдо-полнотекстового поиска. Выполняет регистро-независимый поиск по вхождению строки из @right@ по совокупности полей, которые определены для серверного ресурса. Значение в @left@ при этом игнорируется. строк).
Пример запроса для нахождения контрагента, ИНН которого равен "0000000000":
!filter-example1.png!
В случае, если необходимо найти контрагента по двум условиям (например, ИНН равен 0000000000 и КПП равен 000000000), то объект фильтра выглядит следующим образом:
<pre>
<code class="json>
{
"left": {"left": "inn", "op": "eq", "right": "0000000000"},
"op": "and",
"right": {"left": "kpp", "op": "eq", "right": "000000000"}
}
</code>
</pre>
Комплексные условия фильтров можно задавать с помощью следующих сокращенных записей:
<pre>
<code class="json">
{
"type": "array-and",
"data": [{"left": "inn", "op": "eq", "right": "0000000000"}, {"left": "kpp", "op": "eq", "right": "000000000"}]
}
</code>
</pre>
Здесь @type: "array-and"@ указывает на то, что объекты с условиями из @data@ должны соединиться условием AND. Аналогично, для записывания каскада условий, соединенных OR, используется @type: "array-or"@.
h2. Общие сведения об API Неба
Сервер Неба поддерживает взаимодействие с внешними приложениями с использованием REST-подобного API. По сути, это же API используется для работы клиентского (браузерного) приложения Неба, что позволяет внешним приложениям реализовать все те же действия, что и пользователь в основном интерфейсе Неба.
При использовании текущей версии API Неба необходимо осознавать, что оно предназначено в первую очередь для функционирования основного клиентского приложения Неба. Все спорные и неоднозначные моменты в данном API возникли из-за того, что в первую очередь максимизировалось удобства разработки и функционирования именно клиентского приложения.
Текущая версия API пока не является фиксированной. Возможны изменения в составе передаваемых данных без предварительного уведомления взаимодействующих внешних приложений. Фиксация API по разделам Неба и последующая его версионность будут реализованы в течение 2014 года.
Также помните, что на текущий момент времени "защита от дурака" в Небе равномерно распределена между клиентским и серверным приложением. Значительная часть ограничений на использование Неба (в основном, некритичных для безопасности и целостности данных) реализованы путем выбора специфической схемы организации клиентского приложения, без их дублирования на серверной части. Поэтому, перед использованием тех или иных операций желательно убедиться, что они не противоречат логике предметной области и текущего поведения Неба в целом (например, посмотрев как требуемая операция реализована в интерфейсе Неба).
h2. Аутентификация внешнего приложения
Для аутентификации пользователей применяется общепринятая для Web-приложений схема.
1. Внешнее приложение отсылает GET или POST запрос на URL @https://nebopro.ru/loging@ передавая в параметрах запроса следующие значения:
* @login@ - email зарегистрированного в Небе пользователя;
* @pass@ - его пароль
2. В ответе сервера будет находится JSON объект с @{"success": true}@ при успешной аутентификации, либо @{"success": false, "msg": "Текст с причиной, почему не удалось аутентифицировать пользователя"}@.
3. В заголовке ответа сервер проставляет cookie с именем @sessionid@, которое является сессионным ключом данного пользователя. Здесь же сервер указывает максимальный срок жизни сессии - параметр @expires@.
!loging-sessionid.png!
Полученный @sessionid@ необходимо указывать в cookie всех последующих HTTP-запросов к серверу.
h2. Пример аутентификации с последующим получением списка пользователей в аккаунте
Пример приведен на языке python
<pre>
<code class="python">
#coding:utf-8
import urllib, urllib2 # библиотека python, которая позволяет работать с HTTP запросами
import json
def authenticate(username, password):
u"""
Аутентифицирует пользователя в Небе, возвращая строку с sessionid. При неудачной попытке
возвращается None
"""
# формируем параметры запроса и энкодим их, чтобы они нормально ушли как параметры GET запроса
params = urllib.urlencode({
'login': username,
'pass': password
})
# объект запроса
req = urllib2.Request('https://nebopro.ru/loging', params)
# получаем ответ
resp = urllib2.urlopen(req)
# читаем содержимое ответа
data = json.loads(resp.read())
result = None
if data['success']:
# чтобы не уклоняться от темы, выделим sessionid таким решительным способом
result = unicode(resp.headers['Set-Cookie'], 'utf-8')[10:42]
return result
def get_users(sessionid):
u""" Возвращает JSON объект со списком пользователей """
req = urllib2.Request('https://nebopro.ru/core/nebo-user/list')
req.add_header('Cookie', "sessionid=" + sessionid + ';')
resp = urllib2.urlopen(req)
result = None
if resp.getcode() == 200 and resp.info().type == 'application/json':
# В некоторых случаях, в т.ч. при выполнении запроса с невалидным sessionid сервер вернет
# ответ с типом содержимого text/html. Поэтому, при анализе ответа необходимо проверять
# HTTP-статус ответа 200 и тип ответа application/json
result = json.loads(resp.read())['data']
return result
# получаем ключ доступа
sessionid = authenticate('akvarats@nebopro.ru', '*******')
# получаем список пользователей
users = get_users(sessionid)
</code>
</pre>
Для аккаунта с одним пользователем в @users@ будет следующий список из одного объекта
<pre>
<code class="json">
[{
"profile": {
"status": {
"name": "\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442",
"id": 3
},
"first_name": "akvarats@nebopro.ru",
"last_name": "",
"middle_name": "",
"uid": "*******************",
"is_active": true,
"id": 638,
"phone": "8 927 400 12 05",
"is_admin": true,
"full_name": "akvarats@nebopro.ru",
"phone_binding": {
"can_bind_phone": false,
"phone_bound": false
},
"auth_user": {
"id": 647
},
"email": "akvarats@nebopro.ru"
},
"cnt": {
"itsme": true,
"hd": false
},
"permissions": [{
"chapter": null,
"access_level": {
"name": "\u041f\u043e\u043b\u043d\u044b\u0439",
"id": 1
},
"company": {
"name": "akvarats@nebopro.ru",
"profiles": [],
"id": 668
}
}, {
"chapter": null,
"access_level": {
"name": "\u041f\u043e\u043b\u043d\u044b\u0439",
"id": 1
},
"company": {
"name": "\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u0438\u0437 47 \u0440\u0435\u0433\u0438\u043e\u043d\u0430",
"profiles": [],
"id": 9148
}
}, {
"chapter": null,
"access_level": {
"name": "\u041f\u043e\u043b\u043d\u044b\u0439",
"id": 1
},
"company": {
"name": "\u041a\u043e\u043c\u043f\u0430\u043d\u0438\u044f \u0438\u0437 78 \u0440\u0435\u0433\u0438\u043e\u043d\u0430",
"profiles": [],
"id": 9149
}
}],
"user": {
"uid": "7951d057-a19f-47d8-afdd-63d66fe85b8e",
"user_fio": "akvarats@nebopro.ru",
"email": "akvarats@nebopro.ru",
"id": 728
},
"phone_binding": {
"can_bind_phone": false,
"phone_bound": false
}
}]
</code>
</pre>
h2. Особенности выполнения REST-операций
Для большинства серверных ресурсов (если рассматривать сервер Неба именно как REST-сервер) заданы следующие операции:
* @list@ получение списка экземпляров ресурса;
* @read@ получение единственного экземпляра ресурса по идентификатору (первичному ключу);
* @create@ - создание нового экземпляра;
* @update@ - изменение существующего экземпляра;
* @delete@ - удаление экземпляра.
Особенностью Неба является то, что требуемая от сервера операция над ресурсом задается не типом запроса (GET, POST, PUT, DELETE), а последней частью соответствующего ресурсу URL'а. Например, при работе с контрагентами мы имеем следующие URL'ы:
* /core/contragents/list
* /core/contragents/read
* /core/contragents/create
* /core/contragents/update
* /core/contragents/delete
Тип запроса (GET или POST) в подавляющем большинстве случаев влияет только на способ и вид передачи параметров: в заголовках (как в GET) или в теле (как в POST).
h3. Операция LIST
Принимает параметры:
* @offset@ (целое, необязательное) - параметр для постраничного вывода результата - смещение курсора в выборке;
* @limit@ (целое, необязательное) - параметр для постраничного вывода результата - количество записей в результате;
* @filter@ (json-объект, необязательно) - задает условие фильтрации (формат описания см. ниже);
* @order@ (строка, необязательно) - наименование поля, по которому сортируется результирующий список.
Возвращает JSON объект вида
<pre>
<code class="json">
{
"success": true,
"data": [{}, {}, {}],
"total": 100500
}
</code>
</pre>
Здесь:
* @success@ (true/false) указывает на успешность выполнения операции
* @data@ - результирующий список объектов
* @total@ - общее количество записей (используется при постраничном чтении данных)
Фильтр на записи задается в виде JSON объекта с деревом выражений-условий
<pre>
<code class="json">
{
"left": left_subexpression,
"op": operator,
"right": right_subexpression
}
</code>
</pre>
В качестве @left_subexpression@ могут выступать либо вложенные подвыражения-условия, либо имя поля, на которое накладывается условие. В @right_subexpression@ - либо подвыражения условия, либо значение, с которым сравнивается значение в поле. В случае, если слева-справа указаны подвыражения, то в @operator@ указывается логическое условие. Иначе, указывается оператор сравнения.
В условиях фильтра допустимо задавать следующие виды операторов:
* @and@ - *логическое И*. В @left@ и @right@ ожидаются вложенные объекты с условиями;
* @or@' - *логическое ИЛИ*. В @left@ и @right@ ожидаются вложенные объекты с условиями;
* @not@ - *логическое НЕ*. В @left@ ожидается условие, на которое накладывается отрицание. В @right@ ожидается null;
* @eq@ - значение в поле с именем из @left@ должно быть *равно* значению из @right@;
* @neq@ - значение в поле с именем из @left@ должно быть *не равно* значению из @right@;
* @lt@ - значение в поле с именем из @left@ должно быть *меньше* значения из @right@;
* @lte@ - значение в поле с именем из @left@ должно быть *меньше или равно* значения из @right@;
* @gt@ - значение в поле с именем из @left@ должно быть *больше* значения из @right@;
* @gte@ - значение в поле с именем из @left@ должно быть *больше или равно* значения из @right@;
* @icontains@ - значение в поле с именем из @left@ должно *регистронезависимо содержать* значения из @right@ (используется для сравнения строк);
* @iexact@ - значение в поле с именем из @left@ должно *регистронезависимо совпадать* со значения из @right@ (используется для сравнения строк);
* @in@ - значение в поле с именем из @left@ должно *содержаться* в списке значений из @right@;
* @startswith@ - значение в поле с именем из @left@ должно *начинаться* значением из @right@ (используется для сравнения строк);
* @istartswith@ - значение в поле с именем из @left@ должно *регистронезависимо начинаться* значением из @right@ (используется для сравнения строк);
* @endswith@ - значение в поле с именем из @left@ должно *заканчиваться* значением из @right@ (используется для сравнения строк);
* @iendswith@ - значение в поле с именем из @left@ должно *регистронезависимо заканчиваться* значением из @right@ (используется для сравнения строк);
* @fts@ - оператор псевдо-полнотекстового поиска. Выполняет регистро-независимый поиск по вхождению строки из @right@ по совокупности полей, которые определены для серверного ресурса. Значение в @left@ при этом игнорируется. строк).
Пример запроса для нахождения контрагента, ИНН которого равен "0000000000":
!filter-example1.png!
В случае, если необходимо найти контрагента по двум условиям (например, ИНН равен 0000000000 и КПП равен 000000000), то объект фильтра выглядит следующим образом:
<pre>
<code class="json>
{
"left": {"left": "inn", "op": "eq", "right": "0000000000"},
"op": "and",
"right": {"left": "kpp", "op": "eq", "right": "000000000"}
}
</code>
</pre>
Комплексные условия фильтров можно задавать с помощью следующих сокращенных записей:
<pre>
<code class="json">
{
"type": "array-and",
"data": [{"left": "inn", "op": "eq", "right": "0000000000"}, {"left": "kpp", "op": "eq", "right": "000000000"}]
}
</code>
</pre>
Здесь @type: "array-and"@ указывает на то, что объекты с условиями из @data@ должны соединиться условием AND. Аналогично, для записывания каскада условий, соединенных OR, используется @type: "array-or"@.