dla nowoczesnych aplikacji nastawionych na bogatą interakcję z klientem • zorientowane na zdarzenia (event loop) • jednowątkowość wymusza nieblokujące operacje I/O • podstawą projektu jest silnik JavaScript Google V8, kilka wbudowanych bibliotek i moduły użytkowników • implementuje ECMAScript 5, część standardu CommonJS i dostarcza konsolę REPL
sposobem działania protokołu HTTP • opiera się na pojęciu reprezentacji zasobu i możliwych do wykonania na nim metod zmieniających jego stan • główne cechy • architektura klient-serwer • zorientowanie na zasoby • jednolity interfejs • bezstanowość
sieciowych zgodnych z paradygmatem REST • opracowany z myślą o lepszej kontroli nad HTTP niż dają to frameworki webowe (np. Express) • zawiera szereg udogodnień (wersjonowanie, sumy kontrolne, filtry) dla tworzenia tzw. strict APIs
zasobów, parametry w formie URL-encoded lub JSON są automatycznie parsowane server.get('/posts/:id', function (req, res, next) { console.log('post id = ' + req.params.id); res.send(200, {author: 'adamus', content: 'this is a post'}); return next(); }); $ curl http://localhost:8080/posts/8 {"author":"adamus","content":"this is a post"} // komunikat konsoli serwera: post id = 8 Pozostałe metody obsługujemy analogicznie server.post(...); server.put(...); server.del(...); server.head(...); W miejsce ścieżki możemy przekazać obiekt RegExp a dopasowania kolejnych grup otrzymamy w req.params[0], req.params[1] itd.
API, przyjęto zatem rozwiązanie Semantic Versioning (SemVer) server.get({path: '/posts/:id', version: '2.0.1'}, function (req, res, next) { ... }); server.get({path: '/posts/:id', version: '1.2.3'}, function (req, res, next) { ... }); $ curl localhost:8080/posts/8 -H 'Accept-Version: 1' // otrzymamy wersję 1.2.3 $ curl localhost:8080/posts/8 // otrzymamy pierwszą zarejestrowaną wersję, czyli 2.0.1 $ curl localhost:8080/posts/8 -H 'Accept-Version: 3' HTTP/1.1 400 Bad Request {"code":"InvalidVersion","message":"GET /posts/8 supports versions: 2.0.1, 1.2.3"} W powyższych ścieżkach możemy podać tablice kolejnych wersji W taki sam sposób działa parametr version całego serwera, ale dotyczy wszystkich ścieżek, którym nie nadano wersji (pełni rolę domyślną)
Content-Type może być ustalona automatycznie na podstawie dostępnych formaterów, wbudowano obsługę application/octet-stream*, application/json i text/plain Własne formatery treści podajemy w konfiguracji serwera formatters: { 'application/my-format': function (req, res, body) { // logika konwersji formatu return reprezentacja_body; } // kolejne formatery ... } Wysyłając żądanie z określonym formatem w nagłówku Accept, zostanie odesłana postać zwrócona w funkcji obsługi tego formatu Obsługiwane przez serwer formaty znajdują się w tablicy server.acceptable
zwracać błędy pierwszym argumentem liczbowym metody send() oraz korzystać z obiektu Error('wiadomość') res.send(401, new Error('Nie masz uprawnień')); Restify posiada zestaw symbolicznych błędów HttpError i RestError (niestandardowe błędy z dodatkowym opisem w postaci JSON) return next(new restify.ConflictError("Żądanie jest konfliktowe")); // HttpError return next(new restify.InvalidArgumentError("Niepoprawny argument")); // RestError // obie instrukcje zwrócą kod 409
http.Server Node.js oraz własne, takie jak: • 'NotFound' • 'MethodNotAllowed' • 'VersionNotAllowed' • 'after' • 'uncaughtException' Nasłuchujemy na zdarzenia przez server.on('zdarzenie', funkcja_obsługi(...) { ... });
headers) Restify dodaje własne .header('Authorization') – pobranie wartości nagłówka .accepts('text/plain') – czy żądanie akceptuje format .is('application/json') – czy żądanie jest danego typu .contentLength, .contentType .id – UUID żądania .time – czas dotarcia do serwera .secure – czy żądanie przyszło w sesji SSL
Restify dodaje własne .header('X-My-Header', '...') – ustawienie wartości nagłówka .cache(typ, opcje) – ustawienie nagłówka Cache-Control .status(200)/.code – ustawienie kodu odpowiedzi .contentLength, .contentType .charSet – kodowanie dopisywane do Content-Type .id – UUID żądania Możemy nadpisać domyślnie odsyłane nagłówki i ich treść restify.defaultResponseHeaders = function(data) { this.header('Server', 'Mój serwer'); }; // this to obiekt odpowiedzi a data to gotowe do wysłania ciało (response body)
JsonClient • wysyła i odbiera wiadomości w formacie JSON • StringClient • wysyła Url-Encoded i odbiera czysty tekst, na przykład XML • HttpClient • umożliwia pracę ze strumieniami danych na poziomie oferowanym przez moduły http/https Node.js Konfiguracja żądań obejmuje metodę dostępu, nagłówki, czas timeoutu, liczbę powtórzeń wywołania itp.
Brak obsługi niestandardowych metod HTTP • Brak negocjacji Accept-Charset, -Encoding*, -Language • Brak obsługi nietrywialnych metod autoryzacji • Niespójne API Rozwiązanie? Fork me on GitHub
What is Node.js? http://radar.oreilly.com/2011/07/what-is-node.html • Nobody Understands REST or HTTP http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http • Some People Understand REST and HTTP http://blog.steveklabnik.com/posts/2011-08-07-some-people-understand-rest-and-http • How I Explained REST to My Wife http://tomayko.com/writings/rest-to-my-wife • Restify documentation http://mcavage.github.com/node-restify/ • RESTful Web Services Cookbook Subbu Allamaraju, O’Reilly 2010, Yahoo! Press