WEB 2.0 программирование на Object Pascal. Часть 1



Каждое утро я читаю различные блоги и вот сегодня наткнулся на блог Leonardo (]]>http://leonardorame.blogspot.com/]]>). Я полистал последние топики и увидел, что этот парень пишет в основном про программирование на Delphi. Некоторые посты мне показались очень интересными, поэтому я решил сделать их переводы. Надеюсь, мой труд придется тебе по душе. Свой цикл переводов я хочу начать с заметки: "Web 2.0 programming with Object Pascal (Part 1)". Поехали!

Я думаю, что Вы испытываете такое же чувство как и я, когда "web разработчик" смотрит на экран вашего компьютера и говорит: "Вау! Ваша программа написана на Delphi! Я разрабатывал на нем (Delphi), до того как начал создавать web-приложения". Когда я это слышу, мне хочется возразить и объяснить, что возможно обойтись Delphi (Object Pascal) и даже достичь более лучших результатов, чем при использовании языков программирования, ориентированных на WEB-разработку.

Надеюсь, что данный цикл заметок продемонстрирует возможности Delphi и FreePascal, которые могут быть применены при разработки WEB-приложений. Если вы являетесь PHP-Python-Ruby-ASP (или любой другой язык программирования) программистом, то просто оцените возможности современного объектного паскаля.

Часть 1: CGI + ExtJs

До того как Вы продолжите читать эту заметку крайне желательно почитать теорию о CGI (]]>http://en.wikipedia.org/wiki/Common_Gateway_Interface]]>) и ознакомиться с библиотекой ExtJs (]]>http://www.extjs.com/]]>).

Предположим, что вы уже ознакомились с CGI и ExtJS. Создадим CGI приложение, способное создавать JSON (]]>http://en.wikipedia.org/wiki/JSON]]>) ответы на интерфейс ExtJs.

Рассматриваемый мною пример, может быть скомпилирован с помощью FreePascal 2.5.1. В качестве CGI фреймворка я буду использовать fcl-web, но вы можете выбрать, тот, который вам больше всего нравится. Это могут быть как PowTils (]]>http://powtils.googlecode.com/]]>), так ExCGI. Если для разработки примера вы выбираете Delphi, то создавайте CGI приложение с использованием WebBroker. Это великолепный фреймворк для разработки web-приложений.

Все рассматриваемые примеры в этой статье работают под Apache 2. Само собой, Вы можете использовать любой другой web-server, поддерживающий CGI (IIS, LightHttpd и др.). Для тестирования своих примеров я использовал Ubuntu 9.10 64bit, FreePascal 2.5.1 и ExtJs 3.1.1.

Клиент

Стартовой точкой первого примера будет создание простой ExtJs GridPanel в статической html странице. Данные отображаемые в таблицы, будут пока загружены из JSon файла, т.к. отсутствует взаимодействие с нашим CGI-приложением.

Теперь создадим простую html страницу, содержащую ExtJs grid, скопируйте и вставьте приведенный ниже код в используемый вами текстовый редактор. Сохраните его в файл под именем grid1.html в директорию /var/www/samples.

<html>
    <head>
      <meta equiv="Content-Type" content="text/html; charset=utf-8">
      <title>Grid Example 1</title>
      <link rel="stylesheet" type="text/css" href="/extjs/resources/css/ext-all.css">
      <script type="text/javascript" src="/extjs/adapter/ext/ext-base.js"></script>
      <script type="text/javascript" src="/extjs/ext-all.js"></script>
    </head>
    <body>
      <script type="text/javascript" src="grid1.js"></script>
      <h1>Grid Example 1</h1>
      <div id="grid1"></div>
    </body>
</html>

Взгляните на приведенный выше html код. В нем в нескольких местах имеются ссылки на /extjs. Это означает, что я поместил распакованные файлы библиотеки ExtJs в директорию /var/www. Корневой папкой для моего Apache является /var/www, а файлы библиотеки ExtJs располагаются в папке /var/www/extjs. Помимо ссылок на extjs, в коде имеется ссылка на файл grid1.js, который в моем случае также располагается в той же директории, где и файл grid1.html.

Теперь создадим уже упомянутый файл grid1.js. Код этого файла представлен ниже:

xt.onReady(function(){
 
var dataStore = new Ext.data.JsonStore({
    url: '/samples/customerslist.json',
    root: 'rows',
    method: 'GET',
    fields: [
      {name: 'firstname', type: 'string'},
      {name: 'lastname', type: 'string'},
      {name: 'age', type: 'int'},
      {name: 'phone', type: 'string'}
    ]
  });
 
var myGrid1 = new Ext.grid.GridPanel({
  id: 'customerslist',
  store: dataStore,
  columns: [
    {id: "firstname", header: "First Name", width: 100, dataIndex: "firstname", sortable: true},
    {header: "Last Name", width: 100, dataIndex: "lastname", sortable: true},
    {header: "Age", width: 100, dataIndex: "age", sortable: true},
    {header: "Phone", width: 100, dataIndex: "phone", sortable: true}
  ],
  autoLoad: false,
  stripeRows: true,
  autoHeight: true,
  width: 400
});
 
dataStore.load();
 
myGrid1.render('grid1');
});

Данный файл создает экземпляр объекта Ext.data.JsonStore. Этот объект отвечает за загрузку данных из внешнего источника JSon. В нашем случае это файл "/samples/cusomerlist.json". myGrid1 - это экземпляр объекта Ext.grid.GridPanel, именно в нем будут, содержатся загруженные данные из JsonStore. В последней строке этого файла происходит определение места, где будет располагаться созданная нами таблица. В нашем случае атрибут ID контейнера DIV будет называться "gird1" (смотри grid1.html).

Попробуйте проиграться с этим примером, усовершенствуйте его немного. Добавьте больше данных в JSon файл и измените различные свойства таблицы. Как наиграетесь, возвращайтесь к статье.

Динамические JSON данные

Следующим шагом будет создание CGI приложения, которое будет отвечать за получение запросов из JSonStore. Это достаточно простая задача для нашей CGI программы. Необходимо создать строку, содержащую те же данные что и в файле "customerlist.json", а после отправить их клиенту.

Первое FPC CGI приложение:

program cgiproject1;
 
{$mode objfpc}{$H+}
 
uses
Classes,SysUtils,httpDefs,custcgi;
 
Type
TCGIApp = Class(TCustomCGIApplication)
Public
  Procedure HandleRequest(ARequest : Trequest; AResponse : TResponse); override;
end;
 
Procedure TCGIApp.HandleRequest(ARequest : Trequest; AResponse : TResponse);
begin
AResponse.Content := 'Hello!';
end;
 
begin
With TCGIApp.Create(Nil) do
  try
    Initialize;
    Run;
  finally
    Free;
  end;
end.

Сохраните этот код в файл с именем "customerlist.pp", а затем откомпилируйте с помощью команды "fpc customerlist.pp". После выполнения компиляции, поместите результат в папку /var/www/cgi-bin и запустите ваш web-браузер. Как только запустится браузер, попробуйте пройти по адресу "http://localhost/cgi-bin/customerslist". Если вы не ошиблись при переписывании кода, то вы увидите строку "Hello".

Теперь, научим нашу программу отвечать в формате JSon. Замените строку AResponse.Content := 'Hello!'; на приведенный ниже код.

AResponse.Content :=
'{' +
'  "rows": [ ' +
'    {"firstname": "Leonardo", "lastname": "Rame", "age": 35, "phone": "1234556"}, ' +
'    {"firstname": "John", "lastname": "Williams", "age": 15, "phone": "435233"}, ' +
'    {"firstname": "Cecilia", "lastname": "Strada", "age": 28, "phone": "423234"}, ' +
'    {"firstname": "Gary", "lastname": "Thomson", "age": 43, "phone": "123"} ' +
'  ] ' +
'}';

Компилируйте и копируйте в директорию /var/www/cgi-bin и тестируйте. Чтобы вызвать его из JsonStore, вернитесь к файлу grid1.js и измените значение свойства url JsonStore на "/var/www/cgi-bin/customerslist".

Примечание: Если вы работаете под управлением системой Windows, то файл "customerlist" должен быть создан как "customerlist.exe". Поэтому проверьте, чтобы вместо "/var/www/cgi-bin/customerslist" у вас везде был customerlist.exe.

Если вам не нравится создавать JSON ответы как простые строки, то вы можете воспользоваться фреймворком fcl-json. С его помощью, вы сможете конструировать JSon запрос, используя объектно-ориентированный подход. Чтобы приспособить наш пример под использование fcl-json, вы должны подключить к своему проекту модуль fpjson, а затем немного изменить метод HandleRequest объект TCGIApp:

procedure TCGIApp.HandleRequest(ARequest : Trequest; AResponse : TResponse);
var
lPerson1: TJSONObject;
lPerson2: TJSONObject;
lPerson3: TJSONObject;
lPerson4: TJSONObject;
lJson: TJSONObject;
lJsonArray: TJSONArray;
 
begin
lPerson1 := TJSONObject.Create;
lPerson2 := TJSONObject.Create;
lPerson3 := TJSONObject.Create;
lPerson4 := TJSONObject.Create;
 
lJsonArray := TJSONArray.Create;
lJson := TJSONObject.Create;
try
  (* populate lPerson1 *)
  lPerson1.Add('firstname', TJsonString.Create('Leonardo'));
  lPerson1.Add('lastname', TJsonString.Create('Rame'));
  lPerson1.Add('age', TJSONIntegerNumber.Create(35));
  lPerson1.Add('phone', TJsonString.Create('1234567'));
  (* populate lPerson2 *)
  lPerson2.Add('firstname', TJsonString.Create('John'));
  lPerson2.Add('lastname', TJsonString.Create('Williams'));
  lPerson2.Add('age', TJSONIntegerNumber.Create(15));
  lPerson2.Add('phone', TJsonString.Create('14567'));
  (* populate lPerson3 *)
  lPerson3.Add('firstname', TJsonString.Create('Cecilia'));
  lPerson3.Add('lastname', TJsonString.Create('Strada'));
  lPerson3.Add('age', TJSONIntegerNumber.Create(29));
  lPerson3.Add('phone', TJsonString.Create('34567'));
  (* populate lPerson4 *)
  lPerson4.Add('firstname', TJsonString.Create('Gary'));
  lPerson4.Add('lastname', TJsonString.Create('Thomson'));
  lPerson4.Add('age', TJSONIntegerNumber.Create(43));
  lPerson4.Add('phone', TJsonString.Create('344567'));
 
  (* Fill the array *)
  lJsonArray.Add(lPerson1);
  lJsonArray.Add(lPerson2);
  lJsonArray.Add(lPerson3);
  lJsonArray.Add(lPerson4);
 
  (* Add the array to rows property *)
  lJson.Add('rows', lJsonArray);
 
  AResponse.Content := lJson.AsJSON;
finally
  lJson.Free;
end;
end;

Используя фреймворк fcl-json разработчику приходится писать больше кода, но зато, код становится более читабельным и простым. Какой способ применять - решать вам.

Отображение данных из базы данных

Теперь я заменю статические JSon данные, на динамические, получаемые в результате выборки данных из базы данных. В приведенном мной примере, я устанавливаю соединение с СУБД FireBird. У меня он запущен на локалхосте. Для установки соединения с FireBird я применяю фреймворк fcl-db и класс TIBConnection. Ниже представлен код финальной части проекта, рассматриваемого в этой части заметки. В нем реализовано соединение с БД и показ страницы. На странице располагается таблица, с данными, полученными из базы данных.

program cgiproject1;
 
{$mode objfpc}{$H+}
 
uses
Classes,SysUtils,
httpDefs,custcgi, // needed for creating CGI applications
fpjson, // needed for dealing with JSon data
Db, SqlDb, ibconnection; // needed for connecting to Firebird/Interbase;
 
Type
TCGIApp = Class(TCustomCGIApplication)
Private
  FConn: TSqlConnection;
  FQuery: TSqlQuery;
  FTransaction: TSqlTransaction;
  procedure ConnectToDataBase;
Public
  Procedure HandleRequest(ARequest : Trequest; AResponse : TResponse); override;
end;
 
procedure TCGIApp.ConnectToDataBase;
begin
FConn := TIBConnection.Create(nil);
FQuery := TSqlQuery.Create(nil);
FTransaction := TSqlTransaction.Create(nil);
with FConn do
begin
  DatabaseName := 'TEST-DATABASE';
  UserName := 'SYSDBA';
  Password := 'masterkey';
  HostName := 'localhost';
  Connected := True;
  Transaction := FTransaction;
  FQuery.Database := FConn;
end;
end;
 
Procedure TCGIApp.HandleRequest(ARequest : Trequest; AResponse : TResponse);
var
lPerson: TJSONObject;
lJson: TJSONObject;
lJsonArray: TJSONArray;
 
begin
(* Query the database *)
FQuery.Sql.Text := 'select * from people';
FQuery.Open;
FQuery.First;
 
lJsonArray := TJSONArray.Create;
lJson := TJSONObject.Create;
try
  while not FQuery.Eof do
  begin
    lPerson := TJSONObject.Create;
    lPerson.Add('firstname', TJsonString.Create(FQuery.FieldByName('FirstName').AsString));
    lPerson.Add('lastname', TJsonString.Create(FQuery.FieldByName('LastName').AsString));
    lPerson.Add('age', TJSONIntegerNumber.Create(FQuery.FieldByName('Age').AsInteger));
    lPerson.Add('phone', TJsonString.Create(FQuery.FieldByName('Phone').AsString));
    FQuery.Next;
    (* Fill the array *)
    lJsonArray.Add(lPerson);
  end;
  (* Add the array to rows property *)
  lJson.Add('rows', lJsonArray);
  AResponse.Content := lJson.AsJSON;
finally
  lJson.Free;
end;
end;
 
begin
With TCGIApp.Create(Nil) do
  try
    Initialize;
    ConnectToDatabase;
    Run;
  finally
    Free;
  end;
end.

Заключение

В следующей части заметки, мы расширим функционал нашего приложения и снабдим его такими полезными функциями как: добавление, обновление и удаление данных.

Автор: Leonardo
WWW: ]]>http://leonardorame.blogspot.com]]>
Оригинальная статья: ]]>http://leonardorame.blogspot.com/2010/02/web-20-programming-with-object-pascal.html]]>

Перевод: Антонов Игорь aka Spider_NET
email:

ВложениеРазмер
web-samples1-source.rar1.92 КБ