Sign in Join
Wiki copytemporary

fcl-web: what changed

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

OldNewLine
11## What is fpWeb
22
33fpWeb can be used to build web server applications. A web server application can be one of the following
44
55- CGI application
66- FastCGI application
77- Apache module
88- A standalone HTTP(s) server using Free Pascal HTTP server components.
99- A standalone HTTP(s) server using GNU libmicrohttp
1010- A windows SysHTTP application.
1111
1212In all cases, the development model is the same: you register handlers for a set of URLS (called routes) that your application will support.
1313
1414Then you embed these routes in one of the above environments: This means you choose the application object that you will be using.
1515
1616The lazarus IDE you have the choice of applications when you start a new project. It's possible to change the application type once the application was started, this usually just means changing the name of a unit in the project's uses clause.
1717
1818It is possible to support multiple environments within a single binary, although this takes some extra work.
1919
2020fpWeb also offers some out-of-the box support for a database REST server or for JSON RPC 2.0 mechanisms. These are built on top of the above structure. The Brook framework is also built on top of the basic architecture outlined above.
2121
2222More about this can be found in the [[fpWeb Tutorial]]
2323
2424The [[simpleserver]] example is a small stand alone server application that showcases many of the possibilities of fcl-web.
2525
2626## Using fpWeb together with Lazarus
2727
2828### Installing the weblaz fpWeb Lazarus Package
2929
3030The first step to do is installing the package which comes in the path lazarus/components/fpweb/weblaz.lpk. As usual with design-time packages, you'll have to rebuild Lazarus.
3131
3232### Creating a CGI application
3333
3434After the weblaz package is installed, a very simple CGI web application which displays an HTML page can be created by going to the Lazarus menu "File-\>New...". From the list of possible applications select "CGI Application" as in the image below, which will create a main CGI project file and a fpweb web module.
3535
3636![](/wiki-images/e/ec/New_cgi.PNG)
3737
3838The TFPWebModule allows you to manipulate properties and events using the Object Inspector.
3939
4040To add code to show the page, a request handler should be added. To do this, double click the OnRequest property in the object inspector, as in the image below:
4141
4242![](/wiki-images/3/3d/Webmodule.PNG)
4343
4444In the event handler one should write the HTML code which will be displayed by the browser. To avoid mixing Pascal and HTML, this page can be loaded from the directory the CGI executable is in by using AResponse.Contents.LoadFromFile().
4545
4646The type of the response should be set in AResponse.ContentType. For HTML pages this should have the value 'text/html;charset=utf-8'.
4747
4848Finally, Handled should be set to True to indicate that the request was successfully handled (and return a 200 OK status code to the browser). After adding this code, the web module should look like this:
4949
5050``` pascal
5151unit mainpage;
5252
5353{$mode objfpc}{$H+}
5454
5555interface
5656
5757uses
5858 Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb;
5959
6060type
6161
6262 { TFPWebModule1 }
6363
6464 TFPWebModule1 = class(TFPWebModule)
6565 procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;
6666 AResponse: TResponse; var Handled: Boolean);
6767 private
6868 { private declarations }
6969 public
7070 { public declarations }
7171 end;
7272
7373var
7474 FPWebModule1: TFPWebModule1;
7575
7676implementation
7777
7878{$R *.lfm}
7979
8080{ TFPWebModule1 }
8181
8282procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
8383 AResponse: TResponse; var Handled: Boolean);
8484begin
8585 AResponse.ContentType := 'text/html;charset=utf-8';
8686 AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
8787 Handled := True;
8888end;
8989
9090begin
9191 RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
9292end.
9393```
9494
9595### Deploying the CGI application
9696
9797This section assumes the Apache web server is used. Of course, other web servers that support CGI (nginx, cherokee) can be used, too.
9898
9999Apache can be downloaded here: <http://httpd.apache.org/download.cgi> or installed using your distribution's package manager.
100100
101101The default installation of Apache will treat all files located in its cgi-bin directory as CGI programs, so the user won't be able to access plain HTML files placed there. This directory can be set in the file httpd.conf in the following section:
102102
103103 #
104104 # ScriptAlias: This controls which directories contain server scripts.
105105 # ScriptAliases are essentially the same as Aliases, except that
106106 # documents in the target directory are treated as applications and
107107 # run by the server when requested rather than as documents sent to the
108108 # client. The same rules about trailing "/" apply to ScriptAlias
109109 # directives as to Alias.
110110 #
111111 ScriptAlias /cgi-bin/ "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/"
112112
113113If you place an executable called "mywebpage.cgi" in this directory, then the page can be accessed as <http://localhost/cgi-bin/mywebpage.cgi> (or remotely with the corresponding IP address or domain name).
114114
115115fcl-web with Lazarus on Windows produces .exe files. For Apache to serve these files you have to add this:
116116
117117 AddHandler cgi-script .exe
118118
119119And to serve your executables from another directory, you add this:
120120
121121``` xml
122122 ScriptAlias /bin/ "C:/lazarus_projects/test-fclweb/bin/"
123123 <Directory "C:/lazarus_projects/test-fclweb/bin/">
124124 AllowOverride None
125125 Options None
126126 Order allow,deny
127127 Allow from all
128128 </Directory>
129129```
130130
131131### Formatting the HTML and Reading Query fields
132132
133133The previous example just showed a plain HTML page. One might wish to e.g.
134134
135135- dynamically change the HTML page output, and
136136- as read the variables the browser passed to the webpage in Query fields (in e.g. HTTP GET and HTTP POST actions).
137137
138138A simple solution for the first problem is simply using the standard Pascal routine Format and adding %s or %d in the HTML file in the appropriate places which will receive a custom value.
139139
140140### Reading GET data
141141
142142To read the GET variables one can use ARequest.QueryFields, which is a TStrings descendent. Each variable will be in a separate line in the TStrings in the format variablename=value, similarly to how they are shown in the browser page address. To search for a specific variable one can use ARequest.QueryFields.Values\[\], passing the variable name in the brackets to receive its value back, instead of parsing the string manually.
143143
144144The resulting code:
145145
146146``` pascal
147147procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
148148 AResponse: TResponse; var Handled: Boolean);
149149var
150150 HexText, AsciiText: string;
151151begin
152152 HexText := ARequest.QueryFields.Values['hex'];
153153 AsciiText := HexToAnsii(HexText);
154154
155155 AResponse.ContentType := 'text/html;charset=utf-8';
156156 AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
157157 AResponse.Contents.Text := Format(AResponse.Contents.Text,
158158 [HexText, AsciiText]);
159159 Handled := True;
160160end;
161161```
162162
163163### Reading POST data
164164
165165Data submitted by POST requests can be obtained from `TRequest.Content`. It will come without the request headers - in other words it contains the body of the request.
166166
167167Submitted form data can also be accessed using `TRequest.ContentFields` which is the content parsed as fields separated by & and decoded. For example, the following set of form values:
168168
169-`login: dfg 345&&`\
170-`login_senha: ====`\
171-`email: dfg`
169+```text
170+login: dfg 345&&
171+login_senha: ====
172+email: dfg
173+```
172174
173175Will be encoded in 'APPLICATION/X-WWW-FORM-URLENCODED' like this (see [wikipedia POST (HTTP)](http://en.wikipedia.org/wiki/POST_%28HTTP%29)):
174176
175-`login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg`
177+```text
178+login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg
179+```
176180
177181And will be available in TRequest.ContentFields via the line index or using the Values property, which is more convenient:
178182
179-`TRequest.ContentFields[0]: login=dfg 345&&`\
180-`TRequest.ContentFields[1]: login_senha=====`\
181-`TRequest.ContentFields[2]: email=dfg`\
182-`TRequest.ContentFields.Values['email']: dfg`
183+```text
184+TRequest.ContentFields[0]: login=dfg 345&&
185+TRequest.ContentFields[1]: login_senha=====
186+TRequest.ContentFields[2]: email=dfg
187+TRequest.ContentFields.Values['email']: dfg
188+```
183189
184190If using other mime-types than 'MULTIPART/FORM-DATA' and 'APPLICATION/X-WWW-FORM-URLENCODED' (the types supported by HTML forms):
185191
186192- content is only available in TRequest.Content, not TRequest.ContentFields
187193- use of these mime-types raises an exception for FPC 2.4.2 (fixed in FPC 2.5+).
188194
189195Note that HTTP POST can also send Query fields (in the URL), e.g. <http://server/bla?question=how>, and those are accessed by TRequest.QueryFields as explained in the previous section.
190196
191197``` pascal
192198procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
193199 AResponse: TResponse; var Handled: Boolean);
194200var
195201 lData: String;
196202begin
197203 lData := ARequest.Content;
198204```
199205
200206### Reading POST binary data
201207
202208Uploaded files are saved to a temporary location with a temporary filename, to retain the file you should move it to a permanent location.
203209
204210To receive data on the server that has been POSTed e.g. as multipart/form-data, use something like this:
205211
206212``` pascal
207213procedure TMainWebModule.TFPWebActions2Request(Sender: TObject;
208214 ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
209215var
210216 i: Integer;
211217begin
212218 // Process all received files
213219 for i := 0 to ARequest.Files.Count - 1 do
214220 begin
215221 // Doing something else than writeln is highly recommended ;)
216222 writeln ('Received Filename: '+ARequest.Files[i].LocalFileName);
217223 end;
218224
219225 Handled := true;
220226end;
221227```
222228
223229The client can send data using e.g. FileFormPost.
224230
225231## Specialized modules
226232
227233The *TFPWebModule* class (used below) is a simple example of a WEb module that can be used for all kinds of HTTP requests.
228234
229235However, fpweb comes with some specialized modules, that have extra support for specialized tasks:
230236
231237- The **TSimpleFileModule** class in unit [[fpwebfile]] can be used to send files. You point it to a directory, and it does the rest.
232238- The **TFPHTMLModule** class in unit *fphtml.pp* can be used to produce HTML.
233239- The **TProxyWebModule** class in unit *fpwebproxy.pp* is a ready-made forwarding proxy.
234240- The **TFPWebProviderDataModule** class in unit *fpwebdata.pp* serves data in JSON format that can be consumed by ExtJS stores.
235241- The **TSQLDBRestModule** class in unit *sqldbrestmodule.pp* implements a full REST server backed by SQLDB. See more info in [[SQLDBRestBridge]].
236242- The **TJSONRPCModule** class in unit *webjsonrpc.pp* implements a JSON-RPC service.
237243- The **TExtDirectModule** class in unit *fpextdirect.pp* implements a Ext.Direct variant of a JSON-RPC service.
238244
239245## Using multiple modules
240246
241247If there is only one module in the web application, all requests will be directed to this module.
242248
243249As your web application grows, multiple modules can be used. A new module can be added by choosing 'File - New' and then one of 'Web module' or 'HTML Web Module'.
244250
245251FCL-web uses the URL to determine how a HTTP request should be handled. It must therefore know which web-modules exist in the application. To achieve this, each module must be registered.
246252
247253Each module is registered with fcl-web in the initialization section of the unit it is defined in:
248254
249255``` pascal
250256RegisterHTTPModule('location', TMyModule);
251257```
252258
253259The module will then be invoked if an URL of the form
254260
255-`http://www.mysite.org/mycgi.cgi/location`
261+```text
262+http://www.mysite.org/mycgi.cgi/location
263+```
256264
257265or
258266
259-`http://www.mysite.org/mycgi.cgi?module=location`
267+```text
268+http://www.mysite.org/mycgi.cgi?module=location
269+```
260270
261271is used.
262272
263273If multiple modules are present, the name of the module must appear in the URL, or an error will be raised.
264274
265275This behaviour can also be forced for applications that have only a single module by setting the Application's property AllowDefaultModule to false:
266276
267277``` pascal
268278Application.AllowDefaultModule := False;
269279```
270280
271281In that case, the fcl-web application will always require the name of the module in the URL.
272282
273283The name of the request variable that determines the module name (by default, this is 'module') can be set in the Application.ModuleVariable property. The following code
274284
275285``` pascal
276286Application.ModuleVariable := 'm';
277287```
278288
279289ensures that the following URL is directed to TMyModule:
280290
281-`http://www.mysite.org/mycgi.cgi?m=location`
291+```text
292+http://www.mysite.org/mycgi.cgi?m=location
293+```
282294
283295If all this is not enough to determine the module to which the request should be passed, the Application.OnGetModule event can be used. It is of type TGetModuleEvent:
284296
285297``` pascal
286298type
287299 TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
288300 Var ModuleClass : TCustomHTTPModuleClass) of object;
289301```
290302
291303Creating an event handler for this event allows fine control over the module that is created to handle the request: the request (passed in ARequest) can be examined, and the 'ModuleClass' variable must be set to the class of the module that should handle the request.
292304
293305If 'ModuleClass' is 'Nil' on return, an error will be sent to the browser.
294306
295307## Using Actions
296308
297309A module can be used to group certain kinds of actions that logically belong together. Imagine a module TUserModule that is used to handle user management in the webpages. There can be multiple actions associated with a user:
298310
299311- Creating
300312- Deleting
301313- Editing
302314- Displaying
303315
304316These different actions can be handled by the same module. One can determine the action from the URL manually, as in:
305317
306-`http://mysite/mycgi.cgi/user?action=delete&id=12`
318+```text
319+http://mysite/mycgi.cgi/user?action=delete&id=12
320+```
307321
308322This can be automated.
309323
310324In order to make it easier to distinguish between various actions, the module has a property actions: this is a collection, in which each item is associated with a different response to the request. The actions have various properties:
311325
312326Name
313327The name of the action. The URL will be examined to determine the name of the action.
314328
315329Content
316330A string. If set, this is sent to the browser.
317331
318332Contents
319333A stringlist. If set, this is sent to the browser.
320334
321335ContentProducer
322336If set, the contentproducer will handle the request.
323337
324338Default
325339if set to 'True', then this action is the default action. That means that if FCL-Web cannot determine the action name, this action will be executed.
326340
327341Template
328342If set, this template will be processed, and the results sent to the browser.
329343
330344There are also some events:
331345
332346BeforeRequest
333347executed before the request is processed. Can be used to set the 'Content' or other properties.
334348
335349AfterRequest
336350executed after the request is processed.
337351
338352OnRequest
339353an event handler to handle the request. If set, the handler is used to handle the request.
340354
341355Again, as in the case of multiple modules, the URL is used to determine which action to execute. The part right after the module part ("user" in this example) is used:
342356
343-`http://mysite/mycgi.cgi/user/delete&id=12`
357+```text
358+http://mysite/mycgi.cgi/user/delete&id=12
359+```
344360
345361would execute the action named 'delete'.
346362
347363The 'ActionVar' property of the module can be used to set the name of the request variable to use. Setting
348364
349365``` pascal
350366UserModule.ActionVar := 'a';
351367```
352368
353369may be used to change the above URL to
354370
355-`http://mysite/mycgi.cgi/user?a=delete&id=12`
371+```text
372+http://mysite/mycgi.cgi/user?a=delete&id=12
373+```
356374
357375If there is only one module in the application, the URL can be shortened to
358376
359-`http://mysite/mycgi.cgi/delete&id=12`
377+```text
378+http://mysite/mycgi.cgi/delete&id=12
379+```
360380
361381## Using HTML Templates
362382
363383For information about using templates, template tags and template tag parameters to generate response pages, please refer to the fptemplate.txt file under your FPC directory in /packages/fcl-base/texts/.
364384
365385Example projects that demonstrate using templates can be found under your FPC directory in /packages/fcl-web/examples/fptemplate/ (see the README.txt in there for more). (In earlier versions, these examples were in the Lazarus directory in /components/fpweb/demo/fptemplate/)
366386
367387## Tips & troubleshooting
368388
369389### Sending result codes
370390
371391To send a different HTTP response than 200 OK, use **AResponse.Code** and **AResponse.CodeText**, e.g.
372392
373393``` pascal
374394AResponse.Code := 404;
375395AResponse.CodeText := 'Document not found';
376396```
377397
378398As of FPC 3.3.1 you can use the **SetStatus** method:
379399
380400``` pascal
381401AResponse.SetStatus(404);
382402```
383403
384404it will use the *GetHTTPStatusText* routine of the *httpprotocol* unit to look up standard status texts.
385405
386406You can also tell **SetStatus** to send the response at once:
387407
388408``` pascal
389409AResponse.SetStatus(404,true);
390410```
391411
392412This will set the status and call *SendResponse* to send the response. Any headers or content you have set will be sent as well.
393413
394414### Sending binary data
395415
396416An approach that seems to work to send e.g. a tiff file from the web server to the client - adapted from \$(fpcdirectory)\packages\fcl-web\examples\jsonrpc\demo1\wmdemo.pp - something like:
397417
398418``` pascal
399419AResponse.ContentStream := TMemoryStream.Create;
400420
401421try
402422 AResponse.ContentStream.LoadFromFile('/tmp/sample.tiff');
403423 AResponse.ContentType := 'image/tiff'; //or whatever MIME type you want to send
404424// to do: there is an fpweb example that gets the mime type from the file extension...
405425 AResponse.ContentLength:=AResponse.ContentStream.Size; //apparently doesn't happen automatically?
406426 AResponse.SendContent;
407427finally
408428 AResponse.ContentStream.Free;
409429end;
410430
411431Handled := true;
412432```
413433
414434### Error: Could not determine HTTP module for request
415435
416436You may have multiple modules and multiple actions. If you specify an URL with only 1 item, like:
417437
418438```html4strict
419439http://localhost/cgi-bin/somemodule
420440```
421441
422442then fpweb assumes you're specifying an action. If you don't have a default module set, you will get a 500 internal server error (Could not determine HTTP module for request)
423443
424444You can modify this behaviour to let fpweb map to a module name instead by setting the application's **PreferModuleName** property to true.
425445
426446### Error: response code 500 Internal Server error when trying to handle DELETE method
427447
428448In FPC 2.6.2 and lower, fcl-web does not accept the DELETE method and generates an error.
429449
430450### fpWeb/FCGI and Apache 2.4 (mod_proxy_fcgi mode)
431451
432452When you want to deploy FCGI application behind Apache 2.4+ reverse proxy, you need PATH_INFO variable in headers that are sent via socket to FCGI daemon.
433453
4344541, Enable following modules: setenvif_module, proxy_module and proxy_fcgi_module\
435455(on CentOS 7 modules are defined in file /etc/httpd/conf.modules.d/00-base.conf and /etc/httpd/conf.modules.d/00-proxy.conf).
436456
437457 LoadModule setenvif_module modules/mod_setenvif.so
438458 LoadModule proxy_module modules/mod_proxy.so
439459 LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
440460
4414612, Reverse proxy configuration for fpWeb/FCGI listening on port 9000.\
442462\$1 = module name\
443463\$2 = action name
444464
445465 SetEnvIf Request_URI . proxy-fcgi-pathinfo=full
446466 ProxyPassMatch "/fcgi/(.*)/(.*)" "fcgi://127.0.0.1:9000/$1/$2"
447467
448468### fpWeb/FCGI and nginx
449469
450470For correct routing, fcl-web requires the PATH_INFO variable in headers sent from nginx. For this you have to split the whole URL into FastCGI application name and the path info part whith the configuration statement *fastcgi_split_path_info*. The statement accepts a regular expression and puts the second match into *\$fastcgi_path_info* configuration variable. This variable can be passed to your application with *fastcgi_param* statement.
451471
452472The following configuration example includes a full virtual host for nginx which passes everything from *<http://myserver:8080/mycgiapp/>* to an external FastCGI application.
453473
454474 server {
455475
456476 listen 8080;
457477 listen [::]:8080;
458478
459479 server_name _;
460480
461481 root "/var/www";
462482
463483 location /mycgiap/ {
464484 # setting up PATH_INFO
465485 fastcgi_split_path_info ^(/mycgiapp/)(.*)$;
466486 fastcgi_param PATH_INFO $fastcgi_path_info;
467487
468488 # other parameters
469489 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
470490 fastcgi_param QUERY_STRING $query_string;
471491 fastcgi_param REQUEST_METHOD $request_method;
472492 fastcgi_param CONTENT_TYPE $content_type;
473493 fastcgi_param CONTENT_LENGTH $content_length;
474494 fastcgi_param SCRIPT_NAME $fastcgi_script_name;
475495 fastcgi_param REQUEST_URI $request_uri;
476496 fastcgi_param DOCUMENT_URI $document_uri;
477497 fastcgi_param DOCUMENT_ROOT $document_root;
478498 fastcgi_param SERVER_PROTOCOL $server_protocol;
479499 fastcgi_param REQUEST_SCHEME $scheme;
480500 fastcgi_param HTTPS $https if_not_empty;
481501 fastcgi_param GATEWAY_INTERFACE CGI/1.1;
482502 fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
483503 fastcgi_param REMOTE_ADDR $remote_addr;
484504 fastcgi_param REMOTE_PORT $remote_port;
485505 fastcgi_param SERVER_ADDR $server_addr;
486506 fastcgi_param SERVER_PORT $server_port;
487507 fastcgi_param SERVER_NAME $server_name;
488508
489509 # pass requests to your FastCGI application
490510 fastcgi_pass 127.0.0.1:9000;
491511 }
492512 }
493513
494514## Notes
495515
496516- The cgiapp unit is deprecated, please use fpcgi as much as possible.
497517
498518- The fastcgi, custfcgi, and fpfcgi units are not supported on Darwin at this time, because it uses a different mechanism than the MSG_NOSIGNAL option for recv() to indicate that no SIGPIPE should be raised in case the connection breaks. See <http://lists.apple.com/archives/macnetworkprog/2002/Dec/msg00091.html> for how this should be fixed.
499519
500520- If you deploy your CGI application and get errors like "Error: No action name and no default action", you should make sure there's an action assigned to the URL, or catch non-supported actions with an action marked Default. In both cases, an OnRequest event handler for the action that sets Handled:=true should be used.
501521
502522## See also
503523
504524- [[fpWeb Tutorial]]
505525- [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications
506526- [[fphttpserver]] Small stand alone Object Pascal web server component. Can be used to serve fcl-web applications.
507527- [[simpleserver]] Small stand alone server application.
508528- [[CGI Web Programming|CGI_Web_Programming#Debugging_CGI]] Information about debugging CGI applications
509529- [Part of a tutorial by Leonardo Ramé that shows how to program an fcl-web CGI application; includes example of generating JSON content](https://github.com/leonardorame/Ext4-MVC-Tutorial/wiki/Lazarus-Server)
510530- [[WCHTTPServer]] HTTP/2+HTTP1.1 server based on fcl-web
511531
512532---
513533*From the Free Pascal wiki, page "fcl-web", as it stood on 2021-09-04 after a change by Michael, 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