Простой AngularJS
В Lift всегда использовалась лучшая технология серверного проталкивания. Зачем? Он безопасен, хорошо работает с нестабильными соединениями, учитывает ограниченное количество HTTP-соединений между клиентом и сервером и многое другое.
Angular JS — это очень интересный пакет пользовательского интерфейса, который упрощает создание динамических одностраничных приложений, поскольку существует двусторонняя привязка между моделью и пользовательским интерфейсом, чтобы изменения в модели правильно отражались в пользовательском интерфейсе. И вся привязка является декларативной, поэтому, когда вы используете элемент модели в пользовательском интерфейсе, эта часть пользовательского интерфейса всегда обновляется при изменении модели.
Круглые поездки
Стандартный способ создания приложений Angular JS — связать определенные события в клиенте с вызовами REST к серверу, а когда вызовы REST удовлетворены, модель обновляется и пользовательский интерфейс перерисовывается. Звучит разумно.
Но есть проблемы:
- Существует HTTP-соединение, открытое во время вызова REST, и, учитывая ограниченное количество доступных HTTP-соединений от браузера к данному хосту, одновременное открытие нескольких вызовов REST не является оптимальным.
- REST хорошо работает для простых быстрых циклов запросов / ответов, но если вычисления занимают много времени, HTTP-соединение открыто в течение долгого времени, и это иногда портит прокси-серверы, которые часто задерживают HTTP-запросы через 90 секунд — 3 минуты.
- REST не делает потоковую передачу хорошо
- Разработчик должен защищать каждый вызов REST, потому что конечные точки REST всегда открыты, поэтому в каждый вызов должна быть встроена логика управления доступом
В Lift 3 мы представляем идею циклических поездок: запрос на стороне клиента, отправляемый на сервер, где клиент получает обещание потоковой передачи, сервер выполняет свои вычисления, а когда результаты готовы, они передаются клиенту… и если есть несколько результатов, несколько результатов передаются клиенту по мере того, как результаты становятся доступными.
Поездки Лифта туда и обратно имеют следующие преимущества перед простым ОТДЫХОМ:
- Они потребляют гораздо меньше HTTP-соединений
- Они используют технологию Lift Comet Server-Push, чтобы результаты передавались клиенту, даже если результаты вычисляются, пока клиент временно отключен и передается в порядке
- В запросах используются средства Lift Ajax, которые включают в себя усовершенствованные механизмы повтора и дедупликации
- Lift-json выполняет большую часть сериализации JSON — Scala-land автоматически
- Результаты передаются в потоковом режиме, поэтому результаты передаются с сервера на клиент по завершении вычислений на сервере.
Простое путешествие туда и обратно
Давайте посмотрим на реализацию простой циклической загрузки для загрузки и сохранения некоторых данных String. На стороне сервера у нас есть код, который выглядит следующим образом:
// Save the text… we get a JSON blog and manually decode it
// If an exception is thrown during the save, the client automatically
// gets a Failure
def doSave(info: JValue): JValue = {
for {
JString(path) <- info \ "path"
JString(text) <- info \ "text"
} {
// save the text
}
JNull // a no-op
}
// Load the file
def doLoad(fileName: String): String = {
// load the named file, turn it into a String and return it
}
// Associate the server functions with client-side functions
JScript(
JsCrVar("serverFuncs", sess.buildRoundtrip(List[RoundTripInfo](
"save" -> doSave _, "load" -> doLoad _)))
И чтобы вызвать код от клиента:
// Save the file
$scope.save = function() {
serverFuncs.save({path: $scope.curFile, text: $scope.curText});
}
// load the named file and update the model
// when the file arrives
$scope.load = function(fn) {
serverFuncs.load(fn).then(function(v) {
$scope.$apply(function() {
$scope.curFile = fn;
$scope.curText = v;
});
});
}
Икс
Таким образом, строки кода здесь очень похожи на строки кода для реализации REST.
Но у нас есть контроль доступа, потому что функции на стороне клиента на самом деле связаны с помощью GUID с функциями на стороне сервера, поэтому не нужно беспокоиться о CSRF или защите ресурса REST на основе разрешений пользователя, потому что единственные функции, которые клиент может вызвать, — это функции которые представлены ему.
Нам также не нужно явно настраивать обработку ошибок, потому что исключение на стороне сервера превратится в сбой на клиенте.
Но это потоки
Что происходит, когда вы хотите передавать данные с сервера на клиент?
У Lift’s Round Trips есть простой ответ, используйте Streams. Лифт автоматически определит, является ли возвращаемый тип buildRoundTripфункции Stream[T]«правильно». Итак, если наша функция на стороне сервера:
def thing(s: String): Stream[String] = {
var x = 0
(s + x) #:: {
Thread.sleep(1000);
x += 1;
s + x
} #:: {
Thread.sleep(1000);
x += 1;
s + x
} #:: Stream.empty[String]
}
И наш клиент выглядит так:
$scope.doThing = function(fn) {
$scope.data = [];
$scope.done = false;
serverFuncs.thing(fn).then(function(v) {
$scope.$apply(function() {
$scope.data.push(v);
}).done(function() {
$scope.$apply(function() {$scope.done = true;})
});
});
}
Таким образом, даже если данные поступают с промежутками в 1 секунду, данные передаются клиенту по мере их появления на сервере.
Это бесконечно гибкий
Иногда поток с исключениями для обозначения сбоя не является правильным ответом. Например, если у вас есть библиотека Iteratee, и вы хотели бы обернуть Round-Rate вокруг Iteratee, вам необходимо сообщить о новых данных, выполненных и сбое. RoundTripHandlerFuncявляется ответом для вас.
Вот необработанный код:
private def thing(q: String, onChange: RoundTripHandlerFunc) {
if (q.trim.length < 3) {
onChange.failure("request too short!")
} else {
def doIt() {
if (ran.nextFloat() < 0.05f) {
onChange.done()
} else if (ran.nextFloat() < 0.02f) {
onChange.failure("Hey, I'm a failure")
} else {
onChange.send(/* some data */)
// delay without consuming a thread
Schedule(doIt _, /* some timeout */)
}
}
doIt()
}
}
Итак, приведенный выше код функционирует подобно Streamпримеру выше, но дает нам явный контроль над отправкой данных, выполнением и ошибкой.
Вы можете сделать неявное преобразование из любой формы потоковой библиотеки в RoundTripHandlerFuncвызов стиля и получить что-то вроде def myFunc(in: MyData): Iteratee[T]обслуживания через Round Trips.
Также обратите внимание, что типы ввода наших функций были Stringи JValue, но они могут быть любыми, если есть способ конвертировать JValueв этот тип через Lift-json.
Заворачивать
Здесь описывается, как функция Lift 3 Round Trip позволяет вам создавать действительно превосходные приложения AngularJS, поскольку вы можете легко передавать данные с клиента на сервер, вам не нужно беспокоиться о преобразовании типов данных, вам не нужно беспокоиться об управлении доступом и вам не нужно беспокоиться о разрыве соединения. На самом деле, вы не беспокоитесь о сантехнике, вы беспокоитесь о бизнес-логике вашего приложения.