Sign in Join
Wiki copytemporary

fpWeb Tutorial/ru: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11` Первоначально основано на учебнике fcl-web (в формате PDF) пользователя форума `[`Leledumbo`](https://forum.lazarus.freepascal.org/index.php?action=profile;u=7523)`.`
22
33## Введение
44
55fpWeb - это фреймворк веб-приложений, поставляемый FPC в своем дистрибутиве по умолчанию как часть пакета fcl-web. Сам фреймворк построен на основе функций fcl-web. Фреймворк построен с мыслью о RAD, чтобы эффективно использовать компонентность при создании динамического контента. Предоставляется пакет Lazarus, который может использовать фреймворк методом перетаскивания для управления сеансами и создания контента. В этом руководстве мы попытаемся охватить основные функции fpWeb, чтобы с его помощью можно было создать обычное веб-приложение. Обратите внимание, что это руководство не пытается научить вас HTTP-протоколу, HTML, CSS, JavaScript или манипуляциям с базой данных, поскольку владение протоколом и клиентскими языками должно быть предварительным условием работы для каждого программиста веб-приложений, а манипуляции с базой данных не отличаются от десктопной реализации.
66
77## Архитектура (ПОЖАЛУЙСТА, прочтите)
88
99Перед тем, как начать, необходимо знать архитектуру и поток приложения, чтобы избежать путаницы, когда определенные вещи не работают или работают неожиданно. Поэтому, пожалуйста, потратьте немного времени на чтение этого раздела.
1010
1111### Приложение
1212
1313Под приложением здесь понимается протокол, который будет реализовывать ваше приложение. fpWeb с радостью переключится с CGI, FCGI, с модуля Apache на встроенный сервер и т.д., если fcl-web реализует еще что- нибудь в будущем. Каждое приложение реализовано в своем собственном модуле, поэтому для переключения с одного приложения на другое, за исключением модуля Apache, нужно просто изменить соответствующий идентификатор в разделе uses. В настоящее время (по состоянию на 3.0.0 / 3.1.1) это:
1414
1515- fpCGI -\> CGI
1616- fpFCGI -\> FastCGI
1717- fpApache (также требуется httpd) -\> модуль Apache
1818- fpHttpApp -\> встроенный сервер
1919- microhttpapp -\> встроенный сервер с использованием библиотеки GNU libmicrohttp.
2020- fphttpsys -\> поддержка системой Windows протокола HTTP.
2121
2222В этом руководстве мы будем использовать встроенный сервер для простоты, потому что вам не придется иметь дело с настройкой виртуального сервера и путаницей со сложным файлом конфигурации и управлением службами. Ваше приложение будет единым переносным бинарным веб-приложением! Другая причина может заключаться в том, что существует больше, чем одно приложение веб-сервера, и каждое имеет свой способ настройки. Было бы излишним пытаться охватить их всех, пока их документация уже выполняет свою работу. Модуль Apache реализован как (динамическая) библиотека, тогда как другие протоколы являются обычным приложением. Каждое приложение может иметь определенные свойства (например, порт), доступные и значимые только для этого приложения. Вот почему, если вы посмотрите на примеры fcl-web, пары .lpi / .lpr для каждого протокола помещаются в свои собственные каталоги, только веб-модули являются общими.
2323
2424### Веб-модули
2525
2626![](/wiki-images/5/50/FpWeb-overview.png)
2727
2828Приложения fpWeb состоят из веб-модулей, которые фактически создают контент. Веб-модуль может содержать веб-действия, которые могут еще больше разделить функциональность. Например, веб-модуль аутентификации может иметь веб-действия входа и выхода. Хотя модуль about web может вообще не нуждаться в действии и обслуживает только один контент. Веб-модуль интегрирован с fpTemplate, который можно использовать для создания динамического контента из файла шаблона. Это примерно похоже на то, что делает PHP, только разрыв между логикой и представлением скорее вынужденный, чем предполагаемый. Некоторые говорят, что fpTemplate реализует пассивное представление, в то время как PHP по умолчанию реализует шаблон проектирования активного представления.
2929
3030## Установка
3131
3232Пакет fpWeb для Lazarus по умолчанию не установлен (но идет в стандартной поставке). Чтобы включить fpWeb:
3333
34341. Откройте Lazarus и выберите **Package-\>Install/Uninstall Package**
35352. В списке Available (Доступно) для установки найдите **weblaz** и нажмите **Install selection** (Установить выбранное). Нажмите **Save and rebuild IDE**(Сохранить и пересобрать IDE) и подтвердите, нажав **Continue**(Продолжить).
36363. Позвольте IDE пересобраться и перезапуститься. Если все пойдет хорошо, у вас должна появиться вкладка fpWeb на палитре компонентов, как показано ниже:
3737
3838
3939
4040![](/wiki-images/5/55/Fpwebtutorial_inst.png)
4141
4242## Специализированные модули
4343
4444Класс*TFPWebModule* (используемый ниже) - это простой пример модуля fpWEB, который можно использовать для всех видов HTTP-запросов.
4545
4646Однако fpWEB поставляется с некоторыми специализированными модулями, которые имеют дополнительную поддержку для специализированных задач:
4747
4848- Класс **TSimpleFileModule** в модуле *fpwebfile.pp* можно использовать для отправки файлов. Вы указываете ему каталог, и он делает все остальное.
4949- Класс **TFPHTMLModule** в модуле *fphtml.pp* можно использовать для создания HTML.
5050- Класс **TProxyWebModule** в модуле *fpwebproxy.pp* - это готовый прокси для пересылки.
5151- Класс **TFPWebProviderDataModule** в модуле *fpwebdata.pp* обслуживает данные в формате JSON, которые могут использоваться хранилищами ExtJS.
5252- Класс **TSQLDBRestModule** в модуле *sqldbrestmodule.pp* реализует полноценный сервер REST, поддерживаемый SQLDB. См. дополнительную информацию в [[SQLDBRestBridge]].
5353- Класс **TJSONRPCModule** в модуле *webjsonrpc.pp* реализует службу JSON-RPC.
5454- Класс **TExtDirectModule** в модуле *fpextdirect.pp* реализует вариант Ext.Direct службы JSON-RPC.
5555
5656## Hello, World!
5757
5858Создадим простое веб-приложение. Как обычно преподают при изучении программирования: "Hello, World!" будет нашим первым приложением.\
5959
60601\. Откройте Lazarus и выберите **Project-\>New Project**, затем выберите **HTTP server Application**
6161
6262
6363![Создать новое приложение HTTP-сервера](/wiki-images/5/57/Fpwebtutorial_create_app.png)\
6464
65652\. Появится другое диалоговое окно для обслуживания статических файлов, выбора порта и многопоточности. Просто используйте порт по умолчанию 8080.
6666
6767
6868``` pascal
6969You may skip the static files serving (go to tips and tricks section if you want to know it more).
7070```
7171
7272(*Вы можете пропустить обслуживание статических файлов (перейдите в раздел советов и рекомендаций, если хотите узнать больше)*)
7373
7474
7575![](/wiki-images/e/e6/Fpwebtutorial_port_selection.PNG)
7676
7777:**ВАЖНО!:**
7878
7979
8080Если вы решите использовать потоки на *nix, не забудьте добавить cthreads в качестве первого модуля в разделе uses .lpr, в противном случае будет создан RTE 232. При запуске с консоли должно отображаться сообщение:
8181
8282``` pascal
8383This binary has no thread support compiled in. Recompile the application with a thread-driver in the program uses clause before other units using thread.
8484```
8585
8686(*В этом двоичном файле не скомпилирована поддержка потоков. Перекомпилируйте приложение с драйвером потока в программе using предложение перед другими модулями, использующими поток*)\
8787
88883\. С 14 января 2017г. (или FPC 3.0.4) вам может потребоваться открыть .lpr и добавить следующую строку в основной текст, если ее еще там нет:
8989
9090
9191``` pascal
9292Application.LegacyRouting := true;
9393```
9494
9595причина будет объяснена в главе [[fpWeb Tutorial/ru|Маршрутизация]].
9696
97974\. Что бы вы ни выбрали, нажмите "OK", и вы будете представлены в приложении fpWeb с одним модулем по умолчанию.\
98985\. Переместите фокус на модуль и перейдите в **Object Inspector**. Не стесняйтесь переименовывать модуль, если хотите.\
99996\. Выберите вкладку **Events** и нажмите кнопку справа от второго столбца в строке**OnRequest**, чтобы создать обработчик событий.
100100
101101
102102![Создание обработчика OnRequest веб-модуля в object inspector](/wiki-images/8/8d/Fpwebtutorial_on_request.png)\
103103
104104
105105Вы будете перенаправлены в редактор исходного кода со следующим кодом:
106106
107107:
108108
109109``` pascal
110110procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
111111AResponse: TResponse; var Handled: Boolean);
112112begin
113113 |
114114end;
115115```
116116
117117
118118Заполните событие:
119119
120120:
121121
122122``` pascal
123123procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
124124AResponse: TResponse; var Handled: Boolean);
125125begin
126126 AResponse.Content := 'Hello, World!';
127127 Handled := true;
128128end;
129129```
130130
1311317\. Теперь запустите приложение (или нажмите F9).\
1321328\. Откройте браузер и введите:
133133
134134
135135<http://localhost:8080/>\
136136
1371379\. Вы должны увидеть отображающееся "Hello, World!".\
138138
139139Если это не так, проверьте ниже:
140140
141141- Фреймворк выполняет много операций по обработке исключений, и отладчик IDE может их поймать и прервать работу вашего приложения. Можно добавить большинство исключений в список игнорирования, чтобы вы могли больше сосредоточиться на потоке приложения. Продолжайте пропускать и продолжайте, пока не перестанет появляться диалоговое окно и браузер не покажет результат.
142142
143143- **Handled := true** - это способ, которым мы сообщаем фреймворку, что запрос был обработан. Если вы не установите его (или установите для него значение **false**), вместо этого будет отображаться страница с ошибкой. На данный момент это не влияет на поток запросов, но будет позже. Так что держите это так, пока не придет время, чтобы использовать его с пользой.
144144
145145- другой трек: тест без брандмауэра, загруженного в ОЗУ (как приложение, как сервис или демон, или как оба).
146146
147147## Чтение данных GET и POST
148148
149149Динамический контент, скорее всего, будет запускаться из пользовательского ввода, либо через формы, предоставляя значения в URL-адресе, и т. Д. Эти данные отправляются вместе с запросом, который представлен в методе как **ARequest** параметр типа **TRequest**.
150150
151151### Чтение GET
152152
153153Данные GET предоставляются как **ARequest.QueryFields**, который является потомком **TStrings**. Короче говоря, все, что вы обычно делаете с TStrings, применимо здесь, например, доступ к данным в стиле карты через свойство **Values**.
154154
155155Повторно используя приведенный выше код, замените тело метода на:
156156
157157``` pascal
158158procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
159159 AResponse: TResponse; var Handled: Boolean);
160160var
161161 LName: String;
162162begin
163163 LName := ARequest.QueryFields.Values['Name'];
164164 if LName = EmptyStr then
165165 with AResponse.Contents do
166166 begin
167167 Add('<form action="' + ARequest.URI + '" method="GET"');
168168 Add('<label for="name">Please tell me your name:</label>');
169169 Add('<input type="text" name="name" id="name" />');
170170 Add('<input type="submit" value="Send" />');
171171 Add('</form>');
172172 end
173173 else
174174 AResponse.Content := 'Hello, ' + LName + '!';
175175 Handled := true;
176176end;
177177```
178178
179179**ARequest.URI** - это просто ссылка на текущий URI, поэтому даже когда вы меняете зарегистрированный модуль или имя действия, этот код остается прежним.
180180
181181Обратите внимание, что, как и в Паскале, обращение к данным осуществляется без учета регистра.
182182
183183Теперь вы можете попробовать запросить /, который отобразит
184184
185185` Please tell me your name`
186186
187187и /?name=\<напишите здесь что угодно, например: Bob\>, который отобразит
188188
189189` Hello, Bob!`
190190
191191### Чтение POST
192192
193193POST на самом деле не сильно отличается от GET, отличается только тем, к какому свойству получить доступ. Если доступ к GET осуществляется через **ARequest.QueryFields**, доступ к POST осуществляется через **ARequest.ContentFields**. Стиль POST предыдущего кода:
194194
195195``` pascal
196196procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
197197 AResponse: TResponse; var Handled: Boolean);
198198var
199199 LName: String;
200200begin
201201 LName := ARequest.ContentFields.Values['Name'];
202202 if LName = EmptyStr then
203203 with AResponse.Contents do
204204 begin
205205 Add('<form action="' + ARequest.URI + '" method="POST"');
206206 Add('<label for="name">Please tell me your name:</label>');
207207 Add('<input type="text" name="name" id="name" />');
208208 Add('<input type="submit" value="Send" />');
209209 Add('</form>');
210210 end
211211 else
212212 AResponse.Content := 'Hello, ' + LName + '!';
213213 Handled := true;
214214end;
215215```
216216
217217### Чтение загружаемых файлов
218218
219219Единственное исключение - чтение полей **multipart/form-data**, то есть файлов. Оно доступно в **ARequest.Files** как экземпляр **TUploadedFiles**, который является потомком **TCollection**. Ниже приведен общедоступный интерфейс TUploadedFiles, который вы можете использовать для доступа к файлам:
220220
221221``` pascal
222222TUploadedFiles = Class(TCollection)
223223...
224224public
225225 Function First: TUploadedFile;
226226 Function Last: TUploadedFile;
227227 Function IndexOfFile(AName: String) : Integer;
228228 Function FileByName(AName: String) : TUploadedFile;
229229 Function FindFile(AName: String) : TUploadedFile;
230230 Property Files[Index: Integer] : TUploadedFile read GetFile Write SetFile; default;
231231end;
232232```
233233
234234Каждый **TUploadedFile** сам по себе имеет несколько свойств:
235235
236236``` pascal
237237TUploadedFile = Class(TCollectionItem)
238238...
239239Public
240240 Destructor Destroy; override;
241241 Property FieldName: String Read FFieldName Write FFieldName;
242242 Property FileName: String Read FFileName Write FFileName;
243243 Property Stream: TStream Read GetStream;
244244 Property Size: Int64 Read FSize Write FSize;
245245 Property ContentType: String Read FContentType Write FContentType;
246246 Property Disposition: String Read FDisposition Write FDisposition;
247247 Property LocalFileName: String Read FLocalFileName Write FLocalFileName;
248248 Property Description: String Read FDescription Write FDescription;
249249end;
250250```
251251
252252Они должны быть достаточно информативными, за исключением **FileName** и **LocalFileName**. **FileName** - это **name** исходного файла, загружаемого с клиента, **LocalFileName** - это путь к файлу на сервере, где файл временно хранится. Обратите внимание на разницу, выделенную жирным шрифтом выше.
253253
254254Опять же, повторное использование того же обработчика запросов:
255255
256256``` pascal
257257procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
258258 AResponse: TResponse; var Handled: Boolean);
259259var
260260 n: Integer;
261261 f: TUploadedFile;
262262 i: Integer;
263263begin
264264 n := ARequest.Files.Count;
265265 if n = 0 then
266266 with AResponse.Contents do
267267 begin
268268 Add('<form id="form" action="' + ARequest.URI + '" method="POST" enctype="multipart/form-data">');
269269 Add('<label for="name">Drag n drop or click to add file:</label>');
270270 Add('<input type="file" name="input" />');
271271 Add('<input type="submit" value="Send" />');
272272 Add('</form>');
273273 end
274274 else
275275 begin
276276 f := ARequest.Files[0];
277277 AResponse.Contents.LoadFromStream(f.Stream);
278278 end;
279279 Handled := true;
280280end;
281281```
282282
283283перетащите и бросьте файл (желательно текстовый, так как он будет отображаться как текст) в поле входного файла (или нажмите соответствующую кнопку), затем нажмите кнопку *Send*' (Отправить). Должно отобразиться содержимое файла.
284284
285285## Cookies
286286
287287### Отправка
288288
289-<img src="cookie_session_Object_Pascal.png" title="Концепция «cookie», изобретенная Netscape в 1994 году, позволяет HTTP-серверу идентифицировать всех своих клиентов." width="400" alt="Концепция «cookie», изобретенная Netscape в 1994 году, позволяет HTTP-серверу идентифицировать всех своих клиентов." />\
289+![Концепция «cookie», изобретенная Netscape в 1994 году, позволяет HTTP-серверу идентифицировать всех своих клиентов.](/wiki-images/5/5c/Cookie_session_Object_Pascal.png)\
290290Файлы cookie - небольшой фрагмент данных, отправленный веб-сервером и хранимый на компьютере пользователя. Веб-клиент (обычно веб-браузер) всякий раз при попытке открыть страницу соответствующего сайта пересылает этот фрагмент данных веб-серверу в составе HTTP-запроса ([Wiki](https://ru.wikipedia.org/wiki/Cookie)). **AResponse.Cookies** содержит список отправляемых файлов cookie. Это потомок **TCollection**, соответственно содержащийся **TCookie** является потомком **TCollectionItem**. Следовательно, вы можете использовать TCollection как способ управления элементами для управления ими.
291291
292292Вот пример:
293293
294294``` pascal
295295procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
296296 AResponse: TResponse; var Handled: Boolean);
297297var
298298 C: TCookie;
299299begin
300300 C := AResponse.Cookies.Add;
301301 C.Name := 'mycookie';
302302 C.Value := 'somevalue';
303303 Handled := true;
304304end;
305305```
306306
307307Вы не увидите никаких результатов в своем браузере. Но если вы используете какие-то инструменты разработчика (в Chrome есть встроенный) можно увидеть заголовок ответа:
308308
309309![](/wiki-images/6/69/Fpwebtutorial_cookie_set.png)
310310
311311Обратите внимание, что файл cookie имеет атрибуты, поэтому вы можете установить не только имя и значение. Просмотрите интерфейс TCookie, чтобы узнать, какие свойства поддерживаются.
312312
313313### Получение
314314
315315Как только вы укажете выше заголовок **Set-Cookie**, последующий запрос на ваш сайт будет содержать дополнительный заголовок, содержащий значение, которое вы просили установить ранее:
316316
317317![](/wiki-images/9/93/Fpwebtutorial_cookie_get.png)
318318
319319К счастью, способ чтения не отличается от данных GET и POST. Связанное свойство - **ARequest.CookieFields**. Чтобы прочитать ранее отправленный файл cookie:
320320
321321``` pascal
322322procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
323323 AResponse: TResponse; var Handled: Boolean);
324324begin
325325 AResponse.Contents.Add('<p>Cookie get: ' + ARequest.CookieFields.Values['mycookie'] + '</p>');
326326 Handled := true;
327327end;
328328```
329329
330330## Сессии
331331
332332TFPWebModule является потомком TSessionHTTPModule, поэтому имеет возможность управления сессией. Сессия основана на модулях, поэтому каждый модуль может выбирать, использовать или не использовать управление сессией.
333333
334334Сессия реализована абстрактно. По умолчанию реализация не предусмотрена. Один пример реализации с использованием файлов .ini приведен в модуле **iniwebsession**. Вы должны иметь этот модуль в своем проекте или реализовать его, чтобы управление сессией работало. Если вы решите реализовать один из них, в основном вам необходимо расширить и реализовать абстрактные методы в классах **TCustomSession** и *'TSessionFactory'*.
335335
336336### Активация
337337
338338Чтобы активировать управление сессией, установите для свойства **CreateSession** в значение true. Сессия будет запущена до обработки запроса. В случае новой сессией будет вызвано **OnNewSession**. Здесь инициализируйте переменные сессии.
339339
340340### Управление переменными сессии
341341
342342Переменные сессии представлены как **Session.Variables** (примечание: объект сессии является эквивалентом массива \$ _SESSION, используемого в Php). Это строка для сопоставления строк, подобная структуре, поэтому вы можете читать / писать так:
343343
344344``` pascal
345345Session.Variables['myvar'] := myvar; // записываем
346346...
347347myvar := Session.Variables['myvar']; // читаем
348348```
349349
350350Задание переменной пустой строки **НЕ** удаляет ее. Если вы действительно хотите удалить переменную, вместо этого вызовите **Session.RemoveVariable**.
351351
352352### Завершение
353353
354354Вызывайте **Session.Terminate** всякий раз, когда вы хотите завершить сессию (например, выход пользователя из системы). Сессия также автоматически истечет, если следующий запрос поступит после **Session.TimeOutMinutes** с момента последнего запроса. Когда сессия завершается, будет вызван **OnSessionExpired**. Сделайте там все, что вам нужно.
355355
356356## Маршрутизация
357357
358358Начиная с FPC 3.0.4, был реализован новый механизм маршрутизации. Вместо сохранения обратной совместимости решено, что по умолчанию будет использоваться новая маршрутизация. Таким образом, любой старый код (или новый код в зависимости от старой маршрутизации) необходимо перенести, добавив:
359359
360360``` pascal
361361Application.LegacyRouting := true;
362362```
363363
364364в *.lpr файле.
365365
366366### Старый способ
367367
368368#### Использование нескольких модулей
369369
370370В вашем приложении может быть несколько модулей. Щелкните меню "File", затем щелкните "New...". В появившемся диалоговом окне выберите "Web Module" в дереве выбора модулей.
371371
372372![](/wiki-images/e/e5/Fpwebtutorial_new_module.png)
373373
374374затем нажмите OK.
375375
376376Поскольку в вашем приложении существует несколько модулей, вы больше не можете делать запрос (request) только с использованием `/`. Платформа не сможет волшебным образом выбрать, какой модуль должен обслуживать ответ (response), поэтому есть два способа указать, какой модуль вы хотите вызвать:
377377
378378- /\<module name\>
379379- /?module=\<module name\>
380380
381381Во втором формате вы можете изменить "module" (который является значением по умолчанию) на любой допустимый ключ строки запроса, изменив **Application.ModuleVariable**.
382382
383383#### С использованием Actions
384384
385385До сих пор мы использовали только веб-модули с обработчиком единого запроса. Это не сильно масштабируется, поскольку ваше веб-приложение становится все более и более сложным. Более того, некоторые функции могут иметь общие свойства и могут быть лучше логически сгруппированы, например:
386386
387387- Account module
388388 - Login action
389389 - Logout action
390390 - Register action
391391- Product module
392392 - Create action
393393 - Update action
394394 - Delete action
395395 - Details action
396396
397397##### Поток обработки запросов (request)
398398
399399Прежде, чем использовать действие (action), важно знать, как обрабатывается запрос fpWeb. В противном случае ваше действие (action) может оказаться бесполезным, потому что ваш модуль данных всегда обрабатывает запрос. Как такое могло случится? Возвращаясь немного назад, вспомните **Handled := true**, которое мы всегда делали раньше? Теперь в игру вступает параметр **Handled**.
400400
401401**Каждый** запрос сначала будет проходить через модуль **OnRequest**, независимо от запрошенного действия. Только если для этого **не** установлено значение Handled, выполняется OnRequest веб-действия.
402402
403403В общем, поток запросов:
404404
405405![](/wiki-images/e/ea/Fpwebtutorial_request_flow.png)
406406
407407Обратите внимание на окошко "Our Concern" (наша озабоченность), это то, на что мы собираемся обратить наше внимание.
408408
409409##### Добавление действий в веб-модули
410410
411411Чтобы добавить действие, выберите веб-модуль и перейдите в инспектор объектов. На вкладке свойств выберите "Actions" и нажмите кнопку во втором столбце.
412412
413413![](/wiki-images/9/95/Fpwebtutorial_manage_actions.png)
414414
415415Появится всплывающее окно, в котором вы можете добавлять, удалять и изменять порядок действий.
416416
417417![](/wiki-images/d/db/Fpwebtutorial_edit_actions.png)
418418
419419Нажмите "Add", в списке появится новое действие. Выберите его, затем перейдите в инспектор объектов. В настоящее время он будет показывать свойства и события этого вновь созданного действия. Переименуйте свойство Name (это будет имя, которое вы напишете в URL-адрес, поэтому дайте ему короткое, простое, но описательное имя) как вы желаете, я выберу "Hello". Двигаемся дальше на вкладку событий, сделайте то же самое, что и OnRequest для модуля, нажмите кнопку справа от строки OnRequest, чтобы создать обработчик запросов.
420420
421421![](/wiki-images/2/2e/Fpwebtutorial_action_onrequest.png)
422422
423423Вы будете представлены в том же интерфейсе OnRequest, но этот обрабатывает веб-действие вместо веб-модуля. Все, что вы можете сделать в OnRequest веб-модуля, можно сделать и здесь. Скопируйте тело метода из 'Hello, World!' раздела.
424424
425425Не забудьте удалить **Handled := true** из тела **OnRequest** предыдущего веб-модуля (или полностью удалить событие), чтобы действие позаботилось об обработке запроса.
426426
427427Запустите свой проект и запустите браузер. Теперь, поскольку обработка запросов делегирована веб-действию, вы больше не можете просто запрашивать /, но вам нужно / \<action name\> или \<свойство ActionVar модуля\>=\<action name\>. Обратите внимание, что \<свойство ActionVar модуля\> по умолчанию имеет значение пустой строки, в отличие от **Application.ModuleVariable**, для которого "модуль" является значением по умолчанию. Итак, по умолчанию вы можете использовать только /\<action name\> формы.
428428
429429Если у вас несколько модулей, то у вас есть множество вариантов:
430430
431431- /\<module name\>/\<action name\>
432432- /\<module name\>?action=\<action name\>
433433- /\<action name\>?module=\<module name\>
434434- /?module=\<module name\>&action=\<action name\>
435435
436436Обратите внимание, что как только у модуля есть хотя бы одно действие, /\<module или action name\> по умолчанию сопоставляется с /\<action name\>. Чтобы изменить поведение таким образом, чтобы по умолчанию оно отображалось как /\<module name\>, установите для параметра **Application.PreferModuleName** значение **true**. В случае нескольких модулей, если имя модуля не указано, модуль по умолчанию будет обрабатывать данное действие. Чтобы изменить поведение таким образом, что имя модуля задавалось явно, установите для **Application.AllowDefaultModule** значение **false**.
437437
438438Следующие таблицы обобщают то, что произойдет на основе двух свойств:
439439
440440<thead>
441441
442442</thead>
443443<tbody>
444444
445445</tbody>
446446
447447##### Действие по умолчанию
448448
449449Помните предыдущую диаграмму, "Delegate request handling to actions" (Делегировать обработку запросов действиям) на самом деле не так просто, но если мы расширим эту диаграмму, изображение будет слишком большим, чтобы поместиться. Итак, вот схема этой части:
450450
451451![](/wiki-images/4/44/Fpwebtutorial_request_delegation_flow.png)
452452
453453Две важные вещи из потока: DefActionWhenUnknown и действие по умолчанию. Первое является свойством веб-модуля, а второе соответствует свойству Default действия. В последнем случае, если имеется более двух действий, для которых свойство Default установлено в значение `true`, порядок действий (то, как он отображается во всплывающем окне управления действиями) будет учитываться для определения того, какое действие является действием по умолчанию. Эти два свойства определяют, что должно делать приложение, если для данного запроса не найдено подходящего действия.
454454
455455Следующие таблицы обобщают то, что произойдет на основе двух свойств:
456456
457457<thead>
458458
459459</thead>
460460<tbody>
461461
462462</tbody>
463463
464464<thead>
465465
466466</thead>
467467<tbody>
468468
469469</tbody>
470470
471471В случае приведенного выше ответа об ошибке должна следовать трассировка стека с информацией о номере строки, если вы создаете свое приложение с параметром `-gl`. Позже мы увидим, как создать собственный обработчик для этого (трассировка стека не годится для производства). Но пока убедитесь, что вы понимаете концепцию веб-модуля и веб-действия, особенно поток запросов. Играйте, пока не решите, что готовы к следующему разделу.
472472
473473### Новый механизм
474474
475475Новый механизм чрезвычайно гибок и работает даже без модулей данных (старый механизм работает только с модулями данных).
476476
477477Выделенный модуль для этой маршрутизации предоставляется как **httproute** (добавьте его в пункт использования программы/модуля, где вы хотите зарегистрировать маршруты). Модуль содержит функцию **HTTPouter**, которая возвращает одноэлементный объект, отвечающий за управление маршрутом приложения, и имеет метод **RegisterRoute** для регистрации вашего маршрута.
478478
479479#### Синтаксис маршрута
480480
481481Первый параметр **HTTPouter.RegisterRoute** — это маршрут, который будет сопоставляться с входящим запросом. Это может быть как простое ***** выше, что означает 0 или более путей или просто любые пути, так и сложное, как **/api/v1/:resource/***, что означает, что заголовок **REQUEST_URI** должен начинаться с **/api/v1/**, за которым следует что-то еще, что будет привязано к переменной с именем resource и заканчиваться 0 или более путями. Он будет соответствовать:
482482
483483- /api/v1/products
484484- /api/v1/products/1
485485- /api/v1/products/1/clone
486486- /api/v1/products/something/else/that/is/really/long/and/silly
487487
488488но не:
489489
490490- /api/v1
491491- /excuse/me/api/v1/products
492492
493493В основном есть только 3 специальных символа:
494494
495495- *  обозначает 0 или более путей
496496
497497-
498498 param  обозначающий часть
499499
500500- /  обозначающий разделитель частей
501501
502502ваш маршрут будет состоять из этих 3 символов, а также всего остального, из чего состоит маршрут. 
503503
504504#### Регистрация маршрута
505505
5065062-й, 3-й или 4-й параметр (в зависимости от того, хотите ли вы обрабатывать конкретный метод HTTP и/или передавать ему дополнительные данные) **HTTPRouter.RegisterRoute** перегружен несколькими возможностями:
507507
508508- Процедура обратного вызова
509509
510510``` pascal
511511TRouteCallback = Procedure(ARequest: TRequest; AResponse);
512512```
513513
514514- Событие обратного вызова
515515
516516``` pascal
517517TRouteEvent = Procedure(ARequest: TRequest; AResponse) of object;
518518```
519519
520520- Объект, удовлетворяющий интерфейсу (CORBA)
521521
522522``` pascal
523523IRouteInterface = Interface ['{10115353-10BA-4B00-FDA5-80B69AC4CAD0}']
524524 Procedure HandleRequest(ARequest: TRequest; AResponse: TResponse);
525525end;
526526```
527527
528528- Объект, расширяющий абстрактный класс маршрутизатора
529529
530530``` pascal
531531TRouteObject = Class(TObject, IRouteInterface)
532532Public
533533 Procedure HandleRequest(ARequest: TRequest; AResponse: TResponse); virtual; abstract;
534534end;
535535
536536TRouteObjectClass = Class of TRouteObject;
537537```
538538
539539По умолчанию, если второй параметр не является **TRouteMethod**, будут совпадать все методы HTTP. Используйте один из **rmUnknown, rmAll, rmGet, rmPost, rmPut, rmDelete, rmOptions, rmHead, rmTrace**, чтобы соответствовать только определенному методу HTTP.
540540
541541Порядок регистрации имеет значение. Если есть два или более маршрута, соответствующих текущему запросу, его обработает ранее зарегистрированный.
542542
543543В этом новом механизме автономнаяй "Hello, World!"-программа может быть такой простой, как:
544544
545545``` pascal
546546uses
547547 fphttpapp, httpdefs, httproute;
548548procedure DoHello(ARequest:TRequest; AResponse : TResponse);
549549begin
550550 AResponse.Content:='<html><body><h1>Hello,World!</h1></body></html>'
551551end;
552552
553553begin
554554 HTTPRouter.RegisterRoute('*', @DoHello);
555555 Application.Port := 9000;
556556 Application.Initialize;
557557 Application.Run;
558558end.
559559```
560560
561561#### Пример веб-сервера
562562
563563Это пример простого кроссплатформенного многопоточного веб-сервера.
564564
565565``` pascal
566566program webserver;
567567
568568{$mode objfpc}{$H+}
569569
570570uses
571571 {$ifdef UNIX}
572572 cthreads, cmem,
573573 {$endif}
574574 fphttpapp, httpdefs, httproute;
575575
576576procedure route1(aReq: TRequest; aResp: TResponse);
577577begin
578578 aResp.content:='<html><body><h1>Route 1 The Default</h1></body></html>'
579579end;
580580
581581procedure route2(aReq: TRequest; aResp: TResponse);
582582begin
583583 aResp.content:='<html><body><h1>Route 2</h1></body></html>'
584584end;
585585
586586begin
587587 HTTPRouter.registerRoute('/', @route1, true);
588588 HTTPRouter.registerRoute('/route2', @route2);
589589 Application.port := 8080;
590590 Application.threaded := true;
591591 Application.initialize;
592592 Application.run;
593593end.
594594```
595595
596596## Использование шаблонов
597597
598598fpWeb имеет встроенную поддержку FPTemplate, универсального движка шаблонов Free Pascal. Его не обязательно использовать из контекста веб-приложения, но с интегрированной поддержкой все будет немного проще. По крайней мере, управление памятью можно игнорировать, так как об этом позаботится модуль.
599599
600600Есть два уровня, на которых можно использовать шаблоны: действие и модуль. Полная поддержка RAD до конца не реализована, поэтому в некоторых моментах вам нужно перейти к ручному кодированию.
601601
602602Существует два режима работы: непараметризованный и параметризованный. Активный режим управляется свойством **AllowTagParams**, которое должно быть достаточно очевидным, какое значение относится к какому режиму.
603603
604604Строка шаблона может быть передана из файла через свойство **FileName** или прямая строка через свойство **Template**. `Template.Template` - я знаю, это звучит странно :) Если оба заполнены, то FileName будет иметь приоритет.
605605
606606Два свойства: **StartDelimiter** и **EndDelimiter** определяют, как механизм должен распознавать тег шаблона. Например, если у вас есть:
607607
608608- StartDelimiter = '{+'
609609- EndDelimiter = '+}'
610610
611611затем строка '{+title+}' определяет тег шаблона с именем 'title'. Обратите внимание, что пробелы важны, поэтому '{+ title +}' определяет тег шаблона с именем ' title ', а не просто 'title'.
612612
613613Специальные для параметризованного режима дополнительные три свойства: **ParamStartDelimiter**, **ParamEndDelimiter** и **ParamValueSeparator** определяют, как движок должен распознавать параметр тега шаблона. Например, если у вас есть:
614614
615615- ParamStartDelimiter = '\[-'
616616- ParamEndDelimiter = '-\]'
617617- ParamValueSeparator = '='
618618
619619тогда строка '{+data \[-p=v-\]\[-a=b-\] +}' определяет тег шаблона с именем 'data' с параметром 'p' значения 'v' и параметром 'a' значения ' б'. Это можно использовать для передачи параметра уровня шаблона, такого как ожидаемый формат даты, заголовок-строка-нижний колонтитул для настраиваемого выходного представления, имя файла и т. д., как вы решите.
620620
621621Как следствие другого способа работы, основное событие, в котором работает шаблон, также отличается. Непараметризованный будет использовать **OnGetParam**, а параметризованный будет использовать **OnReplaceTag**. У них, конечно, другой интерфейс:
622622
623623``` pascal
624624Type
625625 // OnGetParam: только для поддержки простого тега шаблона (напр: {Name})
626626 TGetParamEvent = Procedure(
627627 Sender: TObject;
628628 Const ParamName: String;
629629 Out AValue: String
630630 ) Of Object;
631631 // OnReplaceTag: для тегов с поддержкой параметров
632632 TReplaceTagEvent = Procedure(
633633 Sender: TObject;
634634 Const TagString: String;
635635 TagParams: TStringList;
636636 Out ReplaceText: String
637637 ) Of Object;
638638```
639639
640640В **OnGetParam** вы проверяете **ParamName**, а затем соответственно назначаете **AValue**, т. е. если вы хотите, чтобы тег 'title' был заменен на 'My App', тогда заполните метод следующим образом:
641641
642642``` pascal
643643// используйте Trim(), если вы хотите, чтобы пробелы вокруг тега были незначительными
644644case Trim(ParamName) of
645645 'title': AValue := 'My App';
646646else
647647 AValue := 'UNKNOWN';
648648end;
649649```
650650
651651В **OnReplaceTag** вы проверяете **TagString** и, возможно, **TagParams**, а затем соответственно назначаете **ReplaceText**, т.е., если вы хотите, чтобы тег 'datetime' был заменен текущим временем с параметром 'datetimeformat', чтобы указать, как следует форматировать дату и время, заполните метод следующим образом:
652652
653653``` pascal
654654// используйте Trim(), если вы хотите, чтобы пробелы вокруг тега были незначительными
655655case Trim(TagString) of
656656 'datetime': AValue := FormatDateTime(TagParams.Values['datetimeformat'],Now);
657657else
658658 AValue := 'UNKNOWN';
659659end;
660660```
661661
662662### На уровне действий
663663
664664Создайте/выберите действие, затем перейдите в инспектор объектов. Вы увидите свойство подкомпонента с именем **Template**(Шаблон). Этот шаблон является обычным экземпляром **TFPTemplate**. Разверните его и заполните свойства, как описано выше. Теперь перейдите на вкладку Events(События), снова разверните **Template**, вы увидите два события. Заполните тот, который основан на вашем значении свойства **AllowTagParams**.
665665
666666> **Note:** Если ваш Lazarus не может автоматически заполнять событие, попробуйте ввести имя вручную в поле редактирования, а затем нажмите кнопку "...". Это ошибка в текущем Lazarus, которая может быть исправлена ​​в будущем.
667667
668668На этом уровне шаблон с содержимым не устанавливается автоматически в качестве обработчика запроса. Это может измениться в будущем, но давайте разберемся с его текущим состоянием. Заполните событие **OnRequest** действия, затем заполните его:
669669
670670``` pascal
671671with Actions.CurrentAction as TFPWebAction do
672672begin
673673 AResponse.Content := Template.GetContent;
674674end;
675675Handled := true;
676676```
677677
678678Приведение необходимо, поскольку **CurrentAction** имеет тип **TCustomWebAction** вместо **TFPWebAction**. Без него мы не сможем получить доступ к свойству **Template**.
679679
680680### На уровне модуля
681681
682682На уровне модуля вам в настоящее время придется делать это вручную, поскольку поддержка RAD не реализована. Связанным свойством является ModuleTemplate. Однако это не обычный экземпляр **TFPTemplate**, а специальный класс **TFPWebTemplate**, являющийся его потомком.
683683
684684Идея здесь состоит в том, чтобы модуль предоставлял макет, а действия предоставляли контент с возможностью предоставления дополнительных переменных. Поэтому рекомендуется оставить **AllowTagParams** как есть и назначить событие **OnGetParam** только **модуля**. НЕ назначайте **OnGetParam** для **ModuleTemplate**, так как он **никогда** не будет вызываться.
685685
686686Тег шаблона с именем 'content' будет автоматически заменен содержимым, созданным действием, все остальное заменяется либо из внутренних переменных шаблона, либо из **OnGetParam**.
687687
688688### Использование отдельного шаблона
689689
690690Несмотря на несколько неполную интеграцию, ничто не мешает вам использовать fpTemplate (или любые другие решения для шаблонов) вручную, вне встроенной поддержки. В некоторых случаях это может быть даже лучше, поскольку оно будет модульным.
691691
692692## Использольвание html Producer object
693693
694694Предполагая, что существует TWebAction с именем act_get_my_html, вы должны попросить написать в AResponse возврат внутреннего потока байтов: при ответе с помощью решения html Producer objects мы должны использовать методы писателя. Он управляет потоком памяти с целью ускорения. Здесь нет строкового типа, как в приведенном выше решении fpTemplate, которое использует обработку текста строки, как обычно в Php. Producer записывает в AResponse рекурсивную итерацию " foreach ", которая проходит по дереву полиморфных объектов HTML Dom, состоящему из иерархии классов `THtmlCustomElement = Class(TDOMElement)`(см. модуль `htmlelements.pp`). Итак, AResponse пишется параметром aWriter, со следующим вызовом:
695695
696696``` pascal
697697procedure TFPWebModule1.act_get_my_htmlRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
698698begin
699699 (* спрашиваем у HTMLEntityProducer1 содержимое своих html-элементов DOM: *)
700700 HTMLEntityProducer1.HandleRequest(ARequest, AResponse, Handled);
701701 Handled := True;
702702end;
703703```
704704
705705Повторим еще раз, что работа Producer заключается в преобразовании потока внутренней памяти в текст в объекте ответа: именно так был разработан этот шаблон, хотя существует метод рендеринга text-html с именем ProduceContent только для целей отладки. Вы можете переопределить этот метод, если вы пишете или отлаживаете компонент Producer.
706706
707707Вот пример с html-объектом Producer, позволяющий создать веб-приложение «способом RAD», то есть с помощью перетаскивания компонента HTMLEntityProducer из палитры:
708708
709709``` pascal
710710procedure TFPWebModule1.HTMLEntityProducer1WriteEntity(Sender: THTMLContentProducer; aWriter: THTMLWriter);
711711begin
712712
713713 aWriter.startHeader;
714714 aWriter.Meta('','Content-Type','text/html; charset=UTF-8');
715715 aWriter.title('My web page');
716716 aWriter.link('stylesheet','stylesheet.css','text/css','screen');
717717 aWriter.EndHeader;
718718
719719 aWriter.Startbody;
720720 aWriter.Startparagraph;
721721 aWriter.heading2('Hello, world from inside §1:');
722722 aWriter.Text('Here is text written inside the current paragraph.');
723723 aWriter.Endparagraph;
724724 aWriter.paragraph('This is another text written inside a self "started and ended" paragraph.');
725725 aWriter.Startparagraph;
726726 aWriter.heading2('Hello, world from inside §2:');
727727 aWriter.Text('Here is the final text.');
728728 aWriter.Image.src := 'logo.png';
729729 AnotherProducer.WriteContent(aWriter);
730730 aWriter.Endparagraph;
731731 aWriter.Endbody;
732732
733733end;
734734```
735735
736736Свойство TWebAction.ContentProducer позволяет связать THTTPContentProducer с его веб-действием, предоставляемым через URI в сети.
737737
738738\[ToDo: нет официальной документации о компонентах «способ RAD» fpWeb (html producer, html provider, html adapter, html formatter и т.д.)\]
739739
740740## Советы и хитрости
741741
742742### Возврат другого кода ответа HTTP
743743
744744По умолчанию fpWeb возвращает HTTP 200 OK, чтобы указать на успешную обработку запроса. Это, конечно, не всегда так, поскольку пользовательский ввод может быть не таким, как мы ожидали. Для этого установите для параметра **AResponse.Code** в обработчике запроса код, который вы хотите вернуть.
745745
746746### Перенаправление запроса на другой URL
747747
748748Обычная практика после успешного входа в систему — перенаправление пользователя на страницу его учетной записи. Это можно сделать, вызвав **AResponse.SendRedirect** в обработчике запросов, указав URL-адрес для перенаправления запроса.
749749
750750### Обслуживание статических файлов (встроенный веб-сервер)
751751
752752Помните диалоговое окно в разделе [[fpWeb Tutorial/ru|Hello, World!]] после выбора приложения HTTP-сервера? Если вы отметите "Register location to serve files from"(Зарегистрировать местоположение для обслуживания файлов), вы можете заполнить "Location"(Расположение) (сегмент URI, не должен содержать слэшей) и "Directory"(Каталог) (физический каталог на вашем компьютере, должен существовать во время выполнения), то мастер просто добавит:
753753
754754``` pascal
755755RegisterFileLocation('<Location>','<Directory>');
756756```
757757
758758в начало вашего *.lpr-файла и добавит модуль **fpwebfile** в раздел uses. Вы можете сделать это вручную в любое время, а также зарегистрироваться несколько раз для разных местоположений/каталогов. После этого вы можете запросить **/\<Location\>/\<любое имя файла в Directory\>**, и оно будет предоставлено автоматически. Обратите внимание, что *mimetype* файла определяется **fpmimetypes**. Вызовите **MimeTypes.LoadFromFile** с вашим файлом *mime.types*, чтобы указать правильный тип *mimetype* на основе его расширения. В противном случае файл всегда будет обслуживаться как **application/octet-stream**, что означает, что браузер загрузит его, а не интерпретирует (особенно важно для файлов JavaScript и CSS).
759759
760760Вы можете получить полные mime.types [здесь](http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?&view=co).
761761
762762В Lazarus 2.0.6 или новее вы должны добавить в начало своей программы полный путь к файлу mime.types.
763763
764764``` pascal
765765begin
766766 MimeTypesFile := Application.Location + 'mime.txt';
767767```
768768
769769Примите во внимание, что путь по умолчанию, идущий с проектом, это - *lib\\\$(TargetCPU)-\$(TargetOS)*
770770
771771Например: *httpproject\lib\i386-win32\mime.txt*
772772
773773### Централизованное управление конфигурацией и модулями
774774
775775По умолчанию программный файл (.lpr) содержит модуль протокола. Это ограничивает возможность использования объекта **Application** из других контекстов, таких как веб-модули. К счастью, нетрудно провести рефакторинг, чтобы получить то, что мы хотим. Мы удаляем вызовы **RegisterHTTPModule** из веб-модулей и не будем использовать .lpr для очистки основного блока с единственным идентификатором модуля в разделе uses, назовем его: брокер. Содержимое модуля:
776776
777777``` pascal
778778unit Brokers;
779779
780780{$mode objfpc}{$H+}
781781
782782interface
783783
784784{ $define cgi}
785785{ $define fcgi}
786786{$define httpapp}
787787
788788uses
789789 CustWeb;
790790
791791function GetApp: TCustomWebApplication; inline;
792792
793793implementation
794794
795795uses
796796 {$ifdef cgi}fpcgi{$endif}
797797 {$ifdef fcgi}fpfcgi{$endif}
798798 {$ifdef httpapp}fphttpapp{$endif}
799799 ,webmodule1
800800 ,webmodule2
801801 ;
802802
803803function GetApp: TCustomWebApplication;
804804begin
805805 Result := Application;
806806end;
807807
808808initialization
809809 RegisterHTTPModule('wm1', TWebModule1);
810810 RegisterHTTPModule('wm2', TWebModule2);
811811 {$ifndef cgi}
812812 Application.Port := 2015;
813813 {$endif}
814814 Application.Initialize;
815815 Application.Run;
816816end.
817817```
818818
819819Таким образом, мы можем управлять регистрацией веб-модуля, а также предоставлять API для получения объекта **Application** (представленного как **TCustomWebApplication**), в то же время легко переключаясь между реализациями протокола в одном месте.
820820
821821### Элегантное завершение (FastCGI / встроенный веб-сервер)
822822
823823Вместо нажатия `Ctrl`+`C` в вашем приложении есть способ корректно завершить работу вашего приложения, выполнив необходимую очистку, вызвав **Application.Terminate**. Возможно, вам придется использовать предыдущий трюк, чтобы легко получить доступ к объекту **Application**. Распространенной реализацией является предоставление определенного защищенного паролем модуля/действия, которое вызывает метод Terminate. Впрочем, вы можете выбрать любой способ.
824824
825825### Пользовательский обработчик исключений
826826
827827\[отредактировано 1 мая 2020г =\> переход от указателя метода к простой процедуре..\]
828828
829829Чтобы переопределить обработчик исключений по умолчанию, который выдает трассировку стека всякий раз, когда возникает исключение (например, по HTTP 404 или 500), и, следовательно, не подходит для производства, вы должны задать **Application.OnShowRequestException**.
830830
831831Это метод, поэтому вам нужно будет предоставить свою процедуру, реализующую метод, и присвоить ее с помощью объекта, т.е.: если у вас есть **MyExceptionHandler** как объект **TMyExceptionHandler**, который имеет метод **MyShowRequestException**, вы можете назначить его посредством:
832832
833833``` pascal
834834Application.OnShowRequestException := @MyExceptionHandler.MyShowRequestException;
835835```
836836
837837не забудьте **.Create()** **MyExceptionHandler** ПЕРЕД назначением выше, иначе вы получите **EAccessViolation**!
838838
839839Вы должны предоставить свою глобальную процедуру, которая реализует ваш собственный обработчик исключений (в рабочей среде рекомендуется заменить стек вызовов кодом состояния HTTP и его объяснением). Затем вы можете переопределить обработчик исключений по умолчанию, назначив его следующим образом:
840840
841841``` pascal
842842Application.OnShowRequestException := @MyShowRequestException;
843843```
844844
845845### Чисто ручное кодирование (дизайнер форм не требуется)
846846
847847Нет необходимости использовать конструктор форм Lazarus для написания fpWeb-приложения. Вы можете использовать технику чисто ручного кодирования, чтобы написать его. Секрет кроется в третьем параметре **RegisterHTTPModule**: **SkipStreaming**. Если для этого параметра установлено значение true, fpWeb не будет искать ресурс .lfm. Поэтому все должно обрабатываться вручную: настройки свойств, обработчики событий, регистрация действий и т.д.
848848
849849Обратите внимание, что логично делать то, что обычно делается через инспектор объектов в переопределенном конструкторе. Внутри него вызовите унаследованный конструктор, передав в качестве параметров AOwner и CreateMode. После этого вы можете установить свойства, назначить обработчики событий и т.д. Пример:
850850
851851``` pascal
852852type
853853 THelloWorldModule = class(TFPWebModule)
854854 constructor CreateNew(AOwner: TComponent; CreateMode: Integer); override;
855855 procedure Request(Sender: TObject; ARequest: TRequest;
856856 AResponse: TResponse; var Handled: Boolean);
857857 end;
858858
859859constructor THelloWorldModule.CreateNew(AOwner: TComponent; CreateMode: Integer);
860860begin
861861 inherited CreateNew(AOwner,CreateMode);
862862 OnRequest := @Request;
863863end;
864864
865865procedure THelloWorldModule.Request(Sender: TObject; ARequest: TRequest;
866866 AResponse: TResponse; var Handled: Boolean);
867867begin
868868 AResponse.Content := 'Hello, World!';
869869 Handled := true;
870870end;
871871```
872872
873873## См.также
874874
875875- [Lazarus для Интернета (учебник)](https://www.freepascal.org/~michael/articles/#web1)
876876- [Lazarus для Интернета: сеансы и шаблоны (учебник))](https://www.freepascal.org/~michael/articles/#web2) В этой статье описывается класс TSQLDBSession, который управляет подключением SQL, которое наследуется от веб-сеанса (для управления историями переменных веб-сеанса в рамках сеанса подключения к базе данных, имеющей возможности блокировки строк).
877877- примеры - чтение текстовых файлов - в вашем каталоге ...\fpc\x.x.x\source\packages\fcl-web\examples.\
878878
879-<img src="cgi-listrecords.png" title="Копия скриншота одного из примеров" width="50" />
879+![](/wiki-images/c/c0/Cgi-listrecords.png)
880880
881881- XML или JSON на ExtJS: если вы хотите использовать более или менее сложный движок, который очень «продвинут» в отношении аспекта рендеринга объектов Javascript на стороне браузера, в дополнение к их функциональным возможностям (например, ExtJS для отображения сетка с поддержкой базы данных, динамическая графика и т. д.), для такого решения очень часто требуется файл XML или JSON. Существуют классы Adapter и Formatter (например, классы TExtJSXMLWebdataInputAdaptor, TExtJSJSONDataFormatter).
882882
883883В целом, классы Adapter адаптируют узлы входящего jSon или XML к отображению своих полей базы данных TField. А классы Formatter сопоставляют каждую запись TField с ее узлом в формате jSon или XML перед отправкой в ​​браузер.\
884884* [Форматы веб-данных в Lazarus/FPC](https://www.freepascal.org/~michael/articles/#webdata)
885885
886886- [Практическое построение GUI в ExtJS](https://www.freepascal.org/~michael/articles/#extjs1)
887887- [Смешивание веб-приложений ExtJS и Pascal: ExtPascal](https://www.freepascal.org/~michael/articles/#extpascal)
888888
889889---
890890*From the Free Pascal wiki, page "fpWeb Tutorial/ru", as it stood on 2022-06-07 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history