Pokazywanie postów oznaczonych etykietą rest. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą rest. Pokaż wszystkie posty

sobota, 31 października 2015

[HTML|JS|CSS] ES6: Syntax

Nowa specyfikacja ECMAScript wnosi sporo nowości do JavaScriptu. Nie możemy ich jeszcze w pełni wykorzystywać bez potrzeby transpilacji, ale kwestią czasu jest, kiedy przeglądarki zaimplementują wszystkie nowości w swoich engine'ach. W tym momencie do transpilacji można używać np. babeljs, będącego pakietem node.js. Instalujemy go poprzez wywołanie

npm install -g babel-cli
npm install --save-dev babel-preset-es2015
babel --presets es2015 script_es6.js --out-file script_es5.js

Ostatnie polecenie reprezentuje transpilację z jednego pliku do drugiego. Od kilku dni należy do tego celu wykorzystywać preset es2015, wcześniej transpilacja była domyślną operacją.

Najważniejsze zmiany składniowe w ES2015 (ES6):

let keyword

Deklaruje zmienną typu block-scoped, co jest naturalne w większości języków programowania, w JS nie było, kiedy mieliśmy do wyboru tylko var (function-scoped variables).

function myFunction()
{
  console.log(a);

  let b = 13; //accessible throughout function
  console.log(b);

  if(true)
  {
    let c = 14; //accessible throughout function
    let b = 15;
    console.log(b);
  }

  //console.log(c); //c is not defined
}

Zmienne możemy nadpisywać poprzez let, ale tylko jeżeli są w innym block-scope. Zaleca się używanie let zamiast var, ze względu na zmniejszenie podatności na bugi takiego kodu.

const keyword

Zmienne typu read-only, ostatnia linijka zwróci SyntaxError podczas transpilacji, a docelowo także błąd w run-time. Zmienne typu const są również block-scoped.

const pi = 3.141;
var r = 2;

console.log(pi * r * r); //Output "12.564"

pi = 12; //throws read-only exception

 W przypadku obiektów deklarowanych jako const będziemy mogli modyfikować ich propercje, a błąd dostaniemy tylko przy próbie nadpisania całego obiektu.

default parameter values

W ES5 nie musieliśmy przekazywać wszystkich argumentów wywołując funkcję. Nieprzekazane zmienne przyjmowały wartość undefined. Teraz możemy od razu zadeklarować dla każdego parametru domyślne wartości.

function myFunction(x = 1, y = 2, z = 3){
 console.log(x, y, z); //Output "6 7 3"
}

myFunction(6, 7);

spread operator

Operator ten reprentują ... , a jego zadaniem jest rozdzielenie obiektu typu iterable na pojedyncze elementy. Po transpilacji otrzymamy metodę apply (przyjmuje listę parametrów jako tablicę) wołaną na funkcji, ale docelowo wykorzystane będą znacznie szybsze mechanizmy w runtime.

function myFunction3(a, b)
{
 return a + b;
}

let data = [1, 4];
let result = myFunction3(...data);
console.log(result); //Output "5"

Można go wykorzystać także przy budowaniu tablicy z innej tablicy.

rest parameter

Trzech kropek można także użyć do obsługi funkcji ze zmienną liczbą argumentów. Rest parameter zmapuje nadmiarowe zmienne jako tablicę.

function myFunction(a, b, ...args)
{
  console.log(args); //Output "3, 4, 5"
}

myFunction(1, 2, 3, 4, 5);

array destructuring assignment

Pozwala na przypisanie wartości do wielu zmiennych za pomocą jednego wyrażenia. Prawą stroną musi być obiekt typu iterable. Array destructuring pozwala na ignorowanie niektórych elementów oraz użycie spread operator-a.

let myArray2 = [1, 2, 3];
let [g, h, i] = myArray2;
let [j,  , k] = myArray2;
let [l, ...m] = myArray2;

object destructuring assignment

W analogiczny sposób możemy wykonywać destructuring na obiektach.

let object = {"name" : "John", "age" : 23};
let {name, age} = object; //object destructuring assignment syntax

arrow function

Przypomina dobrze znane np. z C# lambda expression. Więcej niż jedną linię należy opakowywać w nawiasy klamrowe.

let circleArea = (pi, r) => pi * r * r;
let result = circleArea(3.14, 3);

let circleArea2 = (pi, r) => {
 let area = pi * r * r;
 return area;
};
let result = circleArea2(3.14, 3);


enhanced object literals

Wprowadzono kilka udogodnien składniowych przy budowie obiektów. Mamy do dyspozycji skróconą składnię przy definiowaniu propercji (również tych z wyliczanymi nazwami) oraz metod, 

let x = 1, y = 2;
let object = { x, y };

console.log(object.x); //output "1"

let object = {
  ["first" + "Name"]: "Eden",
};

//extract
console.log(object["first" + "Name"]); //Output "Eden"

let object = {
  myFunction(){
    console.log("Hello World!!!"); //Output "Hello World!!!"
  }
}

czwartek, 7 listopada 2013

[WCF] REST

REST (Representational State Transfer) to drugi obok SOAP (Simple Object Access Protocol) wzorzec budowy aplikacji wymagających komunikacji pomiędzy stroną kliencką i serwerową. Oparty jest na protokole HTTP (w przeciwieństwie do niezależnego od protokołów SOAP). Wzorzec ten zakłada prostą definicję dostępu do zasobów (poprzez czasowniki HTTP, takie jak GET, POST, PUT, DELETE). Różna są też formaty, w jakich zwracany jest ten zasób, głównie jest to XML i JSON. Mówi się więc, że REST to wzorzec nastawiony na zasoby, natomiast SOAP kładzie nacisk na akcje (wykonywane na zasobach). Dużą przewagą REST-a jest interoperacyjność i skalowalność. Pomiędzy REST i SOAP definiuje się POX (Plain Old Xml) - przesyłanie danych w formacie XML, ale bez narzutów SOAP.

Dla samego REST wykorzystujemy w WCF webHttpBinding oraz webHttpBehavior. Adresowanie zasobów odbywa się poprzez dwa atrybuty zawarte w System.ServiceModel.Web: WebGet i WebInvoke. Pierwszy z nich definiuje operacje wykonywane przy żądaniach typu GET, drugi zapewnia obsługę pozostałych żądań.

[ServiceContract]
public interface IEvaluationService
{
    [OperationContract]
    [WebInvoke(Method ="POST", UriTemplate = "evals")]
    void SubmitEvaluation(Evaluation eval);

    [OperationContract]
    [WebGet(UriTemplate = "evals", ResponseFormat = WebMessageFormat.Json)]
    List<Evaluation> GetEvaluations();

    [OperationContract]
    [WebGet(UriTemplate = "eval/{id}")]
    Evaluation GetEvaluation(string id);

    [OperationContract]
    [WebInvoke(Method = "DELETE", UriTemplate = "eval/{id}")]
    void RemoveEvaluation(string id);
}

Poprzez atrybuty WebGet i WebInvoke można także ustawić format zwracanej / przyjmowanej wiadomości. Domyślnie jest to XML. Plik konfiguracyjny dla aplikacji hostującej jest bardzo krótki, a wszystko dzięki specjalnej klasie hostującej REST-owe serwisy.

<?xml version="1.0"?>
<configuration>

  <system.web>
    <compilation debug="true"/>
  </system.web>
  <system.serviceModel>
    <services>
      <service name="EvaluationServiceLibraryRest.EvaluationService" >
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:18081/evaluations/"/>
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>


WebServiceHost host = new WebServiceHost(typeof(EvaluationService));
try
{
    host.Open();
    Console.ReadKey();
    host.Close();
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
    Console.ReadKey();
    host.Abort();
}

wtorek, 8 października 2013

[WCF] Wprowadzenie

Windows Communication Foundation to technologia, którą świat ujrzał z .NET Framework 3.0 jako odpowiedź na potrzebę budowania rozproszonych systemów, które MS zaczął marketingowo nazywać "Connected Systems". Podczas tworzenia aplikacji rozproszonych ważne jest, aby umożliwić komunikowanie się systemów napisanych w różnych technologiach. Stąd pomysł serwisów jako jednostek udostępniających pewne funkcjonalności poprzez przesyłanie ustandaryzowanych wiadomości.

Dominują dwa wzorce, SOAP i REST.

SOAP (Simple Object Access Protocol) - wiadomości wysyłane są w formacie XML i WS-* protocols. Wspiera wiele protokołów transportowych np. HTTP, TCP, MSMQ.

REST (Representational State Transfer) - różne formaty wiadomości (XML, coraz częściej JSON), protokół transportowy HTTP, paradygmat ukierunkowany na adresowanie, modyfikowanie i odczytywanie zasobów.

WCF wprowadza unifikację, umożliwiając wymianę wiadomości po różnych protokołach, w różnych formatach, wzorcach.

Większość funkcjonalności WCF zawarte jest w System.ServiceModel.dll. Najprościej filozofię WCF oddaje zdanie: serwisy wystawiają swoje funkcjonalności poprzez endpointy, natomiast klienci konsumują je poprzez kanały. WCF-owe endpointy składają się z trzech składowych ABC definiujące podstawowe pojęcia (Address - gdzie?, Binding - jak?, Contract - co?).

Tworzenie najprostszej aplikacji WCF:

Pierwszy projekt to serwisy WCF.

[DataContract]
public class Name
{
    [DataMember]
    public string First { get; set; }

    [DataMember]
    public string Last { get; set; }
}

[ServiceContract]
public interface IMyFirstService
{
    [OperationContract]
    string SayHello(Name name);
}

public class MyFirstService : IMyFirstService
{
    public string SayHello(Name name)
    {
        return string.Format("Hello, {0} {1}", name.First, name.Last);
    }
}

Klasa Name będzie przesyłaną wiadomością, natomiast oznaczenie interfejsu przez atrybuty definiuje, jakie operacje chcemy wystawić na zewnątrz. Kolejne zmiany nanosimy w pliku App.config.Wystawiamy jeden serwis dostępny pod trzema endpointami wykorzystującymi różne protokoły transportowe.

<services>
  <service name="WcfEssentials.MyFirstService">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8080/helloworld/"   />
      </baseAddresses>
    </host>
    <endpoint address="ws"  binding="wsHttpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="basic"  binding="basicHttpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="net.tcp://localhost:8081/helloworld/"  binding="netTcpBinding" contract="WcfEssentials.IMyFirstService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>
</services>

Uruchomienie aplikacji spowoduje pojawienie się okna, w którym możemy testować nasze endpointy.


Jeżeli chcemy wykorzystać nasz serwis w kodzie, dodajemy osobny projekt, np. aplikację konsolową. Następnie musimy dodać referencję do serwisów WCF. Klikamy prawym przyciskiem myszy i wybieramy Add Service Reference....


Strona kliencka pobierze metadane i utworzy obiekty proxy, którymi będziemy mogli się łączyć do stworzonych wcześniej serwisów. W pliku app.config automatycznie wygenerują się bindingi dla strony klienckiej, gdzie każdy endpoint otrzyma swoją nazwę. Wywołanie serwisu w kodzie C# jest bardzo proste.

using (var client = new MyFirstServiceClient("NetTcpBinding_IMyFirstService"))
{
    var name = new Name() { First = "Tom", Last = "Smith" };
    Console.WriteLine(client.SayHello(name));
    Console.Read();
}

Jako parametr konstruktora podajemy wygenerowaną przez VS nazwę serwisu.

piątek, 27 września 2013

[FullTextSearch] ElasticSearch: Query DSL

Zapytania do ElasticSearch wysyłamy REST-owo. Do prostego pobierania dokumentów wystarczą nam sparametryzowane zapytania typu GET. Aby jednak poznać pełnię możliwości ElasticSearch musimy poznać język zapytań DSL, w którym parametry zapytania ustawiamy poprzez załączenie z żądaniem typu POST odpowiedniego obiektu w postaci JSON. ElasticSearch udostępnia wiele rodzajów zapytań. Poniżej ciekawsze z nich.

Term Query

Tego typu zapytania można wykonywać na dwa sposoby. Przy użyciu czasownika GET

http://localhost:9200/books/book/_search?q=title:parallel&pretty=true

lub za pomocą czasownika POST

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 59

{
 "query" : {
  "term" : { "title" : "parallel" }
 }
}

W obu przypadkach otrzymujemy ten sam rezultat

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 809

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.095891505,
    "hits" : [ {
      "_index" : "books",
      "_type" : "book",
      "_id" : "1",
      "_score" : 0.095891505, "_source" : {
 "title": "Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos",
 "author": "Michio Kaku",
 "year": 2006,
 "price": 12.06
}
    }, {
      "_index" : "books",
      "_type" : "book",
      "_id" : "3",
      "_score" : 0.095891505, "_source" : {
 "title": "Hyperspace: A Scientific Odyssey Through Parallel Universes, Time Warps, and the Tenth Dimension",
 "author": "Michio Kaku",
 "year": 1995,
 "price": 16.36
}
    } ]
  }
}

Term Query to takie zapytanie, które zwraca wyniki tylko wtedy, gdy podamy dokładnie słowo, które znajduje się w danym polu. Nie zadziałają tutaj zapytania przedrostowe typu "paral*". Wyniki za każdym razem dostępne będą w tablicy dostępnej pod hits.hits. Pełny dokument zwracany jest w polu _source. Pobieranie tak zagnieżdżonych informacji wydaje się niewygodne, a czasami także niepotrzebnie przesyłany jest cały duży dokument, dlatego możemy podać, które pola nas interesują.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 92

{
 "fields" : ["title","author"],
 "query" : {
  "term" : { "title" : "parallel" }
 }
}

W rezultacie otrzymamy:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 797

{
  "took" : 9,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 2,
    "max_score" : 0.095891505,
    "hits" : [ {
      "_index" : "books",
      "_type" : "book",
      "_id" : "1",
      "_score" : 0.095891505,
      "fields" : {
        "title" : "Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos",
        "author" : "Michio Kaku"
      }
    }, {
      "_index" : "books",
      "_type" : "book",
      "_id" : "3",
      "_score" : 0.095891505,
      "fields" : {
        "title" : "Hyperspace: A Scientific Odyssey Through Parallel Universes, Time Warps, and the Tenth Dimension",
        "author" : "Michio Kaku"
      }
    } ]
  }
}

Odpytywać można także tylko indeks (bez podawania typu book) oraz wiele indeksów lub wiele typów. Na przykład:

http://localhost:9200/books,books5/book/_search?q=author:kaku&pretty=true

Terms Query

Podajemy kilka wartości, które nie będą analizowane (muszą to być dokładne wartości), oraz opcjonalnie ile z nich musi pasować, aby zwrócony został dany dokument.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 111

{
 "query" : {
  "terms" : { "title" : ["parallel","hyperspace","worlds"],
  "minimum_match" : 2
  }
 }
}

Match Query

W tym zapytaniu podane wyrażenie zostaje przetworzone przez analizator. A zatem tym razem możemy podawać niepełne wyrazy, na przykład:

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 65

{
 "query" : {
  "match" : { "title" :"para worlds"
  }
 }
}

Zapytanie takie zwróci wyniki. Domyślnie słowa są łączone operatorem OR, można to zmienić w następujący sposób.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 122

{
 "query" : {
  "match" : {
   "title" : {
    "query" : "parallel worlds",
    "operator" : "and"
   }
  }
 }
}

Match Phrase

Jeżeli chcemy wyszukać frazy składającej się z większej ilości słów, korzystamy z match_phrase, gdzie parametr slop definiuje, ile nieznanych słów może się mieścić pomiędzy podanymi wyrazami.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 121

{
 "query" : {
  "match_phrase" : {
   "title" : {
    "query" : "parallel worlds",
    "slop" : 1
   }
  }
 }
}

Multi Match

Efekt zapytania typu "Match" możemy uzyskać także na wielu polach dokumentu.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 131

{
 "query" : { 
  "multi_match" : {
   "query" : "univers michio penrose",
   "fields" : [ "title", "author" ]
  }
 }
}

Identifiers Query

Kiedy dokładnie znamy identyfikatory dokumentów, które chcemy pobrać, korzystamy z tego typu zapytania.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 73

{
 "query" : {
  "ids" : {
   "values" : [ "1", "4", "5" ]
  }
 }
}

Prefix Query

Wyszukiwanie po przedrostkach.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 63

{
 "query" : {
  "prefix" : {
   "title" : "uni"
  }
 }
}

Fuzzy Like This Query

Zapytanie, które dopuszcza literówki w podanej frazie.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 115

{
 "query" : {
  "fuzzy_like_this" : {
   "fields" : ["title", "author"],
   "like_text" : "penrse"
  }
 }
}

Range Query

Podajemy zakresy, przydatne dla wartości numerycznych.

POST http://localhost:9200/books/book/_search?pretty=true HTTP/1.1
User-Agent: Fiddler
content-type: application/json
Host: localhost:9200
Content-Length: 100

{
 "query" : {
  "range" : {
   "year" : {
    "from" : 1990,
    "to" : 2000
   }
  }
 }
}

niedziela, 22 września 2013

[FullTextSearch] ElasticSearch: CRUD

Używając ElasticSearch jako zwykłej dokumentowej bazy danych możemy w prosty sposób wykonywać CRUD-owe operacje. Jak już wspomniano w poprzednim poście, wszystkie operacje wykonuje się przy użyciu RESTa i odpowiednich verb-ów HTTP.

Dodawanie

PUT http://localhost:9200/books/book/1 HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200
Content-Length: 175

{
	"title": "Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos",
	"author": "Michio Kaku",
	"year": "2006",
	"price": "12.06"
}

Dodaliśmy zasób do kolekcji books. Zasób jest typu book. Na końcu dodajemy unikalny identyfikator. Jeżeli chcemy, by serwer ElasticSearch wygenerował za nas Id wysyłamy żądanie typu POST pod adres /books/book/.

Pobieranie

Aby pobrać zasób po identyfikatorze wysyłamy żądanie typu GET.
 
GET http://localhost:9200/books/book/1 HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200

W odpowiedzi otrzymujemy JSON w następującej postaci:

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Content-Length: 259

{"_index":"books","_type":"book","_id":"1","_version":1,"exists":true, "_source" : {
	"title": "Parallel Worlds: A Journey Through Creation, Higher Dimensions, and the Future of the Cosmos",
	"author": "Michio Kaku",
	"year": "2006",
	"price": "12.06"
}}

Modyfikacja

Do modyfikacji wykorzystujemy POST, gdzie w body podajemy "ścieżkę" do property, które chcemy zmodyfikować.

POST http://localhost:9200/books/book/1/_update HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200
Content-Length: 57

{
  "script": "ctx._source.author = \"Kaku, Michio\""
}

Usuwanie

Do usunięcia wykorzystuje się czasownik DELETE.
 
DELETE http://localhost:9200/books/book/1/ HTTP/1.1
User-Agent: Fiddler
Content-Type: application/json
Host: localhost:9200

Ponowne wysłanie GET'a pod powyższy adres zwraca kod HTTP 404.

piątek, 23 sierpnia 2013

[C#|Visual Studio] ServiceStack: Tworzenie WebSerwisów

W tym poście zostanie opisane, jak utworzyć prosty projekt z RESTowymi serwisami w oparciu o ServiceStacka. Zaczynamy od stworzenia w Visual Studio pustego projektu ASP .NET.


Następnie za pomocą NuGeta instalujemy dllki ServiceStack.


Następnie tworzymy proste obiekty DTO, do których serializowane i deserializowane będą responsy i requesty REST-owe. Dobrą praktyką jest, aby obiekty DTO były symetryczne i przez konwencję do nazwy obiektu zwrotnego dodajemy "Response".

[Route("/entry/{Date}", "GET")]
public class Entry
{
    public DateTime Date { get; set; }
    public int Count { get; set; }
}

public class EntryResponse
{
    public int Total { get; set; }
}

Poprzez atrybuty definiujemy routingi, w których fragmenty ścieżek można bindować bezpośrednio do properties DTO. Następnie dodajemy klasę serwisu, która dziedziczy po bazowej klasie Service z SeriveStack. W klasie dodajemy metody o takich nazwach jak czasowniki HTTP, gdzie parametrem będzie zdefiniowane wcześniej DTO.

public class EntryService : Service
{
    public int Sum { get; set; }

    public object Post(Entry entry)
    {
        Sum += entry.Count;
        return new EntryResponse() {Total = Sum};
    }
}

Aby wystartować serwis, wystarczy skorzystać z pliku .asax.

public class Global : System.Web.HttpApplication
{

    public class MyEntryAppHost : AppHostBase
    {
        public MyEntryAppHost()
            : base("MyEntryService", typeof(EntryService).Assembly)
        {
        }

        public override void Configure(Funq.Container container)
        {
            
        }
    }

    protected void Application_Start(object sender, EventArgs e)
    {
        new MyEntryAppHost().Init();
    }
    //...
}

Metoda Configure posłuży jako bootstrapper, gdzie będzie można konfigurować np. kontener IoC. Ostatnim etapem jest skonfigurowanie pliku Web.config, gdzie dodajemy odpowiedni handler.

<configuration>
    <system.web>
        <compilation debug="true" targetFramework="4.0" />
    </system.web>

  <system.web>
    <httpHandlers>
      <add path="*" type="ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack" verb="*"/>
    </httpHandlers>
  </system.web>

</configuration>

Po uruchomieniu aplikacji mamy możliwość podglądu i testowania naszych endpointów.

czwartek, 11 października 2012

[HTML|JS|CSS] Node.js: RESTful API

Aby serwer mógł komunikować się ze stroną, możemy w prosty sposób wystawić RESTowe API dostosowane do naszych potrzeb. Z użyciem Node.js oraz expressa staje się to bardzo proste. Mamy do dyspozycji cztery metody:
  • GET
  • POST
  • PUT
  • DELETE
Poniżej przykład, jak zaprojektować takie API po stronie serwerowej. Użyto w nim dwóch obiektów middleware. Obiekt express.bodyParser, jak nazwa wskazuje parsuje body requestów typu post na obiekt javascript, dzięki czemu można odwoływać się do poszczególnych propercji takiego obiektu. Użycie express.static konfiguruje ścieżkę do szablonu html względem pliku serwera.

var express = require('express');
var app = express();

app.use(express.bodyParser());
app.use(express.static(__dirname + '/www'));

Samo API można zaprojektować w taki sposób jak poniżej.

app.get('/store/:key', function(req, resp) {
 var val = store[req.params.key];
 if(val){
  resp.send(val);
 }
 else{
  resp.send('undefined',400);
 }
});

app.post('/store/', function(req, resp) {
 store[req.body.key] = req.body.value;
    resp.send('OK');
});

app.put('/store/', function(req, resp){
 store[req.body.key] = req.body.value;
    resp.send('OK');
});

app.del('/store/:key', function(req, resp) {
 var val = store[req.params.key];
 console.log(req.params.key);
 if(val){
  store[req.params.key] = undefined;
  resp.send("OK");
 }
 else{
  resp.send('undefined',400);
 }
});