Difference between revisions of "fcl-web"
(→Notes) |
|||
Line 232: | Line 232: | ||
== See also == | == See also == | ||
* [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications | * [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications | ||
+ | * [[CGI_Web_Programming#Debugging_CGI]] Information about debugging CGI applications | ||
[[Category:Free Component Library]] | [[Category:Free Component Library]] |
Revision as of 13:20, 17 February 2013
Template:Web and Networking Programming
What is fpWeb
fpWeb can be used to build cgi applications. We need more details here: what other functionality does it have, how does it compare to other frameworks/tools, e.g. Brook
Using fpWeb together with Lazarus
Installing the fpWeb Lazarus Package
The first step to do is installing the package which comes in the path lazarus/components/fpweb/weblaz.lpg
Creating a cgi application
After 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 bellow, which will create a main cgi project file and a fpweb web module.
The TFPWebModule allows you to manipulate properties and events using the Object Inspector.
To add code to show the page a request handler should be added, by double clicking the OnRequest property in the object inspector, as in the image below:
In 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().
The type of the response should be set in AResponse.ContentType. For HTML pages this should have the value 'text/html;charset=utf-8'. In the end Handled should be set to True to indicate that the request was successfully handled. After adding this code, the web module should look like this:
unit mainpage;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb;
type
{ TFPWebModule1 }
TFPWebModule1 = class(TFPWebModule)
procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;
AResponse: TResponse; var Handled: Boolean);
private
{ private declarations }
public
{ public declarations }
end;
var
FPWebModule1: TFPWebModule1;
implementation
{$R *.lfm}
{ TFPWebModule1 }
procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
AResponse: TResponse; var Handled: Boolean);
begin
AResponse.ContentType := 'text/html;charset=utf-8';
AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
Handled := True;
end;
begin
RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
end.
Deploying this very simple application
Apache, the most popular web server, can be downloaded here: http://httpd.apache.org/download.cgi
The default installation of Apache will treat all files located in it's 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:
# # ScriptAlias: This controls which directories contain server scripts. # ScriptAliases are essentially the same as Aliases, except that # documents in the target directory are treated as applications and # run by the server when requested rather than as documents sent to the # client. The same rules about trailing "/" apply to ScriptAlias # directives as to Alias. # ScriptAlias /cgi-bin/ "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/"
If you place an executable called "mywebpage.cgi" in this directory, then the page can be accessed as http://localhost/cgi-bin/mywebpage.cgi or with the corresponding domain name if accessed remotely.
Formatting the HTML and Reading Query fields
The previous example just showed a plain HTML page. One might wish to e.g.
- dynamically change the HTML page output, and
- as read the variables the browser passed to the webpage in Query fields (in e.g. HTTP GET and HTTP POST actions).
A 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.
Reading GET data
To 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.
The resulting code:
procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
AResponse: TResponse; var Handled: Boolean);
var
HexText, AsciiText: string;
begin
HexText := ARequest.QueryFields.Values['hex'];
AsciiText := HexToAnsii(HexText);
AResponse.ContentType := 'text/html;charset=utf-8';
AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
AResponse.Contents.Text := Format(AResponse.Contents.Text,
[HexText, AsciiText]);
Handled := True;
end;
Reading POST data
Data submitted by POST requests can be obtained from TRequest.Content. It will come without the request headers.
If using other mime-types than 'MULTIPART/FORM-DATA' and 'APPLICATION/X-WWW-FORM-URLENCODED' (the types supported by HTML forms):
- content is only available in TRequest.Content, not TRequest.ContentFields
- use of these mime-types raises an exception for FPC 2.4.2 (fixed in FPC 2.5+).
Note that HTTP POST can also send Query fields (in the URL), and those are accessed by TRequest.QueryFields as explained in the previous section.
procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
AResponse: TResponse; var Handled: Boolean);
var
lData: String;
begin
lData := ARequest.Content;
Using multiple modules
If there is only one module in the web application, all requests will be directed to this module.
As 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'.
FCL-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.
Each module is registered with fcl-web in the initialization section of the unit it is defined in:
RegisterHTTPModule('location', TMyModule);
The module will then be invoked if an URL of the form
http://www.mysite.org/mycgi.cgi/location
or
http://www.mysite.org/mycgi.cgi?module=location
is used.
If multiple modules are present, the name of the module must appear in the URL, or an error will be raised.
This behaviour can also be forced for applications that have only a single module by setting the Application's property AllowDefaultModule to false:
Application.AllowDefaultModule := False;
In that case, the fcl-web application will always require the name of the module in the URL.
The 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
Application.ModuleVariable := 'm';
ensures that the following URL is directed to TMyModule:
http://www.mysite.org/mycgi.cgi?m=location
If 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:
type
TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
Var ModuleClass : TCustomHTTPModuleClass) of object;
Creating 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.
If 'ModuleClass' is 'Nil' on return, an error will be sent to the browser.
Using Actions
A 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:
- Creating
- Deleting
- Editing
- Displaying
These different actions can be handled by the same module. One can determine the action from the URL manually, as in:
http://mysite/mycgi.cgi/user?action=delete&id=12
This can be automated.
In 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:
- Name
- The name of the action. The URL will be examined to determine the name of the action.
- Content
- A string. If set, this is sent to the browser.
- Contents
- A stringlist. If set, this is sent to the browser.
- ContentProducer
- If set, the contentproducer will handle the request.
- Default
- if 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.
- Template
- If set, this template will be processed, and the results sent to the browser.
There are also some events:
- BeforeRequest
- executed before the request is processed. Can be used to set the 'Content' or other properties.
- AfterRequest
- executed after the request is processed.
- OnRequest
- an event handler to handle the request. If set, the handler is used to handle the request.
Again, 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:
http://mysite/mycgi.cgi/user/delete&id=12
would execute the action named 'delete'. This is equivalent to the URL
http://mysite/mycgi.cgi/user?action=delete&id=12
The 'ActionVar' property of the module can be used to set the name of the request variable to use. Setting
UserModule.ActionVar := 'a';
reduces the above URL to
http://mysite/mycgi.cgi/user?a=delete&id=12
If there is only one module in the application, the URL can be shortened to
http://mysite/mycgi.cgi/delete&id=12
Using HTML Templates
For 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/.
Example 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/)
Notes
- The cgiapp unit is deprecated, please use fpcgi as much as possible.
- 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.
- 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.
See also
- fphttpclient Part of fcl-web that can be used stand-alone in client applications
- CGI_Web_Programming#Debugging_CGI Information about debugging CGI applications