Зачем нужен rest api?
Сервер всё же полезная в хозяйстве вещь. Он может повыполнять какой-нибудь полезный код.
row level securityКакая это работает? Например, строки пользователь не может видеть, а их сумму (или более сложную функцию) может.
Сервер всё же полезная в хозяйстве вещь. Он может повыполнять какой-нибудь полезный код.Напишу, как я реализую это, на примере посылки личных сообщений.
Необходимо, чтобы при посылке личного сообщения, также отправлялся email.
Многие для реализации такой функциональности сделают процедуру на уровне веб-сервера, которая вставляет запись в базу данных и шлет email. Это неправильный способ, так как если почтовый сервер лежит, то мы либо не отправим письмо, но отправим личное сообщение, либо вообще не отправим личное сообщение.
Правильный способ - должна быть скрытая от пользователей таблица отправленных емейлов. При вставке в таблицу личных сообщений, триггер должен вставлять в таблицу емейлов запись. Заводим cron job, который логинится в базу из-под специального пользователя, который видит только таблицу email, выгребает email, которым требуется отправка, шлет их и ставит статус "отправлено".
Можно обойтись без триггеров, тогда надо отобрать у пользователей право на вставку в таблицу личных сообщений, и дать право на выполнение процедуры, которая будет вставлять в таблицу личных сообщений и в таблицу email. Подход аналогичен работе с файлом /etc/passwd в unix.
Какая это работает? Например, строки пользователь не может видеть, а их сумму (или более сложную функцию) может.Заводим вьюху, которая считает сумму, даем право на чтение из этой вьюхи.
процедуры, которая будет вставлять в таблицу личных сообщений и в таблицу email.Это еще не сервер?
Тебе это так принципиально, что это работает внутри базы?
Как будет делаться, например, отправка сообщения в личку другого пользователя?
> Заводим cron job [..] выгребает email, которым требуется отправка, шлет их и ставит статус "отправлено"
С каким периодом будет запускаться этот job?
Вспоминается довольно известная презентация Дмитрия Зуйкова . Он правда еще там между PostgreSQL и клиентом воткнул прокси на Haskell, но тут уж каждый делает как хочет.
Как ты собираешься отдавать с сервера структуры сложнее табличных? Допустим деревянное что-то, оно у тебя на клиента поедет в виде таблицы со ссылкой на родителя?
Как решится вопрос с обновлением кода сервера без простоя и с несколькими версиями кода, работающими одновременно (если конечно тебе такое надо)?
Если надо будет сделать HA или данные перестанут влезать на один сервер СУБД, то что делать?
еще инетересно, что делать, если часть нужных данных лежит не в БД, а где-то еще, куда доступа у юзера нет. Из хранимки сокеты юзать?
Надо ли мне делать rest api, либо просто сделать http обертку, которая даст возможность отправлять по http любые sql запросы?Жопа на жопе. Всех пользователей придется в БД заводить? Интересно, как будет форма регистрации работать, к примеру.
Где и как будет идентифицироваться пользовать от чьего имени выполняется запрос?Пользователи заводятся в БД. HTTP прослойка извлекает credentials из кук, логинится в БД из-под пользователя и делает от его имени запросы.
> Заводим cron job [..] выгребает email, которым требуется отправка, шлет их и ставит статус "отправлено"Для данного форума, раз в пару секунд будет нормально.
С каким периодом будет запускаться этот job?
Как решится вопрос с обновлением кода сервера без простоя и с несколькими версиями кода, работающими одновременно (если конечно тебе такое надо)?Зависит от СУБД. Я ориентируюсь на PostgreSQL, там я могу задеплоить новую версию вьюх и хранимых процедур в новую схему. Часть пользователей может работать с новой схемой, часть со старой. Разумеется, это будет работать только если в новой версии формат таблиц не поменялся.
Если надо будет сделать HA или данные перестанут влезать на один сервер СУБД, то что делать?Мое решение не подходит для сильно нагруженных веб-проектов. Хотя что считать нагруженным веб проектом? stackoverflow.com работает с одного сервера СУБД, у них миллиард просмотров в месяц.
Как ты собираешься отдавать с сервера структуры сложнее табличных? Допустим деревянное что-то, оно у тебя на клиента поедет в виде таблицы со ссылкой на родителя?Синхронизация данных между клиентом и сервером при таком подходе - самое интересное. Данные на клиент должны передаваться в виде обычных плоских таблиц, эти данные можно класть в локальную sqlite (которая есть на айфоне, андроиде и в некоторых бразуерах). При желании, можно выполнять одни и те же запросы на клиенте и на сервере. Например, нам надо отрендерить тред в форуме. Мы делаем запросы на получение сообщений в треде и на получение пользователей, которые комментили в тред. У нас теперь на клиенте есть данные в таблицах users и posts. Мы можем написать sql запросы (которые будут выполняться локально, по уже полученным данным которые выведут данные в любой форме, какую придумает проектировщик интерфейсов - дерево, плоский список, группировка по пользователю.
Таким образом, нам практически не требуется кодить - надо только написать запросы и html шаблоны.
При редактировании, мы сначала меняем данные в локальной БД, и затем делаем sync - из локальной базы удаленные/измененные/добавленные строки отправляются на сервер.
При редактировании, мы сначала меняем данные в локальной БД, и затем делаем sync - из локальной базы удаленные/измененные/добавленные строки отправляются на сервер.как ты ID для новых сообщений в удаленной базе синкать с локальными?
как будет, скажем, выглядеть вход на форум через, скажем, вконтактик (нонче это модно)?
credentials в куках => любой прохожий сможет их узнать, если ты забыл залочить экран отходя от компа?
куки доступны из javascript-а + слабоконтролируемый тобой контент => когда-нибудь твои куки тупо утекут к вежливым людям (вспоминаем ежика (?) и его юзер-скрипт)
+ ну и про приваты
приват - это одна строчка?
если да, то у кого есть права на ее модификацию? или нужен уже не row-level, а cell-level?
если две, то, например, я не смогу отменить посылку (как это можно сделать сейчас на этом форуме) - или придется городить огород с вьющками/хранимками
еще rest хорошо совмещается с отладкой через Angular/Backbone
а писать sql-crud'ы для телефона - это прошлый век
а писать sql-crud'ы для телефона - это прошлый векКак ты предлагаешь писать html5 клиент к форуму (оставляя за скобками вопрос, зачем это нужно)?
Как я вижу, сейчас решение - с одной стороны посадить php макаку, а с другой - js макаку. PHP макака будет с помощью какого-то фреймворка транслировать
GET /threads/:id/posts/byDate/desc
в
select * from posts where thread_id=:id order by date desc
С другого конца js-макака будет педалить монады на джаваскрипте.
Хотя, фактически, требуется спроектировать базу данных и написать html, говнокод на императивных языках не нужен.
на C# этот форум написан ()
на С/C++ Пианист не осилил написать этот форум
когда напишешь форум без императивного языка, приходи будет о чем поговорить, а сейчас не о чем говорить
HTTP прослойка извлекает credentials из кук, логинится в БД из-под пользователя и делает от его имени запросы.Где и как пользователь переводится в его id?
1. Ради безопасности
2. Ради возможности изменения базы и серверных сервисов без необходимости модификации клиента
3. Ради того, чтобы серверной код писался на универсальном языке, а не на sql
4. Ради оптимизации трафика между сервером и клиентом
5. Ради самодокументированности
Забив на п. 3 и оставив остальные причины - получаем следующую архитектуру:
Весь код перемещается в sql-процедуры. Клиенту разрешается вызывать только эти процедуры и не разрешается прямой доступ к таблицам (и, соответственно, свободное манипулирование командами select/insert/update/delete).
Самое забавное, что такое api автоматически кладется на недо-rest: каждая процедура Zzz мапится на путь http://server/Zzz, а аргументы мапятся или так: http://server/Zzz?arg1=<arg1>&arg2=<arg2>, или так http://server/Zzz/<arg1>/<arg2>, или в виде комбинации обоих подходов. Afaik, у Oracle-а, которые поощряют такой подход с написание серверного кода внутри БД - был сервис, который делает что-то подобное и мапит sql-процедуры на недо-rest.
ps
Под недо-rest-ом я подразумеваю вариант, когда используются только http-команды: get и post, и не используются команды put, delete.
1. Ради безопасностиВ моем решении (права на уровне таблиц и строк) с безопасностью все на порядок лучше, чем у общепринятого подхода (проверять на уровне веб-сервера, что у пользователя есть права на выполнение некой процедуры).
В общепринятом подходе любой sql injection компрометирует сразу все данные, которые есть в базе.
Если есть 100 процедур для доступа к данным, и хотя бы одна забыла проверить права доступа, то можно украсть данные через эту процедуру. В моем решении, права доступа - свойство самих данных, а не процедур для работы с ними.
хотя бы одна забыла проверить права доступа, то можно украсть данные через эту процедуру.зачем пихать общую логику в каждую процедуру? это твои "современные" языки так учат?
В моем решении, права доступа - свойство самих данных, а не процедур для работы с ними.И как ты это реализуешь, например, для тредов и сообщений приват-разделов? И на основе какой БД?
Каждое сообщение (запись в таблице) такого раздела должна быть видна определенному набору пользователей, но не видна всем остальным.
И как ты это реализуешь, например, для тредов и сообщений приват-разделов? И на основе какой БД?http://www.postgresql.org/docs/devel/static/ddl-rowsecurity....
Каждое сообщение (запись в таблице) такого раздела должна быть видна определенному набору пользователей, но не видна всем остальным.
ps
Насколько быстро, кстати, postgreSql деградирует по производительности от кол-ва row-policy наложенных на таблицу?
- распределение производительности между пользователями
- ddos
- защита от слива БД
?
Получается любой пользователь может положить всю базу - сделав просто запрос: select * from message (или любая другая большая таблица)
Получается любой пользователь может положить всю базу - сделав просто запрос: select * from message (или любая другая большая таблица)Можно поставить лимит на время выполнения запроса - типа 1 секунда. С каждым превышением банить пользователя на все более продолжительные промежутки времени.
http://facebook.github.io/react/blog/2015/02/20/introducing-...
http://gist.github.com/wincent/598fa75e22bdfa44cf47
Вкратце, фейсбук (по крайней мере в части своих сервисов) не использует rest - у них один сервис, который умеет отвечать на graphql запросы.
С точки зрения фейсбука, код должен выглядеть так:
http://speakerd.s3.amazonaws.com/presentations/7af7c2f33bf9451a892dcd91de55b7c2/slide_67.jpg
Как видим, у них graphql запрос прямо внутри клиентсвкого javascript кода. Делаем теперь поправку на то, что у фейсбука какой-то свой нереляционный бэкенд, и делаем вывод, что если мы уже и так используем реляционную БД, то бойлерплейт для оборачивания sql запросов в rest api не нужен - можно отправлять запросы прямо с клиента.
бойлерплейтв C#+ReSharper боллерплайт не проблема, даже наоборот когда явно прописан весь трубопровод, то можно сделать вставку в любое место трубопровода, что часто требуется, а не искать в поисковике какую настройку фрейворка или посгреса надо крутить
пост ты сознательно проигнорировал?
как ты ID для новых сообщений в удаленной базе синкать с локальными?Использовать распределенную генерацию id. Вообще, про локальную базу я написал в контексте кое-каких своих задач - например с помощью нее можно реализовать offline работу. Для большинства задач она не нужна.
как будет, скажем, выглядеть вход на форум через, скажем, вконтактик (нонче это модно)?Для простоты опишу такую схему: в базе ставим trusted аутентификацию (любой кто может подключиться к базе может представиться кем угодно). Настраиваем, чтобы к базе мог подключиться только веб сервер. Дальше, аутентификация перекладывается на http прослойку - она может аутентифицировать через vkontakte, выписывать временные токены, итд - также как в 99.9% современных веб-приложений. Безопасность такой схемы очень низкая - любой remote code execution в веб-сервере ведет к сливу или модификации всей базы данных. В случае с аутентификацией на уровне БД, разрушительные последствия от remote code execution куда меньше, но большинство баз данных не умеют vkontakte авторизацию.
credentials в куках => любой прохожий сможет их узнать, если ты забыл залочить экран отходя от компа?Я ничего не понял.
куки доступны из javascript-а + слабоконтролируемый тобой контент => когда-нибудь твои куки тупо утекут к вежливым людям (вспоминаем ежика (?) и его юзер-скрипт)
приват - это одна строчка?Уверен, что в текущей реализации форума приват - одна строчка (author, recipient, message, date).
если да, то у кого есть права на ее модификацию? или нужен уже не row-level, а cell-level?
Пользователь может смотреть приват, если author = current_user or recipient = current_user.
Пользователь может редактировать приват, если author = current_user. Таким образом, писать вьюхи и хранимые процедуры не надо. В более сложных случаях это может понадобиться, но я бы назвал это не огородом, а самым минималистичным, прозрачным и понятным способом реализовать бизнес-логику.
Разумеется, настраиваем что при редактировании привата редактировать можно только поле message - иначе можно будет подделать приват от другого юзера.
в C#+ReSharper боллерплайт не проблема, даже наоборот когда явно прописан весь трубопровод, то можно сделать вставку в любое место трубопровода, что часто требуется, а не искать в поисковике какую настройку фрейворка или посгреса надо крутитьДействительно, зачем изучать готовые решения - можно все самому на C#+Resharper написать, и бойлерплейт не проблема.
аутентификация перекладывается на http прослойку
ну вот у тебя уже появилась "прослойка", т.е. не только напрямую sql-запросы из браузера, чтд
потом в этой прослойке появится еще куча всего, чего не сделаешь напрямую через sql-запросы из браузера
если я тебя правильно понял, ты предлагаешь в куках хранить инфу, необходимую для логина. но куки может посмотреть любой проходящий мимо человек, если ты, уходя за кофе, забыл залочить компcredentials в куках => любой прохожий сможет их узнать, если ты забыл залочить экран отходя от компа?Я ничего не понял.
куки доступны из javascript-а + слабоконтролируемый тобой контент => когда-нибудь твои куки тупо утекут к вежливым людям (вспоминаем ежика (?) и его юзер-скрипт)
далее, ты предлагаешь эту инфу юзать из ява-скрипта => любой ява-скрипт код, так или иначе оказавшийся на странице, может эту инфу считать и послать куда надо (как пример - в этом форуме есть фича с подключением чужих "полезных" скриптов; +всякие говноаддоны к браузерам; +ты показываешь контент (посты сгенеренный другими пользователями - ты не можешь быть уверен, что 100% все заескейпишь; + у модеров есть (была) возможность фигачить html-код)
Уверен, что в текущей реализации форума приват - одна строчка (author, recipient, message, date).
ты вообще приватами на форуме пользовался?
Пользователь может редактировать приват, если author = current_user.
это вообще бред, безотносительно этого форума
Действительно, зачем изучать готовые решения - можно все самому на C#+Resharper написать, и бойлерплейт не проблема.Готовые решения какой задачи? Твоей задачи готового решения нет, иначе у тебя бы ее не заказывали.
Еще Брукс говорил, что если в библиотеке дольше разбираться, чем написать самому, то не стоит использовать библиотеку.
далее, ты предлагаешь эту инфу юзать из ява-скрипта => любой ява-скрипт код, так или иначе оказавшийся на странице, может эту инфу считать и послать куда надоgoogle: HTTPOnly
чукча не читатель? я прекрасно знаю про httponly, но ТС-у нужен доступ к этой инфе из JS
протупил
ты вообще приватами на форуме пользовался?Реализация приватов без использования фич 9.5, на чистом 9.4. Пользователь может выполнять любые запросы к my_inbox и my_outbox.
drop table if exists private_message cascade;
drop table if exists inbox;
drop table if exists outbox;
drop view if exists my_inbox;
drop view if exists my_outbox;
drop role if exists forumuser;
drop user if exists "";
drop user if exists "";
drop user if exists "";
-- Роль для обычных пользователей форума
create role forumuser nologin;
-- Тестовые пользователи
create user "" in role forumuser;
create user "" in role forumuser;
create user "" in role forumuser;
/*
Таблица сообщений. Фиксирует тот факт, что некий пользователь отправил
сообщение другому пользователю. Таблица работает только на добавление.
Таблица не видна forum_user
*/
create table private_message(
id serial not null primary key,
author text not null,
recipient text not null,
message text not null
);
/*
Таблица входящих сообщений. Фиксирует тот факт, что некое сообщение из
private_message есть в инбоксе у того пользователя, которому оно отправлено.
Эта и следующая таблицы нужны, чтобы пользователи могли чистить свои ящики
независимо друг от друга.
Таблица не видна forumuser
*/
create table inbox(
private_message_id integer primary key,
foreign key(private_message_id) references private_message(id)
);
/*
То же самое, что и inbox, но для исходящих сообщений
*/
create table outbox(
private_message_id integer primary key,
foreign key(private_message_id) references private_message(id)
);
/*
Пара вьюх - входящие и исходящие сообщения - которые представляют API на
чтение сообщений для forumuser, который может делать любые select запросы к
этим вьюхам - чужих сообщений он все равно не увидит.
*/
create view my_inbox with (security_barrier) as
select
private_message_id, author, recipient, message
from private_message pm, inbox
where inbox.private_message_id = pm.id and pm.recipient = session_user;
grant select on my_inbox to forumuser;
create view my_outbox with (security_barrier) as
select
private_message_id, author, recipient, message
from private_message pm, outbox
where outbox.private_message_id = pm.id and pm.author = session_user;
grant select on my_outbox to forumuser;
/*
Посылка сообщений. Благодаря security definer процедура выполняется из-под
рута, поэтому она может вставлять в таблицы private_message, inbox, outbox,
но права на исполнение процедуры есть у forumuser.
*/
create or replace function send_message(
msg text,
recip text
) returns void as $$
with new_message as (
insert into private_message(message, author, recipient)
values(msg, session_user, recip)
returning id
new_inbox as (
insert into inbox(private_message_id) select id from new_message
)
insert into outbox(private_message_id) select id from new_message;
$$ language sql security definer;
grant execute on function send_message(text, text) to forumuser;
ЗЫ понятно, что приваты можно сделать на вьюхах и хранимках - не очень понятно, в чем тут "принципиальное" отличие от прослойки на любом другом языке. Гораздо интересней, что ты будешь делать, если надо будет сделать запрос "вовне" (логин через VK - как раз пример "из жизни"). Будешь сокеты юзать? или городить очередь с задачами и кроном/демоном на норм языке?
ЗЫ понятно, что приваты можно сделать на вьюхах и хранимках - не очень понятно, в чем тут "принципиальное" отличие от прослойки на любом другом языке.Тут надо различать вьюхи и хранимки. Хранимки принципиально мало чем отличаются от какого-нибудь PHP кода (хотя я все равно предпочел бы хранимки, так как они требуют меньше бойлерплейта на открывание/закрывание транзакций, преобразование резалтсетов в объекты etc). Вьюхи - другое дело, потому что ко вьюхам ты можешь делать любые SQL запросы.
Приведу пример из жизни. На днях я захотел, чтобы джира мне показывала только те задачи, которые не заблокированы другими задачами. Из коробки такой функциональности нет. Но джира, если кто не знает, позволяет писать запросы на каком-то кастрированном диалекте sql. Выглядит это так.
Я думал, что смогу легко добиться желаемого - запрос, как мне казалось, должен выглядеть примерно так:
select * from issues where issue_id not in (
select target_issue_id from issue_links where link_type='block'
)
Но оказалось, что джира не позволяет писать такие запросы без платных плагинов, которых существует даже несколько разных штук.
Вся эта ситуация мне показалась метафорой современного веб-программирования. Есть база данных, которая умеет быстро исполнять запросы на понятном SQL. Есть понятный html, который можно сгенерить, подставив данные, вытащенные SQL запросом, в шаблон. Пока все просто и понятно. Но тут появляются программисты на джаве. Они что-то долго делают, пишут миллионы строк кода, и в результате их работы:
1) Мы теряем возможность писать произвольные SQL запросы, которые поддерживает любая СУБД, на которую ставится джира.
2) Все начинает дичайше тормозить и жрать память. Джира для маленького проекта (несколько тысяч задач) сжирает во много раз больше памяти чем вся эта несчастная база задач.
вместе с большой силой приходит большая ответственность. Возможность писать произвольный sql код даже при условии что нет проблем из-за несанкционированного доступа к данным приводит к тому что пользователь может одним сильно кривым запросом поставить раком БД и устроить отказ в обслуживании всех остальных пользователей. Т.е. к твоему row level security нужно ещё приделать как минимум квоты на использование сервером БД памяти и процессора для каждого пользователя.
Мне кажется, просто поставить разумный таймаут на запрос было бы достаточно.
На сайте джиры написано, что у них самые большие инсталляции - пара сотен тысяч задач. То есть объемы данных смешные, все влезает в память моего мобильного телефона.Дело не только в памяти, но и в том, что большое кол-во запросов имеет сложность n-квадрат и более
n-квадратичный запрос на 100тыс. записей даже при размещении всех записей в памяти будет выполняться более секунды и на это же время целиком сожрет процессор.
На сайте джиры написано, что у них самые большие инсталляции - пара сотен тысяч задач.мне почему-то кажется, что пара сотен тысяч задач - это скорее похоже на большой проект, а не всю джиру. Самый банальный пример - issues.apache.org/jira - там задач около миллиона.
миллион это много?
это больше, чем пара сотен тысяч, а значит либо лжет, либо на сайте джиры написана ерунда.
Детали можно уточнять, но в целом прав. Java по сомнительным причинам оборачивает SQL, в итоге получаются тормоза.
Java по сомнительным причинам оборачивает SQL, в итоге получаются тормоза.у тебя есть несколько примеров популярного ПО на других языках, предоставляющих любому аутентифицированному пользователю возможность написать произвольный sql запрос к своей БД? Специально чтобы исключить клоунаду - это должно быть не основной функцией ПО, как в phpMyAdmin каком-нибудь, а дополнительной.
Насколько я помню даже controllable query был создан тобой именно для решения этой задачи - упросить оборачивание SQL.
Насколько я помню даже controllable query был создан тобой именно для решения этой задачи - упросить оборачивание SQL.Controllable Query не оборачивает SQL. Работаешь напрямую с SQL.
т.е. этот сайт - специализированное ПО, предоставляющее любому пользователю возможность написать запрос на архивной версии других сайтов этой компании. Это его основная цель, поэтому не подходит, ищи ещё что-нибудь.
Controllable Query не оборачивает SQL. Работаешь напрямую с SQL.из веб-интрефейса?
http://data.stackexchange.com/stackoverflow/query/newкстати хороший пример для того чтобы понять, много это или мало - 1 миллион записей. на stackowerflow, как мы видим, всего-то 9 миллионов вопросов - на каждый телефон это конечно в память не поместится, но на ноутбук-то уж точно влезет
из веб-интрефейса?Если бы было время, я бы поисследовал возможность сделать Controllalble Query для TypeScript. Или если бы на клиенте был доступен C#, то можно было и с клиента Controllalble Query использовать. Короче, пока только исторические препятствия, принципиальных препятствий нет.
Оставить комментарий
luna89
Допустим, я хочу написать приложение под html5, айфон и андроид, которое будет работать с реляционными данными, для примера возьмем этот форум.Надо ли мне делать rest api, либо просто сделать http обертку, которая даст возможность отправлять по http любые sql запросы?
На уровне базы данных я реализую row level security.
Какие минусы у такого решения?