Sign in Join
Wiki copytemporary

fpWeb Tutorial: what changed

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

OldNewLine
11Originally based on: fcl-web tutorial (in PDF) by forum user [Leledumbo](https://forum.lazarus.freepascal.org/index.php?action=profile;u=7523).
22
33## Introduction
44
55fpWeb is a web application framework shipped by FPC in its default distribution as a part of fcl-web package. The framework itself is built on top of fcl-web features. The framework is built with RAD mind to make a good use of componentization in producing dynamic content. A Lazarus package is provided that can use the framework in a drag n drop manner for its session management and content production. This tutorial will attempt to cover basic functionality of fpWeb, so that one can build common web application with it. Note that this tutorial does **NOT** attempt to teach HTTP protocol, HTML, CSS, JavaScript or database manipulation as the protocol and client languages should be the prerequisites for every web application programmer and database manipulation does not differ from desktop implementation.
66
77## Architecture (PLEASE read)
88
99Before starting, it's necessary to know the architecture and application flow to avoid confusion when certain things don't work or work unexpectedly. So please spend some time reading this section.
1010
1111### Application
1212
1313Application here refers to the protocol that your app will implement. fpWeb will happily switch from CGI, FCGI, Apache module to embedded server, and more if fcl-web implements another one in the future. Each application is implemented in its own unit, so to switch from one application to another, with the exception of Apache module, one just needs to change the respective identifier in the uses clause. Currently (as of 3.0.0 / 3.1.1), they are:
1414
1515- fpCGI -\> CGI
1616- fpFCGI -\> FastCGI
1717- fpApache (requires httpd as well) -\> Apache module
1818- fpHttpApp -\> embedded server
1919- microhttpapp -\> embedded server using GNU libmicrohttp library.
2020- fphttpsys -\> Windows system support for HTTP protocol.
2121
2222Throughout this tutorial, we will use embedded server for the sake of simplicity because you don't have to deal with setting up virtual server and messing up with complicated configuration file and service management. Your app will be a single binary portable web application! Another reason would be there are more than just one web server application out there and each has different way to configure. It would be overkill to cover all of them while their documentation already does the job. Apache module is implemented as a (dynamic) library, while other protocols are normal application. Each application may have specific properties (such as port) available and meaningful only to that application. That's why if you look at fcl-web examples, .lpi / .lpr pairs for each protocol are put in their own directories, only the web modules are shared.
2323
2424### Web Modules
2525
2626![](/wiki-images/5/50/FpWeb-overview.png)
2727
2828fpWeb applications consist of web modules which do the actual content production. A web module can contain web actions that can divide the functionality even more. For instance, an auth web module might have login and logout web actions. While an about web module might not need an action at all and serves just one content. Web module is integrated with fpTemplate that can be used for producing dynamic content from a template file. This is roughly similar to what PHP does, only the gap between logic and presentation is forced rather than suggested. Some say that fpTemplate implements passive view while PHP by default implements an active view design pattern.
2929
3030## Installation
3131
3232The fpWeb package for Lazarus is not installed by default (but it's shipped), to enable fpWeb:
3333
34341. Open Lazarus and choose **Package-\>Install/Uninstall Package**
35352. In the Available for installation listbox, look for **weblaz** and press **Install selection**. Press **Save and rebuild IDE** and confirm with **Continue**
36363. Let the IDE rebuild and restart itself. If everything goes well, you should now have fpWeb tab in the component palette, as shown below:
3737
3838
3939
4040![](/wiki-images/5/55/Fpwebtutorial_inst.png)
4141
4242## Specialized modules
4343
4444The *TFPWebModule* class (used below) is a simple example of a fpWEB module that can be used for all kinds of HTTP requests.
4545
4646However, fpWEB comes with some specialized modules, that have extra support for specialized tasks:
4747
4848- The **TSimpleFileModule** class in unit *fpwebfile.pp* can be used to send files. You point it to a directory, and it does the rest.
4949- The **TFPHTMLModule** class in unit *fphtml.pp* can be used to produce HTML.
5050- The **TProxyWebModule** class in unit *fpwebproxy.pp* is a ready-made forwarding proxy.
5151- The **TFPWebProviderDataModule** class in unit *fpwebdata.pp* serves data in JSON format that can be consumed by ExtJS stores.
5252- The **TSQLDBRestModule** class in unit *sqldbrestmodule.pp* implements a full REST server backed by SQLDB. See more info in [[SQLDBRestBridge]].
5353- The **TJSONRPCModule** class in unit *webjsonrpc.pp* implements a JSON-RPC service.
5454- The **TExtDirectModule** class in unit *fpextdirect.pp* implements a Ext.Direct variant of a JSON-RPC service.
5555
5656## Hello, World!
5757
5858Let's create a simple Web Application. As commonly taught when learning programming, "Hello, World!" will be our first app.\
5959
60601\. Open up Lazarus and choose **Project-\>New Project** then pick **HTTP server Application**
6161
6262
6363![Create new HTTP server application](/wiki-images/5/57/Fpwebtutorial_create_app.png)\
6464
65652\. Another dialog shall appear for serving static files, port selection and multithreading. Just use default port 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![](/wiki-images/e/e6/Fpwebtutorial_port_selection.PNG)
7373
7474:**IMPORTANT!:**
7575
7676
7777If you choose to use threads on *nix, don't forget to add cthreads as the first unit in the .lpr's uses clause, otherwise a RTE 232 will be generated. When running from console, a message shall appear:
7878
7979``` pascal
8080This binary has no thread support compiled in. Recompile the application with a thread-driver in the program uses clause before other units using thread.
8181```
8282
83833\. Since Jan 14, 2017 (or FPC 3.0.4), you may need to open the .lpr and add the following line in the main body if it's not already there:
8484
8585
8686``` pascal
8787Application.LegacyRouting := true;
8888```
8989
9090reason will be explained in chapter #Routing.
9191
92924\. Whatever you choose, pick "OK" and you'll be presented in the default one module fpWeb app.\
93935\. Focus the module and move to **Object Inspector**. Feel free to rename the module if you wish.\
94946\. Choose **Events** tab and click the button at the right of the second column of the **OnRequest** row to create the event handler.
9595
9696
9797![Creating web module's OnRequest handler in the object inspector](/wiki-images/8/8d/Fpwebtutorial_on_request.png)\
9898
9999
100100You will be redirected to the source editor with the following code:
101101
102102:
103103
104104``` pascal
105105procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
106106AResponse: TResponse; var Handled: Boolean);
107107begin
108108 |
109109end;
110110```
111111
112112
113113Fill in the event with:
114114
115115:
116116
117117``` pascal
118118procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
119119AResponse: TResponse; var Handled: Boolean);
120120begin
121121 AResponse.Content := 'Hello, World!';
122122 Handled := true;
123123end;
124124```
125125
1261267\. Then run your app (or press F9).\
1271278\. Open your browser and type:
128128
129129
130130<http://localhost:8080/>\
131131
1321329\. You should see "Hello, World!" displayed.\
133133
134134If it doesn't, check below:
135135
136136- The framework does a lot of exception handling and the IDE's debugger might catch them and interrupts your app. It's OK to add most of the exceptions to the ignore list so you can concentrate more on your app flow. Keep skipping and continue until no more dialog appears and the browser shows the output.
137137
138138- **Handled := true** is the way we tell the framework that the request has been handled. Not setting it (or setting it to **false**) will show error page instead. For now, this doesn't affect the request flow yet, but it will be later on. So keep it that way until the time comes to further make a good use of it.
139139
140140- other track: test without a firewall loaded in RAM (as an application, as a service or deamon, or as both).
141141
142142## Reading GET & POST data
143143
144144A dynamic content is likely to be triggered from user input, either through forms, providing values in the URL, etc. Those data are sent along the request, which is represented in the method as **ARequest** parameter of type **TRequest**.
145145
146146### Reading GET
147147
148148GET data is provided as **ARequest.QueryFields** , which is a **TStrings** descendant. In short, whatever you usually do with TStrings, is applicable here such as accessing the data in a map style through the **Values** property.
149149
150150Reusing above code, replace the method body with:
151151
152152``` pascal
153153procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
154154 AResponse: TResponse; var Handled: Boolean);
155155var
156156 LName: String;
157157begin
158158 LName := ARequest.QueryFields.Values['Name'];
159159 if LName = EmptyStr then
160160 with AResponse.Contents do
161161 begin
162162 Add('<form action="' + ARequest.URI + '" method="GET"');
163163 Add('<label for="name">Please tell me your name:</label>');
164164 Add('<input type="text" name="name" id="name" />');
165165 Add('<input type="submit" value="Send" />');
166166 Add('</form>');
167167 end
168168 else
169169 AResponse.Content := 'Hello, ' + LName + '!';
170170 Handled := true;
171171end;
172172```
173173
174174**ARequest.URI** is just a convenience to refer to the current URI, so even when you change your registered module or action name, this code stays the same.
175175
176176Note that as in Pascal, referring to the data is done case insensitively.
177177
178178Now you can try requesting /, which will display
179179
180180` Please tell me your name`
181181
182182and /?name=\<write anything here, e.g.: Bob\>, which will display
183183
184184` Hello, Bob!`
185185
186186### Reading POST
187187
188188POST is actually doesn't differ much from GET, only differs in which property to access. If GET is accessed through **ARequest.QueryFields** , POST is accessed through **ARequest.ContentFields**. POST style of previous code is:
189189
190190``` pascal
191191procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
192192 AResponse: TResponse; var Handled: Boolean);
193193var
194194 LName: String;
195195begin
196196 LName := ARequest.ContentFields.Values['Name'];
197197 if LName = EmptyStr then
198198 with AResponse.Contents do
199199 begin
200200 Add('<form action="' + ARequest.URI + '" method="POST"');
201201 Add('<label for="name">Please tell me your name:</label>');
202202 Add('<input type="text" name="name" id="name" />');
203203 Add('<input type="submit" value="Send" />');
204204 Add('</form>');
205205 end
206206 else
207207 AResponse.Content := 'Hello, ' + LName + '!';
208208 Handled := true;
209209end;
210210```
211211
212212### Reading File Uploads
213213
214214One exception is for reading **multipart/form-data** fields, i.e. files. That one is available in **ARequest.Files** as a **TUploadedFiles** instance, which is a **TCollection** descendant. The following is TUploadedFiles public interface which you can use to access the files:
215215
216216``` pascal
217217TUploadedFiles = Class(TCollection)
218218...
219219public
220220 Function First: TUploadedFile;
221221 Function Last: TUploadedFile;
222222 Function IndexOfFile(AName: String) : Integer;
223223 Function FileByName(AName: String) : TUploadedFile;
224224 Function FindFile(AName: String) : TUploadedFile;
225225 Property Files[Index: Integer] : TUploadedFile read GetFile Write SetFile; default;
226226end;
227227```
228228
229229Each **TUploadedFile** itself has several properties:
230230
231231``` pascal
232232TUploadedFile = Class(TCollectionItem)
233233...
234234Public
235235 Destructor Destroy; override;
236236 Property FieldName: String Read FFieldName Write FFieldName;
237237 Property FileName: String Read FFileName Write FFileName;
238238 Property Stream: TStream Read GetStream;
239239 Property Size: Int64 Read FSize Write FSize;
240240 Property ContentType: String Read FContentType Write FContentType;
241241 Property Disposition: String Read FDisposition Write FDisposition;
242242 Property LocalFileName: String Read FLocalFileName Write FLocalFileName;
243243 Property Description: String Read FDescription Write FDescription;
244244end;
245245```
246246
247247They should be descriptive enough, with the exception of **FileName** and **LocalFileName**. **FileName** is the original file **name** as uploaded from client, **LocalFileName** is the file **path** in the server where the file is temporarily stored. Note the difference in bold terms above.
248248
249249Again, reusing the same request handler:
250250
251251``` pascal
252252procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
253253 AResponse: TResponse; var Handled: Boolean);
254254var
255255 n: Integer;
256256 f: TUploadedFile;
257257 i: Integer;
258258begin
259259 n := ARequest.Files.Count;
260260 if n = 0 then
261261 with AResponse.Contents do
262262 begin
263263 Add('<form id="form" action="' + ARequest.URI + '" method="POST" enctype="multipart/form-data">');
264264 Add('<label for="name">Drag n drop or click to add file:</label>');
265265 Add('<input type="file" name="input" />');
266266 Add('<input type="submit" value="Send" />');
267267 Add('</form>');
268268 end
269269 else
270270 begin
271271 f := ARequest.Files[0];
272272 AResponse.Contents.LoadFromStream(f.Stream);
273273 end;
274274 Handled := true;
275275end;
276276```
277277
278278drag n drop a file (preferably text, as it's will be rendered as text) to the input file field (or click the respective button) then click **Send** button. The file content should be displayed.
279279
280280## Cookies
281281
282282### Setting
283283
284-<img src="cookie_session_Object_Pascal.png" title="The &quot;cookie&quot; concept, invented by Netscape in 1994, to allow the HTTP server to identify all its clients." width="450" alt="The &quot;cookie&quot; concept, invented by Netscape in 1994, to allow the HTTP server to identify all its clients." />\
284+![The "cookie" concept, invented by Netscape in 1994, to allow the HTTP server to identify all its clients.](cookie_session_Object_Pascal.png "The "cookie" concept, invented by Netscape in 1994, to allow the HTTP server to identify all its clients.")\
285285Cookies are browser responsibility to save and keep, therefore server need to send it as part of the response in order to set one. **AResponse.Cookies** contains a list of cookies to be sent. It's a descendant of **TCollection**, respectively the contained **TCookie** is a descendant of **TCollectionItem**. Therefore, you can use TCollection way of managing items to manipulate it.
286286
287287Here's an example:
288288
289289``` pascal
290290procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
291291 AResponse: TResponse; var Handled: Boolean);
292292var
293293 C: TCookie;
294294begin
295295 C := AResponse.Cookies.Add;
296296 C.Name := 'mycookie';
297297 C.Value := 'somevalue';
298298 Handled := true;
299299end;
300300```
301301
302302You won't see any output in your browser. But if you use some kind of developer tools (Chrome has one built-in), you can see the response header:
303303
304304![](/wiki-images/6/69/Fpwebtutorial_cookie_set.png)
305305
306306Note that cookie has attributes, so Name and Value is not the only two you can set. Browse TCookie interface to see what properties are supported.
307307
308308### Getting
309309
310310Once you give **Set-Cookie** header above, subsequent request to your site will contain additional header containing the value you ask to set previously:
311311
312312![](/wiki-images/9/93/Fpwebtutorial_cookie_get.png)
313313
314314Fortunately, the way to read it is no different from GET & POST data. The related property is **ARequest.CookieFields**. To read previously set cookie:
315315
316316``` pascal
317317procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
318318 AResponse: TResponse; var Handled: Boolean);
319319begin
320320 AResponse.Contents.Add('<p>Cookie get: ' + ARequest.CookieFields.Values['mycookie'] + '</p>');
321321 Handled := true;
322322end;
323323```
324324
325325## Sessions
326326
327327TFPWebModule is a descendant of TSessionHTTPModule, so it has session management capability. Session is module based, so each module may choose to use or not to use session management.
328328
329329Session is implemented in abstract manner. By default, no implementation is provided. One sample implementation using .ini files is given in **iniwebsession** unit. You must have this unit in your project or implement one for session management to work. If you decide to implement one, basically you need to extend and implement abstract methods in **TCustomSession** and **TSessionFactory** classes.
330330
331331### Activating
332332
333333To activate session management, set **CreateSession** property to true. Session will be started prior to request handling. In case of a new session, **OnNewSession** will be called. Initialize your session variables here.
334334
335335### Session Variables Manipulation
336336
337337Session variables are provided as **Session.Variables** (nb: the Session object is the equivalent of the \$_SESSION array used in PHP). This is a string to string map like structure, so you can read / write it like:
338338
339339``` pascal
340340Session.Variables['myvar'] := myvar; // write
341341...
342342myvar := Session.Variables['myvar']; // read
343343```
344344
345345Setting a variable to empty string does **NOT** remove it. If you really want to remove a variable, call **Session.RemoveVariable** instead.
346346
347347### Terminating
348348
349349Call **Session.Terminate** whenever you want to terminate a session (e.g.: user logout). Session will also automatically expire if the next request comes after **Session.TimeOutMinutes** since last request. When session terminates, **OnSessionExpired** will be called. Do whatever cleanup you need there.
350350
351351## Routing
352352
353353Since FPC 3.0.4, a new routing mechanism has been implemented. Instead of maintaining backward compatibility, it is decided that the new routing will be the default. Thus, any old code  (or new code depending on old routing) must be ported by adding:
354354
355355``` pascal
356356Application.LegacyRouting := true;
357357```
358358
359359in the .lpr.
360360
361361### Old Mechanism
362362
363363#### Using Multiple Modules
364364
365365You can have multiple modules in your app. Click "File" menu, then click "New...". A dialog shall appear, select "Web Module" from the treeview.
366366
367367![](/wiki-images/e/e5/Fpwebtutorial_new_module.png)
368368
369369then click OK.
370370
371371With multiple modules exist in your app, you can no longer request just with /. The framework will not be able to magically select which module must serve the response, so there are two ways to state which module you'd like to call:
372372
373373- /\<module name\>
374374- /?module=\<module name\>
375375
376376In the 2nd format, you can change "module" (which is the default value) to whatever valid query string key by modifying **Application.ModuleVariable**.
377377
378378#### Using Actions
379379
380380So far, we've only used web modules with single request handler. This doesn't scale much as your web app gets more and more complex. Moreover, some features might have shared properties and be better logically grouped, e.g.:
381381
382382- Account module
383383 - Login action
384384 - Logout action
385385 - Register action
386386- Product module
387387 - Create action
388388 - Update action
389389 - Delete action
390390 - Details action
391391
392392##### Request Handling Flow
393393
394394Before using action, it is important to know fpWeb request handling flow. Failing to do so might render your action useless because it's always your data module that handles the request. How could that be? Going back a few chapters, remember the **Handled := true** that we always did before? Now this is where **Handled** parameter comes into play.
395395
396396**Every** requests will go through module's **OnRequest** first, regardless the requested action. Only if it does **not** set Handled to true, web action's OnRequest is executed.
397397
398398In general, the request flow is:
399399
400400![](/wiki-images/e/ea/Fpwebtutorial_request_flow.png)
401401
402402Notice the "Our Concern" box, that's what we're going to put our attention at.
403403
404404##### Add Actions to Web Modules
405405
406406To add an action, select the web module then go to object inspector. In the properties tab, select Actions and click the button on the second column.
407407
408408![](/wiki-images/9/95/Fpwebtutorial_manage_actions.png)
409409
410410A popup window shall appear where you can add, delete and reorder actions.
411411
412412![](/wiki-images/d/db/Fpwebtutorial_edit_actions.png)
413413
414414Press Add, a new action shall appear in the list. Select it then go to object inspector. It will currently show properties and events of that newly created action. Rename the Name property (this will be the name you write in the URL, so give it a short, simple but descriptive name) as you wish, I will choose "Hello". Move on to events tab, do the same as OnRequest for module, click button on the right of OnRequest row to create the request handler.
415415
416416![](/wiki-images/2/2e/Fpwebtutorial_action_onrequest.png)
417417
418418You will be presented in the same OnRequest interface, but this one handles web action instead of web module. Whatever you can do in web module's OnRequest can be done here as well. Copy the method body from the "Hello, World!" section.
419419
420420Remember to remove **Handled := true** from the previous web module's **OnRequest** body (or remove the event completely) for the action to take care of the request handling.
421421
422422Run your project, and fire up your browser. Now, since the request handling is delegated to web action, you can no longer just request /, but you need /\<action name\> or \<Module's ActionVar property\>=\<action name\>. Note that \<Module's ActionVar property\> has a default value of empty string, unlike **Application.ModuleVariable** which has "module" as the default value. So, by default, you can only use the /\<action name\> form.
423423
424424If you have multiple modules, then you have a variety of options:
425425
426426- /\<module name\>/\<action name\>
427427- /\<module name\>?action=\<action name\>
428428- /\<action name\>?module=\<module name\>
429429- /?module=\<module name\>&action=\<action name\>
430430
431431Note that as soon as a module have at least one action, /\<module or action name\> alone will by default map to /\<action name\>. To change the behavior such that it maps to /\<module name\> by default, set **Application.PreferModuleName** to **true**. In case of multiple modules, if no module name given, then the default module will handle the given action. To change the behavior such that module name must be explicitly given, set **Application.AllowDefaultModule** to **false**.
432432
433433The following tables summarize what will happen based on the two properties:
434434
435435<thead>
436436
437437</thead>
438438<tbody>
439439
440440</tbody>
441441
442442##### Default Action
443443
444444Remember the previous diagram, the "Delegate request handling to actions" is actually not so simple, but if we expand that diagram, the image will be too big to fit. So, here's the diagram of that part:
445445
446446![](/wiki-images/4/44/Fpwebtutorial_request_delegation_flow.png)
447447
448448Two important things from the flow: DefActionWhenUnknown and a default action. The former is a web module's property while the latter corresponds to Default property of an action. In the latter case, in case there are more than two actions having Default property set to true, the the action order (as how it's shown in the manage actions popup window) will be considered to decide which is the default action. The two properties forms what the application should do if no matching action found for a given request.
449449
450450The following tables summarize what will happen based on the two properties:
451451
452452<thead>
453453
454454</thead>
455455<tbody>
456456
457457</tbody>
458458
459459<thead>
460460
461461</thead>
462462<tbody>
463463
464464</tbody>
465465
466466In case of error response above, a stack trace shall follow, complete with line number information if you build your app with -gl. We'll see later on how to create custom handler for this (stacktrace are no good for production). But for now, make sure you understand the concept of web module and web action, especially the request flow. Play around until you think you're ready for next section.
467467
468468### New Mechanism
469469
470470The new mechanism is extremely flexible and works even without data modules (old mechanism only works with data modules).
471471
472472A dedicated unit for this routing is provided as **httproute** (add this to uses clause of program / unit where you want to register routes). The unit contains a function **HTTPRouter** that will return singleton object responsible for application's route management and has **RegisterRoute** method to register your route.
473473
474474#### Route Syntax
475475
476476The first parameter of **HTTPRouter.RegisterRoute** is the route that will be matched against incoming request. It can be as simple as ***** above, which means 0 or more paths or simply any paths, up to as complex as **/api/v1/:resource/*** which means **REQUEST_URI** header should start with **/api/v1/** followed by something else that will be bound to variable named  resource and finally ended with 0 or more paths. It will match:
477477
478478- /api/v1/products
479479- /api/v1/products/1
480480- /api/v1/products/1/clone
481481- /api/v1/products/something/else/that/is/really/long/and/silly
482482
483483but not:
484484
485485- /api/v1
486486- /excuse/me/api/v1/products
487487
488488Basically there are only 3 special characters:
489489
490490- *  denoting 0 or more paths
491491
492492-
493493 param  denoting a part
494494
495495- /  denoting part separator
496496
497497your route will be composed of these 3 characters, plus everything else that made up a route. 
498498
499499#### Registering a Route
500500
501501The 2nd, 3rd or 4th parameter (depending on whether you want to handle specific HTTP method and/or pass additional data to it) of **HTTPRouter.RegisterRoute** is overloaded with several possibilities:
502502
503503- Callback procedure
504504
505505``` pascal
506506TRouteCallback = Procedure(ARequest: TRequest; AResponse);
507507```
508508
509509- Callback event
510510
511511``` pascal
512512TRouteEvent = Procedure(ARequest: TRequest; AResponse) of object;
513513```
514514
515515- Object satisfying a (CORBA) interface
516516
517517``` pascal
518518IRouteInterface = Interface ['{10115353-10BA-4B00-FDA5-80B69AC4CAD0}']
519519 Procedure HandleRequest(ARequest: TRequest; AResponse: TResponse);
520520end;
521521```
522522
523523- Object extending abstract router class
524524
525525``` pascal
526526TRouteObject = Class(TObject, IRouteInterface)
527527Public
528528 Procedure HandleRequest(ARequest: TRequest; AResponse: TResponse); virtual; abstract;
529529end;
530530
531531TRouteObjectClass = Class of TRouteObject;
532532```
533533
534534By default, if 2nd parameter is not a **TRouteMethod**, then all HTTP methods will match. Use one of  **rmUnknown, rmAll, rmGet, rmPost, rmPut, rmDelete, rmOptions, rmHead, rmTrace** to match only a specific HTTP method.
535535
536536Registration order matters. If there are two or more routes matching the current request, the earlier registered one will handle it.
537537
538538In this new mechanism, standalone Hello, World! program can be as simple as:
539539
540540``` pascal
541541uses
542542 fphttpapp, httpdefs, httproute;
543543procedure DoHello(ARequest:TRequest; AResponse : TResponse);
544544begin
545545 AResponse.Content:='<html><body><h1>Hello,World!</h1></body></html>'
546546end;
547547
548548begin
549549 HTTPRouter.RegisterRoute('*', @DoHello);
550550 Application.Port := 9000;
551551 Application.Initialize;
552552 Application.Run;
553553end.
554554```
555555
556556#### Webserver example
557557
558558This is an example of a simple, cross-platform, multi-threaded web server.
559559
560560``` pascal
561561program webserver;
562562
563563{$mode objfpc}{$H+}
564564
565565uses
566566 {$ifdef UNIX}
567567 cthreads, cmem,
568568 {$endif}
569569 fphttpapp, httpdefs, httproute;
570570
571571procedure route1(aReq: TRequest; aResp: TResponse);
572572begin
573573 aResp.content:='<html><body><h1>Route 1 The Default</h1></body></html>'
574574end;
575575
576576procedure route2(aReq: TRequest; aResp: TResponse);
577577begin
578578 aResp.content:='<html><body><h1>Route 2</h1></body></html>'
579579end;
580580
581581begin
582582 HTTPRouter.registerRoute('/', @route1, true);
583583 HTTPRouter.registerRoute('/route2', @route2);
584584 Application.port := 8080;
585585 Application.threaded := true;
586586 Application.initialize;
587587 Application.run;
588588end.
589589```
590590
591591To open "Route 1" type into your browser the following URL: <http://127.0.0.1:8080>
592592
593593To open "Route 2" type into your browser the following URL: <http://127.0.0.1:8080/route2>
594594
595595## Using Templates
596596
597597fpWeb has integrated support for FPTemplate, Free Pascal's generic templating engine. It doesn't have to be used from web application context, but with integrated support things will be easier a bit. At least the memory management can be ignored as the module will take care of it.
598598
599599There are two levels where one can use templates at: action and module. The full RAD support is incomplete, so you need to go down to hand coding at some points.
600600
601601There are two modes of operation: non-parameterized and parameterized. The active mode is controlled by **AllowTagParams** property, which should be obvious enough what value refers to what mode.
602602
603603Template string can be given from a file through **FileName** property or a direct string through **Template** property. Template.Template, I know it sounds weird :) If both are filled then FileName will take precedence.
604604
605605The two properties: **StartDelimiter** and **EndDelimiter** define how the engine should recognize a template tag. For instance, if you have:
606606
607607- StartDelimiter = '{+'
608608- EndDelimiter = '+}'
609609
610610then a string '{+title+}' defines a template tag named 'title'. Note that spaces are significant, so '{+ title +}' defines a template tag named ' title ' instead of just 'title'.
611611
612612Special for parameterized mode, additional three properties: **ParamStartDelimiter**, **ParamEndDelimiter** and **ParamValueSeparator** defines how the engine should recognize a template tag parameter. For instance, if you have:
613613
614614- ParamStartDelimiter = '\[-'
615615- ParamEndDelimiter = '-\]'
616616- ParamValueSeparator = '='
617617
618618then a string '{+data \[-p=v-\]\[-a=b-\] +}' defines a template tag named 'data' with parameter 'p' of value 'v' and parameter 'a' of value 'b'. This can be used to pass template level parameter such as expected date format, header-row-footer for customizable output presentation, name of file, etc. you decide.
619619
620620As a consequence of different way of operation, the core event where the template works is different, too. Non-parameterized will use **OnGetParam** while the parameterized will use **OnReplaceTag**. The two has of course different interface:
621621
622622``` pascal
623623Type
624624 // OnGetParam: for simple template tag support only (ex: {Name})
625625 TGetParamEvent = Procedure(
626626 Sender: TObject;
627627 Const ParamName: String;
628628 Out AValue: String
629629 ) Of Object;
630630 // OnReplaceTag: for tags with parameters support
631631 TReplaceTagEvent = Procedure(
632632 Sender: TObject;
633633 Const TagString: String;
634634 TagParams: TStringList;
635635 Out ReplaceText: String
636636 ) Of Object;
637637```
638638
639639In **OnGetParam**, you check for **ParamName**, then assign **AValue** accordingly. i.e., if you want tag 'title' to be replaced by 'My App', then fill in the method with:
640640
641641``` pascal
642642// use Trim() if you want the spaces around tag to be insignificant
643643case Trim(ParamName) of
644644 'title': AValue := 'My App';
645645else
646646 AValue := 'UNKNOWN';
647647end;
648648```
649649
650650In **OnReplaceTag**, you check for **TagString** and optionally **TagParams**, then assign **ReplaceText** accordingly. i.e., if you want tag 'datetime' to be replaced by current time with parameter 'datetimeformat' to specify how the date and time should be formatted, then fill in the method like this:
651651
652652``` pascal
653653// use Trim() if you want the spaces around tag to be insignificant
654654case Trim(TagString) of
655655 'datetime': AValue := FormatDateTime(TagParams.Values['datetimeformat'],Now);
656656else
657657 AValue := 'UNKNOWN';
658658end;
659659```
660660
661661### At Action Level
662662
663663Create/select an action, then go to object inspector. You will see a subcomponent property named **Template**. This template is a normal **TFPTemplate** instance. Expand it and fill in the properties as explained above. Now go to Events tab, again expand **Template**, you will see the two events. Fill the one based on your value of **AllowTagParams** property.
664664
665665NOTE: If your Lazarus cannot autocomplete the event, try writing the name manually in the edit box then click the ... button. This is a bug in present Lazarus which may be fixed in the future.
666666
667667At this level, template with a content is not automatically set as request handler. It might change in the future, but let's deal with its current state. Fill in **OnRequest** event of the action, then fill it with:
668668
669669``` pascal
670670with Actions.CurrentAction as TFPWebAction do
671671begin
672672 AResponse.Content := Template.GetContent;
673673end;
674674Handled := true;
675675```
676676
677677The cast is required since **CurrentAction** is of type **TCustomWebAction** instead of **TFPWebAction**. Without it, we can't access the **Template** property.
678678
679679### At Module Level
680680
681681At module level, you currently have to do it by hand coding since no RAD support is implemented. The linked property is ModuleTemplate . This is however not a regular **TFPTemplate** instance, but special **TFPWebTemplate** class that's a descendant of it.
682682
683683The idea here is to have module provide a layout while the actions provide content, with the ability to provide additional variables. It's therefore a good idea to keep **AllowTagParams** as is and assign **OnGetParam** event **of the module** only. Do NOT assign **OnGetParam** of **ModuleTemplate** as it will **never** be called.
684684
685685A template tag named 'content' will be replaced automatically by what content produced by action, everything else is either replaced from internal template variables or from **OnGetParam**.
686686
687687### Using Separated Template
688688
689689Despite the somewhat incomplete integration, nothing stops you from using fpTemplate (or any other templating solutions) manually, outside from the integrated support. This could even be better in some cases since it's modular.
690690
691691## Using an html Producer object
692692
693693Assuming that there is a TWebAction named act_get_my_html, you must ask to write in AResponse, the return of an internal bytes stream: when answering with html Producer objects solution, we must use the methods of the writer. It manages a memory stream for a purpose of rapidity. No string type here, like with the above fpTemplate solution which uses text \\ string processing as in the regular Php way. The Producer writes in AResponse with a recursive " foreach " iteration, that traverses the polymorphic objects HTML Dom tree, composed with the hierarchy THtmlCustomElement = Class(TDOMElement) Classes (see unit htmlelements.pp). So, the AResponse is written by the parameter aWriter, with the following call:
694694
695695``` pascal
696696procedure TFPWebModule1.act_get_my_htmlRequest(Sender: TObject; ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
697697begin
698698 (* ask HTMLEntityProducer1 for the content of its html DOM elements: *)
699699 HTMLEntityProducer1.HandleRequest(ARequest, AResponse, Handled);
700700 Handled := True;
701701end;
702702```
703703
704704Said again, it's the job of the Producer to convert its internal memory stream into text in the response object: that's how this pattern was designed, although there is a text-html rendering method named ProduceContent for debugging purposes only. You can override this method if you are writing or debugging a Producer component.
705705
706706Here is an example, always with an html Producer object, allowing to create a web application in the "RAD way", i.e. with the drag-drop of the HTMLEntityProducer component from the pallet:
707707
708708``` pascal
709709procedure TFPWebModule1.HTMLEntityProducer1WriteEntity(Sender: THTMLContentProducer; aWriter: THTMLWriter);
710710begin
711711
712712 aWriter.startHeader;
713713 aWriter.Meta('','Content-Type','text/html; charset=UTF-8');
714714 aWriter.title('My web page');
715715 aWriter.link('stylesheet','stylesheet.css','text/css','screen');
716716 aWriter.EndHeader;
717717
718718 aWriter.Startbody;
719719 aWriter.Startparagraph;
720720 aWriter.heading2('Hello, world from inside §1:');
721721 aWriter.Text('Here is text written inside the current paragraph.');
722722 aWriter.Endparagraph;
723723 aWriter.paragraph('This is another text written inside a self "started and ended" paragraph.');
724724 aWriter.Startparagraph;
725725 aWriter.heading2('Hello, world from inside §2:');
726726 aWriter.Text('Here is the final text.');
727727 aWriter.Image.src := 'logo.png';
728728 AnotherProducer.WriteContent(aWriter);
729729 aWriter.Endparagraph;
730730 aWriter.Endbody;
731731
732732end;
733733```
734734
735735The TWebAction.ContentProducer property allows to couple a THTTPContentProducer to its web action exposed via a URI on the net.
736736
737737\[ToDo: there is no official documentation about the components "RAD way" of fpWeb (html producer, html provider, html adapter, html formatter, etc)\]
738738
739739## Tips and Tricks
740740
741741### Returning Different HTTP Response Code
742742
743743By default, fpWeb will return HTTP 200 OK to indicate successful request handling. This surely is not always the case, as user input might not be as what we expected. To do so, set **AResponse.Code** in your request handler to the code you want to return.
744744
745745### Redirect Request to Different URL
746746
747747A common flow after a successful login is to redirect user to his account page. This can be done by calling AResponse.SendRedirect in your request handler, supplying the URL to redirect request to.
748748
749749### Serving Static Files (Embedded Web Server)
750750
751751Remember the dialog in the #Hello, World! section after you select HTTP server Application? If you tick "Register location to serve files from" you can fill "Location" (the URI segment, must not contain any slashes) and "Directory" (physical directory in your computer, must exist at runtime) and the wizard will simply add:
752752
753753``` pascal
754754RegisterFileLocation('<Location>','<Directory>');
755755```
756756
757757to the beginning of your .lpr and add the unit **fpwebfile** to the uses clause. You can actually do this by hand anytime and also register multiple times for different locations / directories. After this you can request **/\<Location\>/\<any filename under Directory\>** and it will be served automatically. Note that the mimetype of the file is determined by fpmimetypes. Call **MimeTypes.LoadFromFile** with your mime.types file in order to give correct mimetype based on its extension. Otherwise, the file will always be served as **application/octet-stream** which means the browser will download it instead of interpreting it (especially important for JavaScript and CSS files).
758758
759759You can grab a complete mime.types here <http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/conf/mime.types?&view=co>
760760
761761In Lazarus 2.0.6 or newer you must add at the top of your program the full path of the mime.types file
762762
763763``` pascal
764764begin
765765 MimeTypesFile := Application.Location + 'mime.txt';
766766```
767767
768768Take into consideration that the default path coming with the project is lib\\\$(TargetCPU)-\$(TargetOS)
769769
770770For example httpproject\lib\i386-win32\mime.txt
771771
772772### Centralize Management of Configuration and Modules
773773
774774By default, the program file (.lpr) is the one that contains protocol unit. This limits the ability to use **Application** object from other contexts such as from web modules. Fortunately, it's not difficult to refactor to have what we want. We remove **RegisterHTTPModule** calls from web modules' units and left out the .lpr to empty main block with single unit identifier in the uses clause, we name it: brokers. The unit contains:
775775
776776``` pascal
777777unit Brokers;
778778
779779{$mode objfpc}{$H+}
780780
781781interface
782782
783783{ $define cgi}
784784{ $define fcgi}
785785{$define httpapp}
786786
787787uses
788788 CustWeb;
789789
790790function GetApp: TCustomWebApplication; inline;
791791
792792implementation
793793
794794uses
795795 {$ifdef cgi}fpcgi{$endif}
796796 {$ifdef fcgi}fpfcgi{$endif}
797797 {$ifdef httpapp}fphttpapp{$endif}
798798 ,webmodule1
799799 ,webmodule2
800800 ;
801801
802802function GetApp: TCustomWebApplication;
803803begin
804804 Result := Application;
805805end;
806806
807807initialization
808808 RegisterHTTPModule('wm1', TWebModule1);
809809 RegisterHTTPModule('wm2', TWebModule2);
810810 {$ifndef cgi}
811811 Application.Port := 2015;
812812 {$endif}
813813 Application.Initialize;
814814 Application.Run;
815815end.
816816```
817817
818818This way, we can control over web module registration and also provide an API to get **Application** object (casted as **TCustomWebApplication**), while still easily switch between protocol implementations, in a single place.
819819
820820### Terminating Gracefully (FastCGI / Embedded Web Server)
821821
822822Instead of Ctrl+C-ing your app, there is a way for your app to terminate gracefully, doing whatever cleanup it needs, by calling **Application.Terminate**. You might need to use previous trick to easily access the **Application** object. A common implementation is to provide a specific password protected module / action that calls the Terminate method. You may choose whatever way you want, though.
823823
824824### Custom Exception Handler
825825
826826\[edit the May 1, 2020 =\> moved from a method pointer to a simple procedure.\]
827827
828828To override the default exception handler, which prints stacktrace whenever an exception is raised (i.e.: on HTTP 404 or 500), and thus not good for production, you must assign **Application.OnShowRequestException**.
829829
830830This is a method so you will need to provide your procedure that implements the method and assign it by using the object. i.e.: if you have **MyExceptionHandler** as an object of **TMyExceptionHandler** which has **MyShowRequestException** method, you can assign it by:
831831
832832``` pascal
833833Application.OnShowRequestException := @MyExceptionHandler.MyShowRequestException;
834834```
835835
836836don't forget to **.Create()** **MyExceptionHandler** BEFORE assigning above or you will get an **EAccessViolation**!
837837
838838You must provide your global procedure that implements your own exception handler (in production, it is advisable to replace the call stack by an HTTP status code and its explanation). Then, you can override the default exception handler, by assigning it like this:
839839
840840``` pascal
841841Application.OnShowRequestException := @MyShowRequestException;
842842```
843843
844844### Pure Hand Coding (No Form Designer Required)
845845
846846It's not a must to use Lazarus' form designer to write an fpWeb application. You can use pure hand coding technique to write it. The secret lies in the 3rd parameter of **RegisterHTTPModule** : **SkipStreaming**. When this parameter is set to true, fpWeb will not search for .lfm resource. Therefore everything must be manually handled: property settings, event handlers, action registration, etc.
847847
848848Note that it's logical to do what's usually done through object inspector in an overriden constructor. Inside it, call the inherited constructor supplying both AOwner and CreateMode as parameters. After that you can set properties, assign event handlers, etc. Example:
849849
850850``` pascal
851851type
852852 THelloWorldModule = class(TFPWebModule)
853853 constructor CreateNew(AOwner: TComponent; CreateMode: Integer); override;
854854 procedure Request(Sender: TObject; ARequest: TRequest;
855855 AResponse: TResponse; var Handled: Boolean);
856856 end;
857857
858858constructor THelloWorldModule.CreateNew(AOwner: TComponent; CreateMode: Integer);
859859begin
860860 inherited CreateNew(AOwner,CreateMode);
861861 OnRequest := @Request;
862862end;
863863
864864procedure THelloWorldModule.Request(Sender: TObject; ARequest: TRequest;
865865 AResponse: TResponse; var Handled: Boolean);
866866begin
867867 AResponse.Content := 'Hello, World!';
868868 Handled := true;
869869end;
870870```
871871
872872## See also
873873
874874- [Possibly same content](https://github.com/leledumbo/fpWebTutorial)
875875- [Lazarus for the web (tutorial)](https://www.freepascal.org/~michael/articles/#web1)
876876- [Lazarus for the web: Sessions and Templates (tutorial)](https://www.freepascal.org/~michael/articles/#web2) This article explains a TSQLDBSession Class that manages a SQL connection, that inherits from a web session (in order to manage the histories of web session variables, within a connection-session to a database having row-locking capabilities).
877877- the examples - read the text files - in your directory ...\fpc\x.x.x\source\packages\fcl-web\examples.\
878878
879-<img src="cgi-listrecords.png" title="Screenshot copy of one of the examples" width="50" />
879+![](/wiki-images/c/c0/Cgi-listrecords.png)
880880
881881- XML or JSON, on ExtJS: if you want to use a more or less complex engine that is very "accomplished" regarding the rendering aspect of Javascript objects on the browser side, in addition to their functionalities (like ExtJS, for example, to display a db-aware grid, dynamic graphics, etc), this kind of solution very very often expects an XML or a JSON file. There are Adapter and Formatter classes (e.g. the classes TExtJSXMLWebdataInputAdaptor, TExtJSJSONDataFormatter).
882882
883883Overall, the Adapter classes adapt the nodes of an incoming jSon or XML to a mapping of their TField database fields. And Formatter classes are mappers of each TField of a record to its node in jSon or XML format, before sending it out towards the browser.\
884884* [Web data formats in Lazarus/FPC](https://www.freepascal.org/~michael/articles/#webdata)
885885
886886- [Practical GUI construction in ExtJS](https://www.freepascal.org/~michael/articles/#extjs1)
887887- [Mixing ExtJS webapplications and Pascal: ExtPascal](https://www.freepascal.org/~michael/articles/#extpascal)
888888
889889---
890890*From the Free Pascal wiki, page "fpWeb Tutorial", as it stood on 2023-03-17 after a change by Trev, 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