Node.js jako middle-end
Jedną z najbardziej interesujących zalet jakie wnosi idea SSJS jest możliwość wykorzystania tego samego kodu zarówno po stronie przeglądarki jak i po stronie serwera. Daje to możliwość współdzielenia części logiki, modelu, walidatorów, czy warstwy widoku. Najprościej przedstawia się sytuacja kiedy od razu napiszemy cały backend w JavaScriptcie, jednak w praktyce częściej będziemy integrować Node.js, czy inne rozwiązanie SSJS, z już istniejącą aplikacją.
Taką warstwę, znajdującą się pomiędzy backendem i frontendem, z zachowaniem pełnej logiki rozumowania, nazywamy middle-endem (po więcej informacji odsyłamy do twórcy tej koncepcji http://blog.getify.com/2010/07/what-exactly-is-the-middle-end/). Zwróćmy też uwagę by nie mylić middle-endu z middlewarem. Zbadaliśmy możliwość zastosowania Node.js jako proxy służące jako silnik szablonów, choć oczywiście możliwa jest też inna architektura. W nowoczesnych aplikacjach webowych zdarza się, że niemal cała struktura drzewa DOM tworzona jest przez JavaScript po stronie przeglądarki. Niestety nie jest to przyjazne rozwiązanie dla systemów o słabszej wydajności (IE lub przeglądarki mobilne). Z pomocą przychodzi właśnie JavaScriptowy serwer proxy.
Idea była następująca: nasze middle-endowe proxy wstawiliśmy pomiędzy już działający backend (napisany w dowolnym języku), a przeglądarkę użytkownika. Proxy przyjmowało zapytania od użytkownika i przekazywało je dalej do serwera z aplikacją backendową. Backend zwracał JSON z danymi (modelem) dla tej podstrony, template’ami i dodatkowymi informacjami jak na przykład listą skryptów JavaScript do załadowania. JavaScriptowe proxy podejmowało następnie decyzję czy klient dysponuje odpowiednią przeglądarką i:
- jeśli tak, to wysyłało krótki, podstawowy szablon wraz z dołączonymi skryptami, szablonami oraz modelem tak by przeglądarka sama stworzyła stronę,
- jeśli nie, wykorzystując serwerową implementację DOM-a, uruchamiało standardowe skrypty, które w pierwszym rozwiązaniu zostały wysłane do przeglądarki i tworzyło pełne drzewo DOM.
Schemat działania naszej aplikacji middle-end.
Efekt końcowy działania w obydwu przypadkach był identyczny. Wolniejsza przeglądarka otrzymywała gotowy HTML, aby odciążyć ją od prac z JavaScript. Chociaż graf powyżej jako przykład podaje IE6, bardziej interesującym zastosowaniem byłoby generowanie HTML-a po stronie serwera dla urządzeń mobilnych, które są stosunkowo słabe, bądź w ogóle nie posiadają wsparcia dla JavaScriptu. Przykładem, mogłoby być generowanie dynamicznego mashup-u typu Pageflakes / Netvibes.
Udało nam się użyć po stronie serwera jQuery (wyłączyliśmy animacje) i przypuszczamy, że większość popularnych bibliotek nie będzie miała problemów z działaniem. Potwierdza to przykład YUI, które w dużej części działa już bez problemów na Node.js.
var query = url.parse(req.url).query;
httpget('localhost', '/', 8888, function(data) {
__model.data = JSON.parse(data);
res.writeHead(200, { 'Content-Type': 'text/html' });
if (query === "fallback" ) {
jsdom.env({
html: templateHtml.replace('{{scripts}}', ''),
scripts: __scripts.map(function(el){
return el.indexOf("http") === 0 ? el : __staticPath + el;
}),
done: function(errors, window) {
errors && console.log(errors);
var __doc = window.document;
window.jQuery.fx.off = true;
window.init(__model); //?
var scripts = __doc.getElementsByTagName('script');
var scriptArr = Array.prototype.slice.call(scripts);
for(var i = 0; i < scriptArr.length; i++) {
var s = scriptArr[i];
s.parentNode.removeChild(s);
}
res.write(window.document.doctype.toString());
res.write(window.document.innerHTML);
res.end();
}
});
} else {
var dynamicScripts = __scripts.map(function(el) {
return '';
})
dynamicScripts.push('');
res.end(templateHtml.replace('{{scripts}}',
dynamicScripts.join('')));
}
});
Fragment implementacji generowania dokumentu po stronie serwera (server.js). Pełny kod
Pewną trudnością jest natomiast pisanie kodu, który byłby przenośny pomiędzy środowiskiem przeglądarki i serwerowym oraz samo emulowanie przeglądarki po stronie serwera. Aby to lepiej zbadać zajęliśmy się stworzeniem walidatora, który był odpowiedzialny za walidację danych formularza po stronie klienta i serwera. Tutaj głównym pytaniem było w jakiś sposób strukturalizować kod, aby można go było swobodnie wywoływać po stronie przeglądarki i Node.js.
Z pewnością ciekawym wzorcem jest opakowywanie kodu w podobne wywołania jakimi posługuje się Node przy dołączaniu modułów. Z wywoływaniem skryptów w kontekście podobnym do przeglądarkowego jednak już tak prosto nie jest, ponieważ node’owa metoda vm.runInNewContext() zachowuje się w sposób dość nieoczekiwany i wymaga trochę hackowania. Na pewno w tym temacie jest jeszcze sporo do dopracowania.
W warstwie proxy wykorzystaliśmy moduł jsdom, który umożliwił dynamiczne budowanie DOMa, zarówno z poziomu API (appendChild, createElement etc.) jak i poprzez “nakładkę” w postaci jQuery.
Więcej informacji:
Reinmar,
Piotrek Koszuliński
@reinmarpl
http://code42.pl
Bartek Szopka
@bartaz

Komentarze
1
Bobby napisał/a 28.09.2011 o 10:11
All of my quesotins settled-thanks!
2
occgnunfq napisał/a 29.09.2011 o 16:38
Yol0Jz tnpljnprcnuu
3
vwhpaa napisał/a 30.09.2011 o 14:37
MZ8qE5 , [url=http://qmofkexietbz.com/]qmofkexietbz[/url], [link=http://vfynqdtgsdiw.com/]vfynqdtgsdiw[/link], http://rcyufxkjnpoo.com/