kbmMW (n-tier framework) – Rest server

vložil Petr Slípek 11. prosince 2012 01:51

Úvod

Minule jsem slíbil článek na téma REST server v Delphi pomocí kbmMW a tady je výsledek. Celý článek je postaven na reálných zkušenostech, které jsme doposud získali.

Než abych složitě vymýšlel vzorový příklad, dovolím si v textu použít odkazy na náš dokončený a reálně fungující server. Výsledná aplikace je dostupná na adrese edubazar.dosli.cz. Na této adrese provozujeme výměnný systém vytvořených digitálních učebních materiálů pomocí našich programů. Tento výměnný systém je plně integrován do aplikací EduBase a DoTest a veškeré stahování a nahrávání dat pak probíhá pomocí uvedených aplikací. Na adrese edubazar.dosli.cz je pouze front-end, který zobrazuje data tak, aby se kdokoliv mohl podívat, co je v EduBazaru nahráno.

Požadavky na server EduBazaru

Při tvorbě serverové části EduBazaru jsme potřebovali vytvořit systém, kde by jakákoliv aplikace (i třetích stran) mohla s EduBazarem komunikovat. Aby uložená data byla k dispozici všem bez nutnosti řešení komunikačních protokolů a podobně. Požadavkem bylo vytvoření vlastního API pro práci s EduBazarem a standardní komunikační protokol. Tím padla volba na vytvoření REST serveru. REST server pracuje tak, že klient (www prohlížeč, aplikace apod.) pošle dotaz na server a dostane odpověď s potřebnými daty. Součástí každého dotazu musí být vše, co je třeba k jeho zpracování. Nic víc, nic míň. Pro komunikaci s REST serverem postačí klientovi pouze možnost komunikace přes http protokol a parser formátu JSON.

Vyzkoušejte si…

Funkcionalitu EduBazar serveru si můžete snadno vyzkoušet pomocí následujících volání:

http://edubazar.dosli.cz/ebapi/folders
http://edubazar.dosli.cz/ebapi/folders?parentid=-1
http://edubazar.dosli.cz/ebapi/folders?parentid=7

Jedná se o API serveru používaná pro vybudování stromu složek v levém panelu. První příklad vrací celý strom najednou (pomalé pro delší strukturu). Druhé volání vrací pouze složky na nejvyšší úrovni (root level). Poslední příklad vrací všechny podsložky ze složky s ID 7. Server ve všech případech vrací korektně formátovaný výstup v JSON formátu.

Server

Velkou výhodou kbmMW je modulární struktura celého systému. Tzn., že jsme mohli použít klasický aplikační server (funkcionalitu), kterou jsme již měli implementovánu, a pouze doplnit náš stávající aplikační server o nový modul. Tím mám na mysli zejména práci s databází, SQL dotazy apod. Nový modul se pak na straně kbmMWServeru pouze zaregistruje a přiřadí se mu komunikační protokol (TkbmMWTCPIPIndyServerTransport).

    1unit MyServer;
    2type 
    3  TMyServer = class(TDataModule)
    4     // základní komponenta pro vytvoření serveru kbmMW
    5     // v rámci jednoho serveru lze registrovat desítky různých modul,
    6     // které obsahují samotnou funkcionalitu serveru  
    7     kbmMWServer: TkbmMWServer;
    8     kbmMWTCPIPIndyServerTransport: TkbmMWTCPIPIndyServerTransport;
    9     procedure StartUpEduBazarServer;    
   10  end;
   11implementation
   12
   13procedure TMyServer.StartUpEduBazarServer;
   14var
   15  lServiceDef: TkbmMWCustomServiceDefinition;
   16begin
   17  // registrace modulu TdmEduBazarHTTPService v kontextu aplikačního serveru kbmMW
   18   lServiceDef:= kbmMWServer.RegisterService(TdmEduBazarHTTPService, True);
   19  // nastavení složek pro www server
   20 TkbmMWHTTPServiceDefinition(lServiceDef).RootPath[mwhfcHTML]    :=  'wwwroot';
   21 TkbmMWHTTPServiceDefinition(lServiceDef).RootPath[mwhfcImage]   := 'wwwroot\images';
   22 TkbmMWHTTPServiceDefinition(lServiceDef).RootPath[mwhfcJavascript]:= 'wwwroot\scripts';
   23  TkbmMWHTTPServiceDefinition(lServiceDef).RootPath[mwhfcStyleSheet]:='wwwroot\styles';
   24 TkbmMWHTTPServiceDefinition(lServiceDef).RootPath[mwhfcOther]  :='wwwroot\files';  
   25  // přiřazení komunikačního protokolu serveru
   26// jeden server může „poslouchat“ na různých portech. A na každém portu
   27// může být jiný komunikační protokol.
   28   kbmMWTCPIPIndyServerTransport.Server := kbmMWServer;
   29   kbmMWTCPIPIndyServerTransport.StreamFormat := AJAX;
   30   kbmMWTCPIPIndyServerTransport.Bindings := 0.0.0.0:80;
   31   kbmMWServer.Active := True;
   32end;

Metoda StartUpEduBazar je volána ve chvíli, kdy se má server spustit.

Pro zajištění samotné funkcionality serveru je třeba vytvořit potomka TkbmMWEventHTTPService (v podstatě se jedná o běžný TDataModul s přidanou funkcionalitou). Pro vytvoření je nejvhodnější využít průvodce, který je součástí kbmMW. Dále už je práce velice jednoduchá. V modulu je nutné implementovat obsluhu událostí OnGet, OnPost, OnPut, OnHead, … dle potřeby.

    1unit dmEduBazarHTTPServiceUnit;
    2type
    3  TdmEduBazarHTTPService = class(TkbmMWEventHTTPService) 
    4  // základní datamodul, který obsahuje Eventy a vlastnosti 
    5  //potřebné pro vytvoření http serveru
    6   // popis metod je u jejich implementace níže 
    7   class function GetPrefServiceName:string;
    8   function TdmEduBazarHTTPService.kbmMWEventHTTPServiceGet(const Func: string;
    9    const ClientIdent: TkbmMWClientIdentity; Args: TkbmMWVariantList): Variant;
   10  end;
   11
   12class function TdmEduBazarHTTPService.GetPrefServiceName:string;
   13begin
   14     Result:='HTTPSERVICE'; 
   15// tento název je klíčový pro celé fungování. V rámci serveru je možné mít 
   16// pouze jednu service, s tímto názvem  v případě uvedení jiného názvu, 
   17// je nutné při dotazu na server uvést také název požadované služby, 
   18// ale tím by se vše komplikovalo.
   19end;
   20
   21function TdmEduBazarHTTPService.kbmMWEventHTTPServiceGet(const Func: string;
   22    const ClientIdent: TkbmMWClientIdentity; Args: TkbmMWVariantList): Variant;
   23var
   24  lCharset: string;
   25  lMimeType: string;
   26  lURL: string;
   27begin
   28  // získání url dotazu
   29  lURL := VarToStr(Args[0]);  
   30  if lURL = '/ebapi/folders' then
   31    result := Perform_EBAPI_Folders(ClientIdent, Args)
   32  else
   33  if lURL = '/ebapi/packages' then
   34    result := … // provedení činností pro vrácení správných dat atd.
   35  else
   36   begin
   37   // klient požaduje URL, které není definováno jako API. 
   38  //Může jít například o html soubor, JS soubor, obrázek atd. Tzn., že 
   39  // potřebujeme vrátit odpověď jako by se jednalo o klasický http server
   40  // následující metody jsou přímo součástí kbmMW.
   41      result := HTTPResponseFromFile(lURL, lMimeType, lCharset);
   42      SetResponseMimeType(lMimeType);
   43      SetResponseCharset(lCharSet);
   44    end;
   45  end;
   46end;

Jednotlivé dotazy na server pak zpracovávají metody Perform_EBAPI_….

    1function TdmEduBazarHTTPService.Perform_EBAPI_Folders(
    2  const ClientIdent: TkbmMWClientIdentity; Args: TkbmMWVariantList): variant;
    3var
    4  lParentID: Integer;
    5  lJSONArray: TkbmMWJSONArray;
    6  lJSONObject : TkbmMWJSONObject;
    7begin
    8  // kbmMW obsahuje nástroje pro parsnutí parametrů z url http requestu.
    9  lParentID := GetQueryValue(Args[2], 'parentid', -2);
   10  // po získání parametrů je vytvořen SQL dotaz na získání potřebných dat z databáze.
   11  // bžná práce s DB.
   12  qryTemp…. // vypuštěno pro zkrácení
   13  qryTemp.Active := True;
   14
   15  // Vytvoření JSON objektu z qryTemp
   16// kbmMW obsahuje vše potřebné pro práci s JSON formátem
   17  // tady máte na výběr z několika různých možností jak převést DataSet na JSON. 
   18  // pokud se použije virtualizace přístupu k databázi přes kbmMWXClientDataSet 
   19  //(což je potomek  TkbmMemTable), je možné použít volání 
   20  //kbmMemTable.SaveToStreamViaFormat, 
   21  //kde JSON je jeden z podporovaných formátů, nebo použít následující kód. 
   22
   23  lJSONArray := TkbmMWJSONArray.Create
   24  try
   25     // následuje zkrácený kód 
   26     // pro každý řádek v tabulce provést
   27        lJSONObject := TkbmMWJSONObject.Create;
   28        //  pro každý sloupec v qryTemp provést
   29        lJSONObject.AsString[COLUMN_NAME] := VALUE;
   30        lJSONArray.Add(lJSONObject);    
   31    // konec zkrácené verze
   32
   33   // po vytvoření pole JSON objektů poslat odpověď klientovi
   34      lJSONStreamer:=TkbmMWJSONStreamer.Create;
   35      try
   36        SetResponseMimeType('application/json');
   37        SetResponseCharset('utf-8');
   38        result := lJSONStreamer.SaveToUTF8String(lJSONArray);
   39      finally
   40        lJSONStreamer.Free;
   41      end;
   42    finally
   43      lJSONArray.Free;
   44    end;
   45end;

No a to je na straně serveru v podstatě vše. Samozřejmě se jedná pouze o základ, uvedené řešení lze snadno rozšířit o další funkcionalitu. U většiny systémů, které jsme prozatím realizovali, se na straně serveru musí vyřešit správa sessions, ukládání stavu klienta, nastavení oprávnění apod. To vše ale již závisí na konkrétních požadavcích na systém.

Zpracování dat na straně klienta

Na straně klienta se zavolá funkce na REST serveru a následně se zpracuje vrácená odpověď. Protože se bavíme o webu, je následující ukázka v JavaScriptu, použít lze ale jakýkoliv programovací jazyk a nástroj, který podporuje JSON a http protokol. V ukázce je ukázka rekurzivní funkce, která načte hlavní úroveň stromu, a po kliknutí pomocí dalšího volání načte vybranou větev. Pro parsnutí JSON formátu je použito JQUERY.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
 <head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <script type="text/javascript" language="javascript" src="jquery.min.js">
  </script>
    <title>Build tree</title>
    <style>
      span{
        color: rgb(0,102,204);
        cursor:pointer;
      }
    </style>
  </head>
  <body>
  <script>   
  function buildTree(target, parent){ // volání funkce na REST serveru
   $.get('http://edubazar.dosli.cz/ebapi/folders', {parentid: parseInt(parent)},
     function(data) {
      var resHtml = "<ul>";     
       for (var i=1; i<data.length; i++){ // Průchod všemi objekty
         var title = data[i]["Name"];
         var id = data[i]["ID"];
         resHtml += '<li data-id="'+id+'">';
         if (data[i]["SubtreeIDs"] != ""){     
           resHtml += '<span onclick="buildTree($(this).parent(), '+id+')">'
              +title+'</span>';             
         }else{              
           resHtml += title;
         }                                        
         resHtml += '</li>';
        } 
       resHtml += '</ul>';         
       $(target).append(resHtml);  
     });  
     }           
    buildTree("body", -1);
  </script>
  </body>
</html>

Poznámka: html stránku v praxi naleznete na edubazar.dosli.cz/test_tree.html.


Nabízíme Delphi školení a konzultace na různá témata, primárně ve Vaší firmě.

Tagy: , ,

Komentáře

11.12.2012 8:29:17 #

Petr Slípek

Ač jsem to po sobě několikrát četl, tak jsem se nevyhnul chybě. V posledním Delphi kódu má být na řádku 23 správně lJSONArray := TkbmMWJSONArray.Create;

Petr Slípek

11.12.2012 9:48:39 #

radekc

opraveno, díky

radekc

Komentování ukončeno

Naše nabídka

Partial English version.

MVP
Ing. Radek Červinka - Embarcadero MVP
profil na linkedin, Twitter:@delphicz

Nabízím placené poradenství a konzultace v oblasti programování a vývoje SW.
Dále nabízíme i vývoj speciálního software na zakázku.

Neváhejte nás kontaktovat (i ohledně reklamy nebo burzy práce).

Pokud chcete podpořit tento server libovolnou částkou, můžete použít PayPal. Moc děkuji.

Delphi Certified Developer

O Delphi.cz

Delphi je jediný moderní RAD nástroj podporující tvorbu nativních aplikací pro platformu Win32, Win64 , Mac OSX a na iPhone a Android (s výhledem na další platformy díky FireMonkey) na současném trhu (včetně Windows 8.1).

V současnosti je světová komunita přes dva miliónů vývojářů.

Delphi.cz je nezávislý portál pro uživatele Delphi. Portál není koncipován pro úplné začátečníky, i když i ti se zde nebudou nudit, ale spíše na programátory, kteří již něco znají a chtějí své znalosti dále rozvíjet a sledovat novinky.

Anketa

Poslední komentáře

Comment RSS