Sign in Join
Wiki copytemporary

fpWeb Tutorial/ru: what changed

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

OldNewLine
1-` Первоначально основано на учебнике fcl-web (в формате PDF) пользователя форума `[`Leledumbo`](https://forum.lazarus.freepascal.org/index.php?action=profile;u=7523)`.`
1+```text
2+ Первоначально основано на учебнике fcl-web (в формате PDF) пользователя форума Leledumbo.
3+```
24
35## Введение
46
57fpWeb - это фреймворк веб-приложений, поставляемый FPC в своем дистрибутиве по умолчанию как часть пакета fcl-web. Сам фреймворк построен на основе функций fcl-web. Фреймворк построен с мыслью о RAD, чтобы эффективно использовать компонентность при создании динамического контента. Предоставляется пакет Lazarus, который может использовать фреймворк методом перетаскивания для управления сеансами и создания контента. В этом руководстве мы попытаемся охватить основные функции fpWeb, чтобы с его помощью можно было создать обычное веб-приложение. Обратите внимание, что это руководство не пытается научить вас HTTP-протоколу, HTML, CSS, JavaScript или манипуляциям с базой данных, поскольку владение протоколом и клиентскими языками должно быть предварительным условием работы для каждого программиста веб-приложений, а манипуляции с базой данных не отличаются от десктопной реализации.
68
79## Архитектура (ПОЖАЛУЙСТА, прочтите)
810
911Перед тем, как начать, необходимо знать архитектуру и поток приложения, чтобы избежать путаницы, когда определенные вещи не работают или работают неожиданно. Поэтому, пожалуйста, потратьте немного времени на чтение этого раздела.
1012
1113### Приложение
1214
1315Под приложением здесь понимается протокол, который будет реализовывать ваше приложение. fpWeb с радостью переключится с CGI, FCGI, с модуля Apache на встроенный сервер и т.д., если fcl-web реализует еще что- нибудь в будущем. Каждое приложение реализовано в своем собственном модуле, поэтому для переключения с одного приложения на другое, за исключением модуля Apache, нужно просто изменить соответствующий идентификатор в разделе uses. В настоящее время (по состоянию на 3.0.0 / 3.1.1) это:
1416
1517- fpCGI -\> CGI
1618- fpFCGI -\> FastCGI
1719- fpApache (также требуется httpd) -\> модуль Apache
1820- fpHttpApp -\> встроенный сервер
1921- microhttpapp -\> встроенный сервер с использованием библиотеки GNU libmicrohttp.
2022- fphttpsys -\> поддержка системой Windows протокола HTTP.
2123
2224В этом руководстве мы будем использовать встроенный сервер для простоты, потому что вам не придется иметь дело с настройкой виртуального сервера и путаницей со сложным файлом конфигурации и управлением службами. Ваше приложение будет единым переносным бинарным веб-приложением! Другая причина может заключаться в том, что существует больше, чем одно приложение веб-сервера, и каждое имеет свой способ настройки. Было бы излишним пытаться охватить их всех, пока их документация уже выполняет свою работу. Модуль Apache реализован как (динамическая) библиотека, тогда как другие протоколы являются обычным приложением. Каждое приложение может иметь определенные свойства (например, порт), доступные и значимые только для этого приложения. Вот почему, если вы посмотрите на примеры fcl-web, пары .lpi / .lpr для каждого протокола помещаются в свои собственные каталоги, только веб-модули являются общими.
2325
2426### Веб-модули
2527
2628![](/wiki-images/5/50/FpWeb-overview.png)
2729
2830Приложения fpWeb состоят из веб-модулей, которые фактически создают контент. Веб-модуль может содержать веб-действия, которые могут еще больше разделить функциональность. Например, веб-модуль аутентификации может иметь веб-действия входа и выхода. Хотя модуль about web может вообще не нуждаться в действии и обслуживает только один контент. Веб-модуль интегрирован с fpTemplate, который можно использовать для создания динамического контента из файла шаблона. Это примерно похоже на то, что делает PHP, только разрыв между логикой и представлением скорее вынужденный, чем предполагаемый. Некоторые говорят, что fpTemplate реализует пассивное представление, в то время как PHP по умолчанию реализует шаблон проектирования активного представления.
2931
3032## Установка
3133
3234Пакет fpWeb для Lazarus по умолчанию не установлен (но идет в стандартной поставке). Чтобы включить fpWeb:
3335
34361. Откройте Lazarus и выберите **Package-\>Install/Uninstall Package**
35372. В списке Available (Доступно) для установки найдите **weblaz** и нажмите **Install selection** (Установить выбранное). Нажмите **Save and rebuild IDE**(Сохранить и пересобрать IDE) и подтвердите, нажав **Continue**(Продолжить).
36383. Позвольте IDE пересобраться и перезапуститься. Если все пойдет хорошо, у вас должна появиться вкладка fpWeb на палитре компонентов, как показано ниже:
3739
3840
3941
4042![](/wiki-images/5/55/Fpwebtutorial_inst.png)
4143
4244## Специализированные модули
4345
4446Класс*TFPWebModule* (используемый ниже) - это простой пример модуля fpWEB, который можно использовать для всех видов HTTP-запросов.
4547
4648Однако fpWEB поставляется с некоторыми специализированными модулями, которые имеют дополнительную поддержку для специализированных задач:
4749
4850- Класс **TSimpleFileModule** в модуле *fpwebfile.pp* можно использовать для отправки файлов. Вы указываете ему каталог, и он делает все остальное.
4951- Класс **TFPHTMLModule** в модуле *fphtml.pp* можно использовать для создания HTML.
5052- Класс **TProxyWebModule** в модуле *fpwebproxy.pp* - это готовый прокси для пересылки.
5153- Класс **TFPWebProviderDataModule** в модуле *fpwebdata.pp* обслуживает данные в формате JSON, которые могут использоваться хранилищами ExtJS.
5254- Класс **TSQLDBRestModule** в модуле *sqldbrestmodule.pp* реализует полноценный сервер REST, поддерживаемый SQLDB. См. дополнительную информацию в [[SQLDBRestBridge]].
5355- Класс **TJSONRPCModule** в модуле *webjsonrpc.pp* реализует службу JSON-RPC.
5456- Класс **TExtDirectModule** в модуле *fpextdirect.pp* реализует вариант Ext.Direct службы JSON-RPC.
5557
5658## Hello, World!
5759
5860Создадим простое веб-приложение. Как обычно преподают при изучении программирования: "Hello, World!" будет нашим первым приложением.\
5961
60621\. Откройте Lazarus и выберите **Project-\>New Project**, затем выберите **HTTP server Application**
6163
6264
6365![Создать новое приложение HTTP-сервера](/wiki-images/5/57/Fpwebtutorial_create_app.png)\
6466
65672\. Появится другое диалоговое окно для обслуживания статических файлов, выбора порта и многопоточности. Просто используйте порт по умолчанию 8080.
6668
6769
6870``` pascal
6971You may skip the static files serving (go to tips and tricks section if you want to know it more).
7072```
7173
7274(*Вы можете пропустить обслуживание статических файлов (перейдите в раздел советов и рекомендаций, если хотите узнать больше)*)
7375
7476
7577![](/wiki-images/e/e6/Fpwebtutorial_port_selection.PNG)
7678
7779:**ВАЖНО!:**
7880
7981
8082Если вы решите использовать потоки на *nix, не забудьте добавить cthreads в качестве первого модуля в разделе uses .lpr, в противном случае будет создан RTE 232. При запуске с консоли должно отображаться сообщение:
8183
8284``` pascal
8385This binary has no thread support compiled in. Recompile the application with a thread-driver in the program uses clause before other units using thread.
8486```
8587
8688(*В этом двоичном файле не скомпилирована поддержка потоков. Перекомпилируйте приложение с драйвером потока в программе using предложение перед другими модулями, использующими поток*)\
8789
88903\. С 14 января 2017г. (или FPC 3.0.4) вам может потребоваться открыть .lpr и добавить следующую строку в основной текст, если ее еще там нет:
8991
9092
9193``` pascal
9294Application.LegacyRouting := true;
9395```
9496
9597причина будет объяснена в главе [[fpWeb Tutorial/ru|Маршрутизация]].
9698
97994\. Что бы вы ни выбрали, нажмите "OK", и вы будете представлены в приложении fpWeb с одним модулем по умолчанию.\
981005\. Переместите фокус на модуль и перейдите в **Object Inspector**. Не стесняйтесь переименовывать модуль, если хотите.\
991016\. Выберите вкладку **Events** и нажмите кнопку справа от второго столбца в строке**OnRequest**, чтобы создать обработчик событий.
100102
101103
102104![Создание обработчика OnRequest веб-модуля в object inspector](/wiki-images/8/8d/Fpwebtutorial_on_request.png)\
103105
104106
105107Вы будете перенаправлены в редактор исходного кода со следующим кодом:
106108
107109:
108110
109111``` pascal
110112procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
111113AResponse: TResponse; var Handled: Boolean);
112114begin
113115 |
114116end;
115117```
116118
117119
118120Заполните событие:
119121
120122:
121123
122124``` pascal
123125procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
124126AResponse: TResponse; var Handled: Boolean);
125127begin
126128 AResponse.Content := 'Hello, World!';
127129 Handled := true;
128130end;
129131```
130132
1311337\. Теперь запустите приложение (или нажмите F9).\
1321348\. Откройте браузер и введите:
133135
134136
135137<http://localhost:8080/>\
136138
1371399\. Вы должны увидеть отображающееся "Hello, World!".\
138140
139141Если это не так, проверьте ниже:
140142
141143- Фреймворк выполняет много операций по обработке исключений, и отладчик IDE может их поймать и прервать работу вашего приложения. Можно добавить большинство исключений в список игнорирования, чтобы вы могли больше сосредоточиться на потоке приложения. Продолжайте пропускать и продолжайте, пока не перестанет появляться диалоговое окно и браузер не покажет результат.
142144
143145- **Handled := true** - это способ, которым мы сообщаем фреймворку, что запрос был обработан. Если вы не установите его (или установите для него значение **false**), вместо этого будет отображаться страница с ошибкой. На данный момент это не влияет на поток запросов, но будет позже. Так что держите это так, пока не придет время, чтобы использовать его с пользой.
144146
145147- другой трек: тест без брандмауэра, загруженного в ОЗУ (как приложение, как сервис или демон, или как оба).
146148
147149## Чтение данных GET и POST
148150
149151Динамический контент, скорее всего, будет запускаться из пользовательского ввода, либо через формы, предоставляя значения в URL-адресе, и т. Д. Эти данные отправляются вместе с запросом, который представлен в методе как **ARequest** параметр типа **TRequest**.
150152
151153### Чтение GET
152154
153155Данные GET предоставляются как **ARequest.QueryFields**, который является потомком **TStrings**. Короче говоря, все, что вы обычно делаете с TStrings, применимо здесь, например, доступ к данным в стиле карты через свойство **Values**.
154156
155157Повторно используя приведенный выше код, замените тело метода на:
156158
157159``` pascal
158160procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
159161 AResponse: TResponse; var Handled: Boolean);
160162var
161163 LName: String;
162164begin
163165 LName := ARequest.QueryFields.Values['Name'];
164166 if LName = EmptyStr then
165167 with AResponse.Contents do
166168 begin
167169 Add('<form action="' + ARequest.URI + '" method="GET"');
168170 Add('<label for="name">Please tell me your name:</label>');
169171 Add('<input type="text" name="name" id="name" />');
170172 Add('<input type="submit" value="Send" />');
171173 Add('</form>');
172174 end
173175 else
174176 AResponse.Content := 'Hello, ' + LName + '!';
175177 Handled := true;
176178end;
177179```
178180
179181**ARequest.URI** - это просто ссылка на текущий URI, поэтому даже когда вы меняете зарегистрированный модуль или имя действия, этот код остается прежним.
180182
181183Обратите внимание, что, как и в Паскале, обращение к данным осуществляется без учета регистра.
182184
183185Теперь вы можете попробовать запросить /, который отобразит
184186
185-` Please tell me your name`
187+```text
188+ Please tell me your name
189+```
186190
187191и /?name=\<напишите здесь что угодно, например: Bob\>, который отобразит
188192
189-` Hello, Bob!`
193+```text
194+ Hello, Bob!
195+```
190196
191197### Чтение POST
192198
193199POST на самом деле не сильно отличается от GET, отличается только тем, к какому свойству получить доступ. Если доступ к GET осуществляется через **ARequest.QueryFields**, доступ к POST осуществляется через **ARequest.ContentFields**. Стиль POST предыдущего кода:
194200
195201``` pascal
196202procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
197203 AResponse: TResponse; var Handled: Boolean);
198204var
199205 LName: String;
200206begin
201207 LName := ARequest.ContentFields.Values['Name'];
202208 if LName = EmptyStr then
203209 with AResponse.Contents do
204210 begin
205211 Add('<form action="' + ARequest.URI + '" method="POST"');
206212 Add('<label for="name">Please tell me your name:</label>');
207213 Add('<input type="text" name="name" id="name" />');
208214 Add('<input type="submit" value="Send" />');
209215 Add('</form>');
210216 end
211217 else
212218 AResponse.Content := 'Hello, ' + LName + '!';
213219 Handled := true;
214220end;
215221```
216222
217223### Чтение загружаемых файлов
218224
219225Единственное исключение - чтение полей **multipart/form-data**, то есть файлов. Оно доступно в **ARequest.Files** как экземпляр **TUploadedFiles**, который является потомком **TCollection**. Ниже приведен общедоступный интерфейс TUploadedFiles, который вы можете использовать для доступа к файлам:
220226
221227``` pascal
222228TUploadedFiles = Class(TCollection)
223229...
224230public
225231 Function First: TUploadedFile;
226232 Function Last: TUploadedFile;
227233 Function IndexOfFile(AName: String) : Integer;
228234 Function FileByName(AName: String) : TUploadedFile;
229235 Function FindFile(AName: String) : TUploadedFile;
230236 Property Files[Index: Integer] : TUploadedFile read GetFile Write SetFile; default;
231237end;
232238```
233239
234240Каждый **TUploadedFile** сам по себе имеет несколько свойств:
235241
236242``` pascal
237243TUploadedFile = Class(TCollectionItem)
238244...
239245Public
240246 Destructor Destroy; override;
241247 Property FieldName: String Read FFieldName Write FFieldName;
242248 Property FileName: String Read FFileName Write FFileName;
243249 Property Stream: TStream Read GetStream;
244250 Property Size: Int64 Read FSize Write FSize;
245251 Property ContentType: String Read FContentType Write FContentType;
246252 Property Disposition: String Read FDisposition Write FDisposition;
247253 Property LocalFileName: String Read FLocalFileName Write FLocalFileName;
248254 Property Description: String Read FDescription Write FDescription;
249255end;
250256```
251257
252258Они должны быть достаточно информативными, за исключением **FileName** и **LocalFileName**. **FileName** - это **name** исходного файла, загружаемого с клиента, **LocalFileName** - это путь к файлу на сервере, где файл временно хранится. Обратите внимание на разницу, выделенную жирным шрифтом выше.
253259
254260Опять же, повторное использование того же обработчика запросов:
255261
256262``` pascal
257263procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
258264 AResponse: TResponse; var Handled: Boolean);
259265var
260266 n: Integer;
261267 f: TUploadedFile;
262268 i: Integer;
263269begin
264270 n := ARequest.Files.Count;
265271 if n = 0 then
266272 with AResponse.Contents do
267273 begin
268274 Add('<form id="form" action="' + ARequest.URI + '" method="POST" enctype="multipart/form-data">');
269275 Add('<label for="name">Drag n drop or click to add file:</label>');
270276 Add('<input type="file" name="input" />');
271277 Add('<input type="submit" value="Send" />');
272278 Add('</form>');
273279 end
274280 else
275281 begin
276282 f := ARequest.Files[0];
277283 AResponse.Contents.LoadFromStream(f.Stream);
278284 end;
279285 Handled := true;
280286end;
281287```
282288
283289перетащите и бросьте файл (желательно текстовый, так как он будет отображаться как текст) в поле входного файла (или нажмите соответствующую кнопку), затем нажмите кнопку *Send*' (Отправить). Должно отобразиться содержимое файла.
284290
285291## Cookies
286292
287293### Отправка
288294
289295![Концепция «cookie», изобретенная Netscape в 1994 году, позволяет HTTP-серверу идентифицировать всех своих клиентов.](/wiki-images/5/5c/Cookie_session_Object_Pascal.png)\
290296Файлы cookie - небольшой фрагмент данных, отправленный веб-сервером и хранимый на компьютере пользователя. Веб-клиент (обычно веб-браузер) всякий раз при попытке открыть страницу соответствующего сайта пересылает этот фрагмент данных веб-серверу в составе HTTP-запроса ([Wiki](https://ru.wikipedia.org/wiki/Cookie)). **AResponse.Cookies** содержит список отправляемых файлов cookie. Это потомок **TCollection**, соответственно содержащийся **TCookie** является потомком **TCollectionItem**. Следовательно, вы можете использовать TCollection как способ управления элементами для управления ими.
291297
292298Вот пример:
293299
294300``` pascal
295301procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
296302 AResponse: TResponse; var Handled: Boolean);
297303var
298304 C: TCookie;
299305begin
300306 C := AResponse.Cookies.Add;
301307 C.Name := 'mycookie';
302308 C.Value := 'somevalue';
303309 Handled := true;
304310end;
305311```
306312
307313Вы не увидите никаких результатов в своем браузере. Но если вы используете какие-то инструменты разработчика (в Chrome есть встроенный) можно увидеть заголовок ответа:
308314
309315![](/wiki-images/6/69/Fpwebtutorial_cookie_set.png)
310316
311317Обратите внимание, что файл cookie имеет атрибуты, поэтому вы можете установить не только имя и значение. Просмотрите интерфейс TCookie, чтобы узнать, какие свойства поддерживаются.
312318
313319### Получение
314320
315321Как только вы укажете выше заголовок **Set-Cookie**, последующий запрос на ваш сайт будет содержать дополнительный заголовок, содержащий значение, которое вы просили установить ранее:
316322
317323![](/wiki-images/9/93/Fpwebtutorial_cookie_get.png)
318324
319325К счастью, способ чтения не отличается от данных GET и POST. Связанное свойство - **ARequest.CookieFields**. Чтобы прочитать ранее отправленный файл cookie:
320326
321327``` pascal
322328procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
323329 AResponse: TResponse; var Handled: Boolean);
324330begin
325331 AResponse.Contents.Add('<p>Cookie get: ' + ARequest.CookieFields.Values['mycookie'] + '</p>');
326332 Handled := true;
327333end;
328334```
329335
330336## Сессии
331337
332338TFPWebModule является потомком TSessionHTTPModule, поэтому имеет возможность управления сессией. Сессия основана на модулях, поэтому каждый модуль может выбирать, использовать или не использовать управление сессией.
333339
334340Сессия реализована абстрактно. По умолчанию реализация не предусмотрена. Один пример реализации с использованием файлов .ini приведен в модуле **iniwebsession**. Вы должны иметь этот модуль в своем проекте или реализовать его, чтобы управление сессией работало. Если вы решите реализовать один из них, в основном вам необходимо расширить и реализовать абстрактные методы в классах **TCustomSession** и *'TSessionFactory'*.
335341
336342### Активация
337343
338344Чтобы активировать управление сессией, установите для свойства **CreateSession** в значение true. Сессия будет запущена до обработки запроса. В случае новой сессией будет вызвано **OnNewSession**. Здесь инициализируйте переменные сессии.
339345
340346### Управление переменными сессии
341347
342348Переменные сессии представлены как **Session.Variables** (примечание: объект сессии является эквивалентом массива \$ _SESSION, используемого в Php). Это строка для сопоставления строк, подобная структуре, поэтому вы можете читать / писать так:
343349
344350``` pascal
345351Session.Variables['myvar'] := myvar; // записываем
346352...
347353myvar := Session.Variables['myvar']; // читаем
348354```
349355
350356Задание переменной пустой строки **НЕ** удаляет ее. Если вы действительно хотите удалить переменную, вместо этого вызовите **Session.RemoveVariable**.
351357
352358### Завершение
353359
354360Вызывайте **Session.Terminate** всякий раз, когда вы хотите завершить сессию (например, выход пользователя из системы). Сессия также автоматически истечет, если следующий запрос поступит после **Session.TimeOutMinutes** с момента последнего запроса. Когда сессия завершается, будет вызван **OnSessionExpired**. Сделайте там все, что вам нужно.
355361
356362## Маршрутизация
357363
358364Начиная с FPC 3.0.4, был реализован новый механизм маршрутизации. Вместо сохранения обратной совместимости решено, что по умолчанию будет использоваться новая маршрутизация. Таким образом, любой старый код (или новый код в зависимости от старой маршрутизации) необходимо перенести, добавив:
359365
360366``` pascal
361367Application.LegacyRouting := true;
362368```
363369
364370в *.lpr файле.
365371
366372### Старый способ
367373
368374#### Использование нескольких модулей
369375
370376В вашем приложении может быть несколько модулей. Щелкните меню "File", затем щелкните "New...". В появившемся диалоговом окне выберите "Web Module" в дереве выбора модулей.
371377
372378![](/wiki-images/e/e5/Fpwebtutorial_new_module.png)
373379
374380затем нажмите OK.
375381
376382Поскольку в вашем приложении существует несколько модулей, вы больше не можете делать запрос (request) только с использованием `/`. Платформа не сможет волшебным образом выбрать, какой модуль должен обслуживать ответ (response), поэтому есть два способа указать, какой модуль вы хотите вызвать:
377383
378384- /\<module name\>
379385- /?module=\<module name\>
380386
381387Во втором формате вы можете изменить "module" (который является значением по умолчанию) на любой допустимый ключ строки запроса, изменив **Application.ModuleVariable**.
382388
383389#### С использованием Actions
384390
385391До сих пор мы использовали только веб-модули с обработчиком единого запроса. Это не сильно масштабируется, поскольку ваше веб-приложение становится все более и более сложным. Более того, некоторые функции могут иметь общие свойства и могут быть лучше логически сгруппированы, например:
386392
387393- Account module
388394 - Login action
389395 - Logout action
390396 - Register action
391397- Product module
392398 - Create action
393399 - Update action
394400 - Delete action
395401 - Details action
396402
397403##### Поток обработки запросов (request)
398404
399405Прежде, чем использовать действие (action), важно знать, как обрабатывается запрос fpWeb. В противном случае ваше действие (action) может оказаться бесполезным, потому что ваш модуль данных всегда обрабатывает запрос. Как такое могло случится? Возвращаясь немного назад, вспомните **Handled := true**, которое мы всегда делали раньше? Теперь в игру вступает параметр **Handled**.
400406
401407**Каждый** запрос сначала будет проходить через модуль **OnRequest**, независимо от запрошенного действия. Только если для этого **не** установлено значение Handled, выполняется OnRequest веб-действия.
402408
403409В общем, поток запросов:
404410
405411![](/wiki-images/e/ea/Fpwebtutorial_request_flow.png)
406412
407413Обратите внимание на окошко "Our Concern" (наша озабоченность), это то, на что мы собираемся обратить наше внимание.
408414
409415##### Добавление действий в веб-модули
410416
411417Чтобы добавить действие, выберите веб-модуль и перейдите в инспектор объектов. На вкладке свойств выберите "Actions" и нажмите кнопку во втором столбце.
412418
413419![](/wiki-images/9/95/Fpwebtutorial_manage_actions.png)
414420
415421Появится всплывающее окно, в котором вы можете добавлять, удалять и изменять порядок действий.
416422
417423![](/wiki-images/d/db/Fpwebtutorial_edit_actions.png)
418424
419425Нажмите "Add", в списке появится новое действие. Выберите его, затем перейдите в инспектор объектов. В настоящее время он будет показывать свойства и события этого вновь созданного действия. Переименуйте свойство Name (это будет имя, которое вы напишете в URL-адрес, поэтому дайте ему короткое, простое, но описательное имя) как вы желаете, я выберу "Hello". Двигаемся дальше на вкладку событий, сделайте то же самое, что и OnRequest для модуля, нажмите кнопку справа от строки OnRequest, чтобы создать обработчик запросов.
420426
421427![](/wiki-images/2/2e/Fpwebtutorial_action_onrequest.png)
422428
423429Вы будете представлены в том же интерфейсе OnRequest, но этот обрабатывает веб-действие вместо веб-модуля. Все, что вы можете сделать в OnRequest веб-модуля, можно сделать и здесь. Скопируйте тело метода из 'Hello, World!' раздела.
424430
425431Не забудьте удалить **Handled := true** из тела **OnRequest** предыдущего веб-модуля (или полностью удалить событие), чтобы действие позаботилось об обработке запроса.
426432
427433Запустите свой проект и запустите браузер. Теперь, поскольку обработка запросов делегирована веб-действию, вы больше не можете просто запрашивать /, но вам нужно / \<action name\> или \<свойство ActionVar модуля\>=\<action name\>. Обратите внимание, что \<свойство ActionVar модуля\> по умолчанию имеет значение пустой строки, в отличие от **Application.ModuleVariable**, для которого "модуль" является значением по умолчанию. Итак, по умолчанию вы можете использовать только /\<action name\> формы.
428434
429435Если у вас несколько модулей, то у вас есть множество вариантов:
430436
431437- /\<module name\>/\<action name\>
432438- /\<module name\>?action=\<action name\>
433439- /\<action name\>?module=\<module name\>
434440- /?module=\<module name\>&action=\<action name\>
435441
436442Обратите внимание, что как только у модуля есть хотя бы одно действие, /\<module или action name\> по умолчанию сопоставляется с /\<action name\>. Чтобы изменить поведение таким образом, чтобы по умолчанию оно отображалось как /\<module name\>, установите для параметра **Application.PreferModuleName** значение **true**. В случае нескольких модулей, если имя модуля не указано, модуль по умолчанию будет обрабатывать данное действие. Чтобы изменить поведение таким образом, что имя модуля задавалось явно, установите для **Application.AllowDefaultModule** значение **false**.
437443
438444Следующие таблицы обобщают то, что произойдет на основе двух свойств:
439445
440446<thead>
441447
442448</thead>
443449<tbody>
444450
445451</tbody>
446452
447453##### Действие по умолчанию
448454
449455Помните предыдущую диаграмму, "Delegate request handling to actions" (Делегировать обработку запросов действиям) на самом деле не так просто, но если мы расширим эту диаграмму, изображение будет слишком большим, чтобы поместиться. Итак, вот схема этой части:
450456
451457![](/wiki-images/4/44/Fpwebtutorial_request_delegation_flow.png)
452458
453459Две важные вещи из потока: DefActionWhenUnknown и действие по умолчанию. Первое является свойством веб-модуля, а второе соответствует свойству Default действия. В последнем случае, если имеется более двух действий, для которых свойство Default установлено в значение `true`, порядок действий (то, как он отображается во всплывающем окне управления действиями) будет учитываться для определения того, какое действие является действием по умолчанию. Эти два свойства определяют, что должно делать приложение, если для данного запроса не найдено подходящего действия.
454460
455461Следующие таблицы обобщают то, что произойдет на основе двух свойств:
456462
457463<thead>
458464
459465</thead>
460466<tbody>
461467
462468</tbody>
463469
464470<thead>
465471
466472</thead>
467473<tbody>
468474
469475</tbody>
470476
471477В случае приведенного выше ответа об ошибке должна следовать трассировка стека с информацией о номере строки, если вы создаете свое приложение с параметром `-gl`. Позже мы увидим, как создать собственный обработчик для этого (трассировка стека не годится для производства). Но пока убедитесь, что вы понимаете концепцию веб-модуля и веб-действия, особенно поток запросов. Играйте, пока не решите, что готовы к следующему разделу.
472478
473479### Новый механизм
474480
475481Новый механизм чрезвычайно гибок и работает даже без модулей данных (старый механизм работает только с модулями данных).
476482
477483Выделенный модуль для этой маршрутизации предоставляется как **httproute** (добавьте его в пункт использования программы/модуля, где вы хотите зарегистрировать маршруты). Модуль содержит функцию **HTTPouter**, которая возвращает одноэлементный объект, отвечающий за управление маршрутом приложения, и имеет метод **RegisterRoute** для регистрации вашего маршрута.
478484
479485#### Синтаксис маршрута
480486
481487Первый параметр **HTTPouter.RegisterRoute** — это маршрут, который будет сопоставляться с входящим запросом. Это может быть как простое ***** выше, что означает 0 или более путей или просто любые пути, так и сложное, как **/api/v1/:resource/***, что означает, что заголовок **REQUEST_URI** должен начинаться с **/api/v1/**, за которым следует что-то еще, что будет привязано к переменной с именем resource и заканчиваться 0 или более путями. Он будет соответствовать:
482488
483489- /api/v1/products
484490- /api/v1/products/1
485491- /api/v1/products/1/clone
486492- /api/v1/products/something/else/that/is/really/long/and/silly
487493
488494но не:
489495
490496- /api/v1
491497- /excuse/me/api/v1/products
492498
493499В основном есть только 3 специальных символа:
494500
495501- *  обозначает 0 или более путей
496502
497503-
498504 param  обозначающий часть
499505
500506- /  обозначающий разделитель частей
501507
502508ваш маршрут будет состоять из этих 3 символов, а также всего остального, из чего состоит маршрут. 
503509
504510#### Регистрация маршрута
505511
5065122-й, 3-й или 4-й параметр (в зависимости от того, хотите ли вы обрабатывать конкретный метод HTTP и/или передавать ему дополнительные данные) **HTTPRouter.RegisterRoute** перегружен несколькими возможностями:
507513
508514- Процедура обратного вызова
509515
510516``` pascal
511517TRouteCallback = Procedure(ARequest: TRequest; AResponse);
512518```
513519
514520- Событие обратного вызова
515521
516522``` pascal
517523TRouteEvent = Procedure(ARequest: TRequest; AResponse) of object;
518524```
519525
520526- Объект, удовлетворяющий интерфейсу (CORBA)
521527
522528``` pascal
523529IRouteInterface = Interface ['{10115353-10BA-4B00-FDA5-80B69AC4CAD0}']
524530 Procedure HandleRequest(ARequest: TRequest; AResponse: TResponse);
525531end;
526532```
527533
528534- Объект, расширяющий абстрактный класс маршрутизатора
529535
530536``` pascal
531537TRouteObject = Class(TObject, IRouteInterface)
532538Public
533539 Procedure HandleRequest(ARequest: TRequest; AResponse: TResponse); virtual; abstract;
534540end;
535541
536542TRouteObjectClass = Class of TRouteObject;
537543```
538544
539545По умолчанию, если второй параметр не является **TRouteMethod**, будут совпадать все методы HTTP. Используйте один из **rmUnknown, rmAll, rmGet, rmPost, rmPut, rmDelete, rmOptions, rmHead, rmTrace**, чтобы соответствовать только определенному методу HTTP.
540546
541547Порядок регистрации имеет значение. Если есть два или более маршрута, соответствующих текущему запросу, его обработает ранее зарегистрированный.
542548
543549В этом новом механизме автономнаяй "Hello, World!"-программа может быть такой простой, как:
544550
545551``` pascal
546552uses
547553 fphttpapp, httpdefs, httproute;
548554procedure DoHello(ARequest:TRequest; AResponse : TResponse);
549555begin
550556 AResponse.Content:='<html><body><h1>Hello,World!</h1></body></html>'
551557end;
552558
553559begin
554560 HTTPRouter.RegisterRoute('*', @DoHello);
555561 Application.Port := 9000;
556562 Application.Initialize;
557563 Application.Run;
558564end.
559565```
560566
561567#### Пример веб-сервера
562568
563569Это пример простого кроссплатформенного многопоточного веб-сервера.
564570
565571``` pascal
566572program webserver;
567573
568574{$mode objfpc}{$H+}
569575
570576uses
571577 {$ifdef UNIX}
572578 cthreads, cmem,
573579 {$endif}
574580 fphttpapp, httpdefs, httproute;
575581
576582procedure route1(aReq: TRequest; aResp: TResponse);
577583begin
578584 aResp.content:='<html><body><h1>Route 1 The Default</h1></body></html>'
579585end;
580586
581587procedure route2(aReq: TRequest; aResp: TResponse);
582588begin
583589 aResp.content:='<html><body><h1>Route 2</h1></body></html>'
584590end;
585591
586592begin
587593 HTTPRouter.registerRoute('/', @route1, true);
588594 HTTPRouter.registerRoute('/route2', @route2);
589595 Application.port := 8080;
590596 Application.threaded := true;
591597 Application.initialize;
592598 Application.run;
593599end.
594600```
595601
596602## Использование шаблонов
597603
598604fpWeb имеет встроенную поддержку FPTemplate, универсального движка шаблонов Free Pascal. Его не обязательно использовать из контекста веб-приложения, но с интегрированной поддержкой все будет немного проще. По крайней мере, управление памятью можно игнорировать, так как об этом позаботится модуль.
599605
600606Есть два уровня, на которых можно использовать шаблоны: действие и модуль. Полная поддержка RAD до конца не реализована, поэтому в некоторых моментах вам нужно перейти к ручному кодированию.
601607
602608Существует два режима работы: непараметризованный и параметризованный. Активный режим управляется свойством **AllowTagParams**, которое должно быть достаточно очевидным, какое значение относится к какому режиму.
603609
604610Строка шаблона может быть передана из файла через свойство **FileName** или прямая строка через свойство **Template**. `Template.Template` - я знаю, это звучит странно :) Если оба заполнены, то FileName будет иметь приоритет.
605611
606612Два свойства: **StartDelimiter** и **EndDelimiter** определяют, как механизм должен распознавать тег шаблона. Например, если у вас есть:
607613
608614- StartDelimiter = '{+'
609615- EndDelimiter = '+}'
610616
611617затем строка '{+title+}' определяет тег шаблона с именем 'title'. Обратите внимание, что пробелы важны, поэтому '{+ title +}' определяет тег шаблона с именем ' title ', а не просто 'title'.
612618
613619Специальные для параметризованного режима дополнительные три свойства: **ParamStartDelimiter**, **ParamEndDelimiter** и **ParamValueSeparator** определяют, как движок должен распознавать параметр тега шаблона. Например, если у вас есть:
614620
615621- ParamStartDelimiter = '\[-'
616622- ParamEndDelimiter = '-\]'
617623- ParamValueSeparator = '='
618624
619625тогда строка '{+data \[-p=v-\]\[-a=b-\] +}' определяет тег шаблона с именем 'data' с параметром 'p' значения 'v' и параметром 'a' значения ' б'. Это можно использовать для передачи параметра уровня шаблона, такого как ожидаемый формат даты, заголовок-строка-нижний колонтитул для настраиваемого выходного представления, имя файла и т. д., как вы решите.
620626
621627Как следствие другого способа работы, основное событие, в котором работает шаблон, также отличается. Непараметризованный будет использовать **OnGetParam**, а параметризованный будет использовать **OnReplaceTag**. У них, конечно, другой интерфейс:
622628
623629``` pascal
624630Type
625631 // OnGetParam: только для поддержки простого тега шаблона (напр: {Name})
626632 TGetParamEvent = Procedure(
627633 Sender: TObject;
628634 Const ParamName: String;
629635 Out AValue: String
630636 ) Of Object;
631637 // OnReplaceTag: для тегов с поддержкой параметров
632638 TReplaceTagEvent = Procedure(
633639 Sender: TObject;
634640 Const TagString: String;
635641 TagParams: TStringList;
636642 Out ReplaceText: String
637643 ) Of Object;
638644```
639645
640646В **OnGetParam** вы проверяете **ParamName**, а затем соответственно назначаете **AValue**, т. е. если вы хотите, чтобы тег 'title' был заменен на 'My App', тогда заполните метод следующим образом:
641647
642648``` pascal
643649// используйте Trim(), если вы хотите, чтобы пробелы вокруг тега были незначительными
644650case Trim(ParamName) of
645651 'title': AValue := 'My App';
646652else
647653 AValue := 'UNKNOWN';
648654end;
649655```
650656
651657В **OnReplaceTag** вы проверяете **TagString** и, возможно, **TagParams**, а затем соответственно назначаете **ReplaceText**, т.е., если вы хотите, чтобы тег 'datetime' был заменен текущим временем с параметром 'datetimeformat', чтобы указать, как следует форматировать дату и время, заполните метод следующим образом:
652658
653659``` pascal
654660// используйте Trim(), если вы хотите, чтобы пробелы вокруг тега были незначительными
655661case Trim(TagString) of
656662 'datetime': AValue := FormatDateTime(TagParams.Values['datetimeformat'],Now);
657663else
658664 AValue := 'UNKNOWN';
659665end;
660666```
661667
662668### На уровне действий
663669
664670Создайте/выберите действие, затем перейдите в инспектор объектов. Вы увидите свойство подкомпонента с именем **Template**(Шаблон). Этот шаблон является обычным экземпляром **TFPTemplate**. Разверните его и заполните свойства, как описано выше. Теперь перейдите на вкладку Events(События), снова разверните **Template**, вы увидите два события. Заполните тот, который основан на вашем значении свойства **AllowTagParams**.
665671
666672> **Note:** Если ваш Lazarus не может автоматически заполнять событие, попробуйте ввести имя вручную в поле редактирования, а затем нажмите кнопку "...". Это ошибка в текущем Lazarus, которая может быть исправлена ​​в будущем.
667673
668674На этом уровне шаблон с содержимым не устанавливается автоматически в качестве обработчика запроса. Это может измениться в будущем, но давайте разберемся с его текущим состоянием. Заполните событие **OnRequest** действия, затем заполните его:
669675
670676``` pascal
671677with Actions.CurrentAction as TFPWebAction do
672678begin
673679 AResponse.Content := Template.GetContent;
674680end;
675681Handled := true;
676682```
677683
678684Приведение необходимо, поскольку **CurrentAction** имеет тип **TCustomWebAction** вместо **TFPWebAction**. Без него мы не сможем получить доступ к свойству **Template**.
679685
680686### На уровне модуля
681687
682688На уровне модуля вам в настоящее время придется делать это вручную, поскольку поддержка RAD не реализована. Связанным свойством является ModuleTemplate. Однако это не обычный экземпляр **TFPTemplate**, а специальный класс **TFPWebTemplate**, являющийся его потомком.
683689
684690Идея здесь состоит в том, чтобы модуль предоставлял макет, а действия предоставляли контент с возможностью предоставления дополнительных переменных. Поэтому рекомендуется оставить **AllowTagParams** как есть и назначить событие **OnGetParam** только **модуля**. НЕ назначайте **OnGetParam** для **ModuleTemplate**, так как он **никогда** не будет вызываться.
685691
686692Тег шаблона с именем 'content' будет автоматически заменен содержимым, созданным действием, все остальное заменяется либо из внутренних переменных шаблона, либо из **OnGetParam**.
687693
688694### Использование отдельного шаблона
689695
690696Несмотря на несколько неполную интеграцию, ничто не мешает вам использовать fpTemplate (или любые другие решения для шаблонов) вручную, вне встроенной поддержки. В некоторых случаях это может быть даже лучше, поскольку оно будет модульным.
691697
692698## Использольвание html Producer object
693699
694700Предполагая, что существует TWebAction с именем act_get_my_html, вы должны попросить написать в AResponse возврат внутреннего потока байтов: при ответе с помощью решения html Producer objects мы должны использовать методы писателя. Он управляет потоком памяти с целью ускорения. Здесь нет строкового типа, как в приведенном выше решении fpTemplate, которое использует обработку текста строки, как обычно в Php. Producer записывает в AResponse рекурсивную итерацию " foreach ", которая проходит по дереву полиморфных объектов HTML Dom, состоящему из иерархии классов `THtmlCustomElement = Class(TDOMElement)`(см. модуль `htmlelements.pp`). Итак, AResponse пишется параметром aWriter, со следующим вызовом:
695701
696702``` pascal
697703procedure TFPWebModule1.act_get_my_htmlRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
698704begin
699705 (* спрашиваем у HTMLEntityProducer1 содержимое своих html-элементов DOM: *)
700706 HTMLEntityProducer1.HandleRequest(ARequest, AResponse, Handled);
701707 Handled := True;
702708end;
703709```
704710
705711Повторим еще раз, что работа Producer заключается в преобразовании потока внутренней памяти в текст в объекте ответа: именно так был разработан этот шаблон, хотя существует метод рендеринга text-html с именем ProduceContent только для целей отладки. Вы можете переопределить этот метод, если вы пишете или отлаживаете компонент Producer.
706712
707713Вот пример с html-объектом Producer, позволяющий создать веб-приложение «способом RAD», то есть с помощью перетаскивания компонента HTMLEntityProducer из палитры:
708714
709715``` pascal
710716procedure TFPWebModule1.HTMLEntityProducer1WriteEntity(Sender: THTMLContentProducer; aWriter: THTMLWriter);
711717begin
712718
713719 aWriter.startHeader;
714720 aWriter.Meta('','Content-Type','text/html; charset=UTF-8');
715721 aWriter.title('My web page');
716722 aWriter.link('stylesheet','stylesheet.css','text/css','screen');
717723 aWriter.EndHeader;
718724
719725 aWriter.Startbody;
720726 aWriter.Startparagraph;
721727 aWriter.heading2('Hello, world from inside §1:');
722728 aWriter.Text('Here is text written inside the current paragraph.');
723729 aWriter.Endparagraph;
724730 aWriter.paragraph('This is another text written inside a self "started and ended" paragraph.');
725731 aWriter.Startparagraph;
726732 aWriter.heading2('Hello, world from inside §2:');
727733 aWriter.Text('Here is the final text.');
728734 aWriter.Image.src := 'logo.png';
729735 AnotherProducer.WriteContent(aWriter);
730736 aWriter.Endparagraph;
731737 aWriter.Endbody;
732738
733739end;
734740```
735741
736742Свойство TWebAction.ContentProducer позволяет связать THTTPContentProducer с его веб-действием, предоставляемым через URI в сети.
737743
738744\[ToDo: нет официальной документации о компонентах «способ RAD» fpWeb (html producer, html provider, html adapter, html formatter и т.д.)\]
739745
740746## Советы и хитрости
741747
742748### Возврат другого кода ответа HTTP
743749
744750По умолчанию fpWeb возвращает HTTP 200 OK, чтобы указать на успешную обработку запроса. Это, конечно, не всегда так, поскольку пользовательский ввод может быть не таким, как мы ожидали. Для этого установите для параметра **AResponse.Code** в обработчике запроса код, который вы хотите вернуть.
745751
746752### Перенаправление запроса на другой URL
747753
748754Обычная практика после успешного входа в систему — перенаправление пользователя на страницу его учетной записи. Это можно сделать, вызвав **AResponse.SendRedirect** в обработчике запросов, указав URL-адрес для перенаправления запроса.
749755
750756### Обслуживание статических файлов (встроенный веб-сервер)
751757
752758Помните диалоговое окно в разделе [[fpWeb Tutorial/ru|Hello, World!]] после выбора приложения HTTP-сервера? Если вы отметите "Register location to serve files from"(Зарегистрировать местоположение для обслуживания файлов), вы можете заполнить "Location"(Расположение) (сегмент URI, не должен содержать слэшей) и "Directory"(Каталог) (физический каталог на вашем компьютере, должен существовать во время выполнения), то мастер просто добавит:
753759
754760``` pascal
755761RegisterFileLocation('<Location>','<Directory>');
756762```
757763
758764в начало вашего *.lpr-файла и добавит модуль **fpwebfile** в раздел uses. Вы можете сделать это вручную в любое время, а также зарегистрироваться несколько раз для разных местоположений/каталогов. После этого вы можете запросить **/\<Location\>/\<любое имя файла в Directory\>**, и оно будет предоставлено автоматически. Обратите внимание, что *mimetype* файла определяется **fpmimetypes**. Вызовите **MimeTypes.LoadFromFile** с вашим файлом *mime.types*, чтобы указать правильный тип *mimetype* на основе его расширения. В противном случае файл всегда будет обслуживаться как **application/octet-stream**, что означает, что браузер загрузит его, а не интерпретирует (особенно важно для файлов JavaScript и CSS).
759765
760766Вы можете получить полные mime.types [здесь](http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?&view=co).
761767
762768В Lazarus 2.0.6 или новее вы должны добавить в начало своей программы полный путь к файлу mime.types.
763769
764770``` pascal
765771begin
766772 MimeTypesFile := Application.Location + 'mime.txt';
767773```
768774
769775Примите во внимание, что путь по умолчанию, идущий с проектом, это - *lib\\\$(TargetCPU)-\$(TargetOS)*
770776
771777Например: *httpproject\lib\i386-win32\mime.txt*
772778
773779### Централизованное управление конфигурацией и модулями
774780
775781По умолчанию программный файл (.lpr) содержит модуль протокола. Это ограничивает возможность использования объекта **Application** из других контекстов, таких как веб-модули. К счастью, нетрудно провести рефакторинг, чтобы получить то, что мы хотим. Мы удаляем вызовы **RegisterHTTPModule** из веб-модулей и не будем использовать .lpr для очистки основного блока с единственным идентификатором модуля в разделе uses, назовем его: брокер. Содержимое модуля:
776782
777783``` pascal
778784unit Brokers;
779785
780786{$mode objfpc}{$H+}
781787
782788interface
783789
784790{ $define cgi}
785791{ $define fcgi}
786792{$define httpapp}
787793
788794uses
789795 CustWeb;
790796
791797function GetApp: TCustomWebApplication; inline;
792798
793799implementation
794800
795801uses
796802 {$ifdef cgi}fpcgi{$endif}
797803 {$ifdef fcgi}fpfcgi{$endif}
798804 {$ifdef httpapp}fphttpapp{$endif}
799805 ,webmodule1
800806 ,webmodule2
801807 ;
802808
803809function GetApp: TCustomWebApplication;
804810begin
805811 Result := Application;
806812end;
807813
808814initialization
809815 RegisterHTTPModule('wm1', TWebModule1);
810816 RegisterHTTPModule('wm2', TWebModule2);
811817 {$ifndef cgi}
812818 Application.Port := 2015;
813819 {$endif}
814820 Application.Initialize;
815821 Application.Run;
816822end.
817823```
818824
819825Таким образом, мы можем управлять регистрацией веб-модуля, а также предоставлять API для получения объекта **Application** (представленного как **TCustomWebApplication**), в то же время легко переключаясь между реализациями протокола в одном месте.
820826
821827### Элегантное завершение (FastCGI / встроенный веб-сервер)
822828
823829Вместо нажатия `Ctrl`+`C` в вашем приложении есть способ корректно завершить работу вашего приложения, выполнив необходимую очистку, вызвав **Application.Terminate**. Возможно, вам придется использовать предыдущий трюк, чтобы легко получить доступ к объекту **Application**. Распространенной реализацией является предоставление определенного защищенного паролем модуля/действия, которое вызывает метод Terminate. Впрочем, вы можете выбрать любой способ.
824830
825831### Пользовательский обработчик исключений
826832
827833\[отредактировано 1 мая 2020г =\> переход от указателя метода к простой процедуре..\]
828834
829835Чтобы переопределить обработчик исключений по умолчанию, который выдает трассировку стека всякий раз, когда возникает исключение (например, по HTTP 404 или 500), и, следовательно, не подходит для производства, вы должны задать **Application.OnShowRequestException**.
830836
831837Это метод, поэтому вам нужно будет предоставить свою процедуру, реализующую метод, и присвоить ее с помощью объекта, т.е.: если у вас есть **MyExceptionHandler** как объект **TMyExceptionHandler**, который имеет метод **MyShowRequestException**, вы можете назначить его посредством:
832838
833839``` pascal
834840Application.OnShowRequestException := @MyExceptionHandler.MyShowRequestException;
835841```
836842
837843не забудьте **.Create()** **MyExceptionHandler** ПЕРЕД назначением выше, иначе вы получите **EAccessViolation**!
838844
839845Вы должны предоставить свою глобальную процедуру, которая реализует ваш собственный обработчик исключений (в рабочей среде рекомендуется заменить стек вызовов кодом состояния HTTP и его объяснением). Затем вы можете переопределить обработчик исключений по умолчанию, назначив его следующим образом:
840846
841847``` pascal
842848Application.OnShowRequestException := @MyShowRequestException;
843849```
844850
845851### Чисто ручное кодирование (дизайнер форм не требуется)
846852
847853Нет необходимости использовать конструктор форм Lazarus для написания fpWeb-приложения. Вы можете использовать технику чисто ручного кодирования, чтобы написать его. Секрет кроется в третьем параметре **RegisterHTTPModule**: **SkipStreaming**. Если для этого параметра установлено значение true, fpWeb не будет искать ресурс .lfm. Поэтому все должно обрабатываться вручную: настройки свойств, обработчики событий, регистрация действий и т.д.
848854
849855Обратите внимание, что логично делать то, что обычно делается через инспектор объектов в переопределенном конструкторе. Внутри него вызовите унаследованный конструктор, передав в качестве параметров AOwner и CreateMode. После этого вы можете установить свойства, назначить обработчики событий и т.д. Пример:
850856
851857``` pascal
852858type
853859 THelloWorldModule = class(TFPWebModule)
854860 constructor CreateNew(AOwner: TComponent; CreateMode: Integer); override;
855861 procedure Request(Sender: TObject; ARequest: TRequest;
856862 AResponse: TResponse; var Handled: Boolean);
857863 end;
858864
859865constructor THelloWorldModule.CreateNew(AOwner: TComponent; CreateMode: Integer);
860866begin
861867 inherited CreateNew(AOwner,CreateMode);
862868 OnRequest := @Request;
863869end;
864870
865871procedure THelloWorldModule.Request(Sender: TObject; ARequest: TRequest;
866872 AResponse: TResponse; var Handled: Boolean);
867873begin
868874 AResponse.Content := 'Hello, World!';
869875 Handled := true;
870876end;
871877```
872878
873879## См.также
874880
875881- [Lazarus для Интернета (учебник)](https://www.freepascal.org/~michael/articles/#web1)
876882- [Lazarus для Интернета: сеансы и шаблоны (учебник))](https://www.freepascal.org/~michael/articles/#web2) В этой статье описывается класс TSQLDBSession, который управляет подключением SQL, которое наследуется от веб-сеанса (для управления историями переменных веб-сеанса в рамках сеанса подключения к базе данных, имеющей возможности блокировки строк).
877883- примеры - чтение текстовых файлов - в вашем каталоге ...\fpc\x.x.x\source\packages\fcl-web\examples.\
878884
879885![](/wiki-images/c/c0/Cgi-listrecords.png)
880886
881887- XML или JSON на ExtJS: если вы хотите использовать более или менее сложный движок, который очень «продвинут» в отношении аспекта рендеринга объектов Javascript на стороне браузера, в дополнение к их функциональным возможностям (например, ExtJS для отображения сетка с поддержкой базы данных, динамическая графика и т. д.), для такого решения очень часто требуется файл XML или JSON. Существуют классы Adapter и Formatter (например, классы TExtJSXMLWebdataInputAdaptor, TExtJSJSONDataFormatter).
882888
883889В целом, классы Adapter адаптируют узлы входящего jSon или XML к отображению своих полей базы данных TField. А классы Formatter сопоставляют каждую запись TField с ее узлом в формате jSon или XML перед отправкой в ​​браузер.\
884890* [Форматы веб-данных в Lazarus/FPC](https://www.freepascal.org/~michael/articles/#webdata)
885891
886892- [Практическое построение GUI в ExtJS](https://www.freepascal.org/~michael/articles/#extjs1)
887893- [Смешивание веб-приложений ExtJS и Pascal: ExtPascal](https://www.freepascal.org/~michael/articles/#extpascal)
888894
889895---
890896*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