Sign in Join
Wiki copytemporary

fcl-web/es: what changed

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

OldNewLine
11## Que es fpWeb
22
33fpWeb se puede utilizar para crear aplicaciones cgi . **En este punto necesitamos más información: que otra funcionalidad tiene, como es en comparación con otros frameworks/tools, e.g. Brook**
44
55## Utilizando fpWeb junto con Lazarus
66
77### Instalando el paquete weblaz fpWeb para Lazarus
88
99El primer paso a realizar es instalar el paquete que viene en el trayecto lazarus/components/fpweb/weblaz.lpk. Tal como es usual con los paquetes en tiempo de diseño, tendrás que reconstruir Lazarus.
1010
1111### Creando una aplicación CGI
1212
1313Después de instalar el paquete weblaz se puede crear una aplicación CGI (Common Gateway Interface) muy simple, la cual muestra una página HTML. Para ello vamos a menu "File-\>New...". Del listado de las posibles aplicaciones seleccionamos "CGI Application" como en la imagen de abajo, lo cual creará un fichero de proyecto principal CGI y un módulo web fpweb.
1414
1515![](/wiki-images/e/ec/New_cgi.PNG)
1616
1717TFPWebModule permite manipular las propiedades y eventos utilizando el Inspector de Objetos.
1818
1919Para añadir código para mostrar la página se debe añadir un manejador (handler) de peticiones. Esto se consigue haciendo doble click en la propiedad OnRequest del inspector de objetos, tal como se muestra en la siguiente imagen:
2020
2121![](/wiki-images/3/3d/Webmodule.PNG)
2222
2323En el manejador de eventos se debe escribir el código HTML que se quiere mostrar en el navegador. Para evitar mezclar Pascal y HTML, se puede cargar esta página del directorio en el que se encuentra el ejecutable CGI mediante el uso de AResponse.Contents.LoadFromFile().
2424
2525El tipo de respuesta debe establecerse en AResponse.ContentType. Para páginas HTML debe tener el valor 'text/html;charset=utf-8'.
2626
2727Finalmente, el manejador (handled) debería establecerse a True para indicar que la peticion fue manejada satisfactoriamente y retornar un código (200 OK status) al navegador. Después de añadir este código el módulo web debería mostrarse como:
2828
2929``` pascal
3030unit mainpage;
3131
3232{$mode objfpc}{$H+}
3333
3434interface
3535
3636uses
3737 Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb;
3838
3939type
4040
4141 { TFPWebModule1 }
4242
4343 TFPWebModule1 = class(TFPWebModule)
4444 procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;
4545 AResponse: TResponse; var Handled: Boolean);
4646 private
4747 { private declarations }
4848 public
4949 { public declarations }
5050 end;
5151
5252var
5353 FPWebModule1: TFPWebModule1;
5454
5555implementation
5656
5757{$R *.lfm}
5858
5959{ TFPWebModule1 }
6060
6161procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
6262 AResponse: TResponse; var Handled: Boolean);
6363begin
6464 AResponse.ContentType := 'text/html;charset=utf-8';
6565 AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
6666 Handled := True;
6767end;
6868
6969begin
7070 RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
7171end.
7272```
7373
7474### Diseñando la aplicación CGI
7575
7676Esta sección asume que se está usando un Servidor Web Apache. Por supuesto también se pueden utilizar otros servidores web que soporten CGI (nginx, cherokee).
7777
7878Apache se puede descargar desde: <http://httpd.apache.org/download.cgi> o utilizando el gestor de paquetes de la distribución en uso.
7979
8080La instalación por defecto de Apache tratará todos los ficheros ubicados en su directorio cgi-bin como programas CGI, por lo que los usuarios no serán capaces de acceder a ficheros en formato plano HTML que se encuentren en el. Este directorio puede establecerse en el fichero httpd.conf dentro de la siguiente sección:
8181
8282 #
8383 # ScriptAlias: This controls which directories contain server scripts.
8484 # ScriptAliases are essentially the same as Aliases, except that
8585 # documents in the target directory are treated as applications and
8686 # run by the server when requested rather than as documents sent to the
8787 # client. The same rules about trailing "/" apply to ScriptAlias
8888 # directives as to Alias.
8989 #
9090 ScriptAlias /cgi-bin/ "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/"
9191
9292Si tu situas un ejecutable llamado "mywebpage.cgi" en este directorio, entonces la página será accesible comos <http://localhost/cgi-bin/mywebpage.cgi> (o remotamente con la dirección IP o nombre de dominio correspondientes).
9393
9494fcl-web con Lazarus bajo Windows produce ficheros .exe. Para que Apache sirva estos ficheros hay que añadir esto:
9595
9696 AddHandler cgi-script .exe
9797
9898Y para servir los ejecutables desde otro directorio hay que añadir esto otro:
9999
100100 ScriptAlias /bin/ "C:/lazarus_projects/test-fclweb/bin/"
101101 <Directory "C:/lazarus_projects/test-fclweb/bin/">
102102 AllowOverride None
103103 Options None
104104 Order allow,deny
105105 Allow from all
106106 </Directory>
107107
108108### Formateando el HTML y leyendo campos de consulta (Query)
109109
110110El ejemplo previo simplemente muestra una página en HTML plano, pero se puede desear por ejemplo:
111111
112112- Cambiar dinámicamente la salida de la página HTML, y
113113- En cuanto el navegador lee las variables las pasa a la página web en formato de campos de consulta (en e.g. acciones HTTP **GET** y HTTP **POST**).
114114
115115Una solución simple para este primer problema consiste simplemente en utilizar el formato de rutina estandar de Pascal y añadir **%s** o **%d** en el fichero HTML en los lugares apropiados en los que recibirá un valor personalizado.
116116
117117### Leyendo datos **GET**
118118
119119Para leer las variables GET se puede utilizar ARequest.QueryFields, que es un descendiente de TStrings. Cada variable estará en una línea separada del TStrings en el formato variablename=value, de manera similar a como se muestran en la línea de direcciones del navegador. Para buscar por una variable específica se puede utilizar ARequest.QueryFields.Values\[\], pasando el nombre de la variable entre corchetes para recibir de vuelta su valor, en lugar de pasar la cadena manualmente.
120120
121121El código resultante es:
122122
123123``` pascal
124124procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
125125 AResponse: TResponse; var Handled: Boolean);
126126var
127127 HexText, AsciiText: string;
128128begin
129129 HexText := ARequest.QueryFields.Values['hex'];
130130 AsciiText := HexToAnsii(HexText);
131131
132132 AResponse.ContentType := 'text/html;charset=utf-8';
133133 AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
134134 AResponse.Contents.Text := Format(AResponse.Contents.Text,
135135 [HexText, AsciiText]);
136136 Handled := True;
137137end;
138138```
139139
140140### Leyendo datos **POST**
141141
142142Los datos enviados por solicitueds **POST** se puede obtener de `TRequest.Content`. Nos retornará sin las cabeceras de requerimiento - en otras palabras contendrá el cuerpo (BODY) del requerimiento.
143143
144144Los datos enviados por el formulario también pueden ser accedidos mediante `TRequest.ContentFields` el cual es el contenido pasado como campos separados por & y decodificados. Por ejemplo, el siguiente conjunto de valores de formularo:
145145
146-`login: dfg 345&&`\
147-`login_senha: ====`\
148-`email: dfg`
146+```text
147+login: dfg 345&&
148+login_senha: ====
149+email: dfg
150+```
149151
150152Se codificará en 'APPLICATION/X-WWW-FORM-URLENCODED' tal como esto (Ver [wikipedia POST (HTTP)](http://en.wikipedia.org/wiki/POST_%28HTTP%29)):
151153
152-`login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg`
154+```text
155+login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg
156+```
153157
154158Y que estará disponible en TRequest.ContentFields a través del indice de línea o utilizando la propiedad Values, que es más conveniente:
155159
156-`TRequest.ContentFields[0]: login=dfg 345&&`\
157-`TRequest.ContentFields[1]: login_senha=====`\
158-`TRequest.ContentFields[2]: email=dfg`\
159-`TRequest.ContentFields.Values['email']: dfg`
160+```text
161+TRequest.ContentFields[0]: login=dfg 345&&
162+TRequest.ContentFields[1]: login_senha=====
163+TRequest.ContentFields[2]: email=dfg
164+TRequest.ContentFields.Values['email']: dfg
165+```
160166
161167Si se utilizan otros tipos MIME distintos de 'MULTIPART/FORM-DATA' y 'APPLICATION/X-WWW-FORM-URLENCODED' (los tipos soportados por formularios HTML):
162168
163169- El contenido estará solamente disponible para TRequest.Content, no para TRequest.ContentFields.
164170- El uso de estos tipos MIME genera una excepción para FPC 2.4.2 (solucionado en FPC 2.5+).
165171
166172Ten en cuenta que HTTP POST puede además enviar campos de consulta (en la URL), e.g. <http://server/bla?question=how>, y estos son accesible mediante TRequest.QueryFields tal como se explicó en la sección previa.
167173
168174``` pascal
169175procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
170176 AResponse: TResponse; var Handled: Boolean);
171177var
172178 lData: String;
173179begin
174180 lData := ARequest.Content;
175181```
176182
177183### Leyendo datos binarios **POST**
178184
179185Para recibir datos en el servidor que ha sido posteado (**POST**ed), e.g. multipart/form-data, utiliza algo como esto:
180186
181187``` pascal
182188procedure TMainWebModule.TFPWebActions2Request(Sender: TObject;
183189 ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
184190var
185191 i: Integer;
186192begin
187193 // Procesa todos los ficheros recibidos.
188194 for i := 0 to ARequest.Files.Count - 1 do
189195 begin
190196 // Doing something else than writeln is highly recommended ;)
191197 writeln ('Nombre de fichero recibido: '+ARequest.Files[i].LocalFileName);
192198 end;
193199
194200 Handled := true;
195201end;
196202```
197203
198204El cliente puede enviar datos utilizando e.g. FileFormPost.
199205
200206## Utilizando múltiples módulos
201207
202208Si solamente hay un módulo en la aplicación web entonces todos los requerimientos se dirigirán a dicho módulo.
203209
204210Según tu aplicación web vaya creciendo, probablemente será necesario utilizar más módulos. Para esto se pueden añadir nuevos módulos mediante 'File - New' y después 'Web module' o 'HTML Web Module'.
205211
206212FCL-web utiliza la URL para determinar como será manejado el requerimiento HTTP. Debe conocer de antemano que módulos web existen en la aplicación. Para lograr esto, cada módulo debe ser registrado.
207213
208214Cada módulo se registra con fcl-web en la sección de inicialización de la unidad que está definida en:
209215
210216``` pascal
211217RegisterHTTPModule('location', TMyModule);
212218```
213219
214220Entonces el módulo será invocado si se utiliza una URL de la forma
215221
216-`http://www.mysite.org/mycgi.cgi/location`
222+```text
223+http://www.mysite.org/mycgi.cgi/location
224+```
217225
218226o
219227
220-`http://www.mysite.org/mycgi.cgi?module=location`
228+```text
229+http://www.mysite.org/mycgi.cgi?module=location
230+```
221231
222232Si están presentes múltipls módulos entonces debe aparecer el nombre del módulo en la URL, caso contrario se generará un error.
223233
224234Este comportamiento puede también forzarse por parte de las aplicaciones que solamente tienen un módulo mediante el establecimiento de la propiedad AllowDefaultModule de la aplicación.
225235
226236``` pascal
227237Application.AllowDefaultModule := False;
228238```
229239
230240En este caso, la aplicación fcl-web requerirá siempre el nombre del módulo en la URL.
231241
232242El nombre de la variable requerida que determina el nombre del módulo (por defecto, este es 'module') se puede establecer en la propiedad Application.ModuleVariable. El siguiente código:
233243
234244``` pascal
235245Application.ModuleVariable := 'm';
236246```
237247
238248asegura que la siguiente dirección es dirigida a TMyModule:
239249
240-`http://www.mysite.org/mycgi.cgi?m=location`
250+```text
251+http://www.mysite.org/mycgi.cgi?m=location
252+```
241253
242254Si todo esto no es suficiente para determnar el módulo al cual se debe pasar el requerimiento, se puede utilizar el evento Application.OnGetModule. Es del tipo TGetModuleEvent:
243255
244256``` pascal
245257type
246258
247259 TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
248260 Var ModuleClass : TCustomHTTPModuleClass) of object;
249261```
250262
251263Crear un manejador de evento para este evento permite afinar el control sobre el módulo que se crea para manejar el requerimiento: el requerimiento (pasado en ARequest) se puede examinar, y la variable 'ModuleClass' se debe establecer a la clase del módulo que deba manejar el requerimiento.
252264
253265Si 'ModuleClass' vale 'Nil' al ser retornada, entonces se enviará un error al navegador.
254266
255267## Utilizando acciones (Actions)
256268
257269Un módulo puede utlizarse para agrupar cierto tipo de acciones que lógicamente permanecen unidas. Imagina un módulo TUserModule que sea utilizado para manejar la gestión de usuario en páginas web. Puede haber múltiples acciones asociadas con un usuaro:
258270
259271- Creación.
260272- Eliminación.
261273- Edición.
262274- Presentación.
263275
264276Estas distintas acciones se pueden manejar mediante un mismo módulo. Uno puede determinar la acción desde la URL manualmente, como en:
265277
266-`http://mysite/mycgi.cgi/user?action=delete&id=12`
278+```text
279+http://mysite/mycgi.cgi/user?action=delete&id=12
280+```
267281
268282Esto se puede automatizar.
269283
270284En orden a hacer más fácil el distinguir entre varias acciones, el módulo tiene una propiedad actions: es una colección en la que cada elemento se asocia con una respuesta distinta para el requerimiento. Estas acciones tienen varias propiedades:
271285
272286Name
273287El nombre de la acción. Examinará la URL para determinar el nombre de la acción.
274288
275289Content
276290Una cadena (string). Si se establece entonces se enviará al visor web (navegador).
277291
278292Contents
279293Un stringlist. Si se establece también se enviará al visor web (navegador).
280294
281295ContentProducer
282296Si se establece hará que el contentproducer maneje el requerimiento.
283297
284298Default
285299Si se establece a 'True' entonces esta será la acción por defecto. Esto significa que si FCL-Web no puede determinar el nombre de la acción entonces se ejecutará esta acción.
286300
287301Template
288302If set, this template will be processed, and the results sent to the browser.
289303
290304There are also some events:
291305
292306BeforeRequest
293307executed before the request is processed. Can be used to set the 'Content' or other properties.
294308
295309AfterRequest
296310executed after the request is processed.
297311
298312OnRequest
299313an event handler to handle the request. If set, the handler is used to handle the request.
300314
301315Again, 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:
302316
303-`http://mysite/mycgi.cgi/user/delete&id=12`
317+```text
318+http://mysite/mycgi.cgi/user/delete&id=12
319+```
304320
305321would execute the action named 'delete'.
306322
307323La propiedad 'ActionVar' del módulo puede utilizarse para estableceer el nombre del la variable de requerimiento a usar. Estableciendo
308324
309325``` pascal
310326UserModule.ActionVar := 'a';
311327```
312328
313329puede utilizarse para cambiar esa URL a
314330
315331http://mysite/mycgi.cgi/user?a=delete&id=12
316332
317333If there is only one module in the application, the URL can be shortened to
318334
319-`http://mysite/mycgi.cgi/delete&id=12`
335+```text
336+http://mysite/mycgi.cgi/delete&id=12
337+```
320338
321339## Using HTML Templates
322340
323341For 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/.
324342
325343Example 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/)
326344
327345## Tips & troubleshooting
328346
329347### Enviando códigos de resultado
330348
331349Para enviar una respuesta HTTP distinta de 200 OK, utiliza **AResponse.Code** y **AResponse.CodeText**, e.g.
332350
333351``` pascal
334352AResponse.Code:=404;
335353AResponse.CodeText:='Documento no encontrado';
336354```
337355
338356### Enviando datos binarios
339357
340358Una aproximación que parece funcionar para enviar pongamos por caso un fichero de imagen tiff desde el servidor al cliente - adaptado desde \$(fpcdirectory)\packages\fcl-web\examples\jsonrpc\demo1\wmdemo.pp - es algo así como:
341359
342360``` pascal
343361var
344362
345363AResponse.ContentStream := TMemoryStream.Create;
346364try
347365 AResponse.ContentStream.LoadFromFile('/tmp/sample.tiff');
348366 AResponse.ContentType := 'image/tiff'; //or whatever MIME type you want to send
349367// to do: there is an fpweb example that gets the mime type from the file extension...
350368 AResponse.ContentLength:=AResponse.ContentStream.Size; //apparently doesn't happen automatically?
351369 AResponse.SendContent;
352370finally
353371 AResponse.ContentStream.Free;
354372end;
355373Handled := true;
356374```
357375
358376### Error: Could not determine HTTP module for request
359377
360378You may have multiple modules and multiple actions. If you specify an URL with only 1 item, like:
361379
362380```html4strict
363381http://localhost/cgi-bin/somemodule
364382```
365383
366384then 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)
367385
368386You can modify this behaviour to let fpweb map to a module name instead by setting the application's **PreferModuleName** property to true.
369387
370388### Error: response code 500 Internal Server error when trying to handle DELETE method
371389
372390In FPC 2.6.2 and lower, fcl-web does not accept the DELETE method and generates an error.
373391
374392### fpWeb/FCGI and Apache 2.4 (mod_proxy_fcgi mode)
375393
376394When 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.
377395
3783961, Enable following modules: setenvif_module, proxy_module and proxy_fcgi_module\
379397(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).
380398
381399 LoadModule setenvif_module modules/mod_setenvif.so
382400 LoadModule proxy_module modules/mod_proxy.so
383401 LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
384402
3854032, Reverse proxy configuration for fpWeb/FCGI listening on port 9000.\
386404\$1 = module name\
387405\$2 = action name
388406
389407 SetEnvIf Request_URI . proxy-fcgi-pathinfo=full
390408 ProxyPassMatch "/fcgi/(.*)/(.*)" "fcgi://127.0.0.1:9000/$1/$2"
391409
392410## Notes
393411
394412- The cgiapp unit is deprecated, please use fpcgi as much as possible.
395413
396414- 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.
397415
398416- 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.
399417
400418## See also
401419
402420- [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications
403421- *write me!*fphttpserver Small stand alone Object Pascal web server. Can be used to serve fcl-web CGI applications.
404422- [[CGI Web Programming|CGI_Web_Programming#Debugging_CGI]] Information about debugging CGI applications
405423- [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)
406424
407425---
408426*From the Free Pascal wiki, page "fcl-web/es", as it stood on 2020-02-14 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