Sign in Join
Wiki copytemporary

fcl-web/es: what changed

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

Title: fcl-web/es

OldNewLine
1+## Que es fpWeb
2+
3+fpWeb 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**
4+
5+## Utilizando fpWeb junto con Lazarus
6+
7+### Instalando el paquete weblaz fpWeb para Lazarus
8+
9+El 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.
10+
11+### Creando una aplicación CGI
12+
13+Despué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.
14+
15+![](/wiki-images/e/ec/New_cgi.PNG)
16+
17+TFPWebModule permite manipular las propiedades y eventos utilizando el Inspector de Objetos.
18+
19+Para 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:
20+
21+![](/wiki-images/3/3d/Webmodule.PNG)
22+
23+En 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().
24+
25+El tipo de respuesta debe establecerse en AResponse.ContentType. Para páginas HTML debe tener el valor 'text/html;charset=utf-8'.
26+
27+Finalmente, 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:
28+
29+``` pascal
30+unit mainpage;
31+
32+{$mode objfpc}{$H+}
33+
34+interface
35+
36+uses
37+ Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb;
38+
39+type
40+
41+ { TFPWebModule1 }
42+
43+ TFPWebModule1 = class(TFPWebModule)
44+ procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;
45+ AResponse: TResponse; var Handled: Boolean);
46+ private
47+ { private declarations }
48+ public
49+ { public declarations }
50+ end;
51+
52+var
53+ FPWebModule1: TFPWebModule1;
54+
55+implementation
56+
57+{$R *.lfm}
58+
59+{ TFPWebModule1 }
60+
61+procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
62+ AResponse: TResponse; var Handled: Boolean);
63+begin
64+ AResponse.ContentType := 'text/html;charset=utf-8';
65+ AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
66+ Handled := True;
67+end;
68+
69+begin
70+ RegisterHTTPModule('TFPWebModule1', TFPWebModule1);
71+end.
72+```
73+
74+### Diseñando la aplicación CGI
75+
76+Esta 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).
77+
78+Apache se puede descargar desde: <http://httpd.apache.org/download.cgi> o utilizando el gestor de paquetes de la distribución en uso.
79+
80+La 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:
81+
82+ #
83+ # ScriptAlias: This controls which directories contain server scripts.
84+ # ScriptAliases are essentially the same as Aliases, except that
85+ # documents in the target directory are treated as applications and
86+ # run by the server when requested rather than as documents sent to the
87+ # client. The same rules about trailing "/" apply to ScriptAlias
88+ # directives as to Alias.
89+ #
90+ ScriptAlias /cgi-bin/ "C:/Program Files/Apache Software Foundation/Apache2.2/cgi-bin/"
91+
92+Si 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).
93+
94+fcl-web con Lazarus bajo Windows produce ficheros .exe. Para que Apache sirva estos ficheros hay que añadir esto:
95+
96+ AddHandler cgi-script .exe
97+
98+Y para servir los ejecutables desde otro directorio hay que añadir esto otro:
99+
100+ ScriptAlias /bin/ "C:/lazarus_projects/test-fclweb/bin/"
101+ <Directory "C:/lazarus_projects/test-fclweb/bin/">
102+ AllowOverride None
103+ Options None
104+ Order allow,deny
105+ Allow from all
106+ </Directory>
107+
108+### Formateando el HTML y leyendo campos de consulta (Query)
109+
110+El ejemplo previo simplemente muestra una página en HTML plano, pero se puede desear por ejemplo:
111+
112+- Cambiar dinámicamente la salida de la página HTML, y
113+- 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**).
114+
115+Una 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.
116+
117+### Leyendo datos **GET**
118+
119+Para 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.
120+
121+El código resultante es:
122+
123+``` pascal
124+procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
125+ AResponse: TResponse; var Handled: Boolean);
126+var
127+ HexText, AsciiText: string;
128+begin
129+ HexText := ARequest.QueryFields.Values['hex'];
130+ AsciiText := HexToAnsii(HexText);
131+
132+ AResponse.ContentType := 'text/html;charset=utf-8';
133+ AResponse.Contents.LoadFromFile(ExtractFilePath(ParamStr(0)) + 'mainpage.html');
134+ AResponse.Contents.Text := Format(AResponse.Contents.Text,
135+ [HexText, AsciiText]);
136+ Handled := True;
137+end;
138+```
139+
140+### Leyendo datos **POST**
141+
142+Los 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.
143+
144+Los 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:
145+
146+`login: dfg 345&&`\
147+`login_senha: ====`\
148+`email: dfg`
149+
150+Se codificará en 'APPLICATION/X-WWW-FORM-URLENCODED' tal como esto (Ver [wikipedia POST (HTTP)](http://en.wikipedia.org/wiki/POST_%28HTTP%29)):
151+
152+`login=dfg+345%26%26&login_senha=%3D%3D%3D%3D&email=dfg`
153+
154+Y que estará disponible en TRequest.ContentFields a través del indice de línea o utilizando la propiedad Values, que es más conveniente:
155+
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+
161+Si se utilizan otros tipos MIME distintos de 'MULTIPART/FORM-DATA' y 'APPLICATION/X-WWW-FORM-URLENCODED' (los tipos soportados por formularios HTML):
162+
163+- El contenido estará solamente disponible para TRequest.Content, no para TRequest.ContentFields.
164+- El uso de estos tipos MIME genera una excepción para FPC 2.4.2 (solucionado en FPC 2.5+).
165+
166+Ten 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.
167+
168+``` pascal
169+procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;
170+ AResponse: TResponse; var Handled: Boolean);
171+var
172+ lData: String;
173+begin
174+ lData := ARequest.Content;
175+```
176+
177+### Leyendo datos binarios **POST**
178+
179+Para recibir datos en el servidor que ha sido posteado (**POST**ed), e.g. multipart/form-data, utiliza algo como esto:
180+
181+``` pascal
182+procedure TMainWebModule.TFPWebActions2Request(Sender: TObject;
183+ ARequest: TRequest; AResponse: TResponse; var Handled: Boolean);
184+var
185+ i: Integer;
186+begin
187+ // Procesa todos los ficheros recibidos.
188+ for i := 0 to ARequest.Files.Count - 1 do
189+ begin
190+ // Doing something else than writeln is highly recommended ;)
191+ writeln ('Nombre de fichero recibido: '+ARequest.Files[i].LocalFileName);
192+ end;
193+
194+ Handled := true;
195+end;
196+```
197+
198+El cliente puede enviar datos utilizando e.g. FileFormPost.
199+
200+## Utilizando múltiples módulos
201+
202+Si solamente hay un módulo en la aplicación web entonces todos los requerimientos se dirigirán a dicho módulo.
203+
204+Segú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'.
205+
206+FCL-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.
207+
208+Cada módulo se registra con fcl-web en la sección de inicialización de la unidad que está definida en:
209+
210+``` pascal
211+RegisterHTTPModule('location', TMyModule);
212+```
213+
214+Entonces el módulo será invocado si se utiliza una URL de la forma
215+
216+`http://www.mysite.org/mycgi.cgi/location`
217+
218+o
219+
220+`http://www.mysite.org/mycgi.cgi?module=location`
221+
222+Si están presentes múltipls módulos entonces debe aparecer el nombre del módulo en la URL, caso contrario se generará un error.
223+
224+Este 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.
225+
226+``` pascal
227+Application.AllowDefaultModule := False;
228+```
229+
230+En este caso, la aplicación fcl-web requerirá siempre el nombre del módulo en la URL.
231+
232+El 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:
233+
234+``` pascal
235+Application.ModuleVariable := 'm';
236+```
237+
238+asegura que la siguiente dirección es dirigida a TMyModule:
239+
240+`http://www.mysite.org/mycgi.cgi?m=location`
241+
242+Si 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:
243+
244+``` pascal
245+type
246+
247+ TGetModuleEvent = Procedure (Sender : TObject; ARequest : TRequest;
248+ Var ModuleClass : TCustomHTTPModuleClass) of object;
249+```
250+
251+Crear 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.
252+
253+Si 'ModuleClass' vale 'Nil' al ser retornada, entonces se enviará un error al navegador.
254+
255+## Utilizando acciones (Actions)
256+
257+Un 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:
258+
259+- Creación.
260+- Eliminación.
261+- Edición.
262+- Presentación.
263+
264+Estas distintas acciones se pueden manejar mediante un mismo módulo. Uno puede determinar la acción desde la URL manualmente, como en:
265+
266+`http://mysite/mycgi.cgi/user?action=delete&id=12`
267+
268+Esto se puede automatizar.
269+
270+En 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:
271+
272+Name
273+El nombre de la acción. Examinará la URL para determinar el nombre de la acción.
274+
275+Content
276+Una cadena (string). Si se establece entonces se enviará al visor web (navegador).
277+
278+Contents
279+Un stringlist. Si se establece también se enviará al visor web (navegador).
280+
281+ContentProducer
282+Si se establece hará que el contentproducer maneje el requerimiento.
283+
284+Default
285+Si 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.
286+
287+Template
288+If set, this template will be processed, and the results sent to the browser.
289+
290+There are also some events:
291+
292+BeforeRequest
293+executed before the request is processed. Can be used to set the 'Content' or other properties.
294+
295+AfterRequest
296+executed after the request is processed.
297+
298+OnRequest
299+an event handler to handle the request. If set, the handler is used to handle the request.
300+
301+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:
302+
303+`http://mysite/mycgi.cgi/user/delete&id=12`
304+
305+would execute the action named 'delete'.
306+
307+La propiedad 'ActionVar' del módulo puede utilizarse para estableceer el nombre del la variable de requerimiento a usar. Estableciendo
308+
309+``` pascal
310+UserModule.ActionVar := 'a';
311+```
312+
313+puede utilizarse para cambiar esa URL a
314+
315+http://mysite/mycgi.cgi/user?a=delete&id=12
316+
317+If there is only one module in the application, the URL can be shortened to
318+
319+`http://mysite/mycgi.cgi/delete&id=12`
320+
321+## Using HTML Templates
322+
323+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/.
324+
325+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/)
326+
327+## Tips & troubleshooting
328+
329+### Enviando códigos de resultado
330+
331+Para enviar una respuesta HTTP distinta de 200 OK, utiliza **AResponse.Code** y **AResponse.CodeText**, e.g.
332+
333+``` pascal
334+AResponse.Code:=404;
335+AResponse.CodeText:='Documento no encontrado';
336+```
337+
338+### Enviando datos binarios
339+
340+Una 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:
341+
342+``` pascal
343+var
344+
345+AResponse.ContentStream := TMemoryStream.Create;
346+try
347+ AResponse.ContentStream.LoadFromFile('/tmp/sample.tiff');
348+ AResponse.ContentType := 'image/tiff'; //or whatever MIME type you want to send
349+// to do: there is an fpweb example that gets the mime type from the file extension...
350+ AResponse.ContentLength:=AResponse.ContentStream.Size; //apparently doesn't happen automatically?
351+ AResponse.SendContent;
352+finally
353+ AResponse.ContentStream.Free;
354+end;
355+Handled := true;
356+```
357+
358+### Error: Could not determine HTTP module for request
359+
360+You may have multiple modules and multiple actions. If you specify an URL with only 1 item, like:
361+
362+```html4strict
363+http://localhost/cgi-bin/somemodule
364+```
365+
366+then 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)
367+
368+You can modify this behaviour to let fpweb map to a module name instead by setting the application's **PreferModuleName** property to true.
369+
370+### Error: response code 500 Internal Server error when trying to handle DELETE method
371+
372+In FPC 2.6.2 and lower, fcl-web does not accept the DELETE method and generates an error.
373+
374+### fpWeb/FCGI and Apache 2.4 (mod_proxy_fcgi mode)
375+
376+When 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.
377+
378+1, Enable following modules: setenvif_module, proxy_module and proxy_fcgi_module\
379+(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).
380+
381+ LoadModule setenvif_module modules/mod_setenvif.so
382+ LoadModule proxy_module modules/mod_proxy.so
383+ LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
384+
385+2, Reverse proxy configuration for fpWeb/FCGI listening on port 9000.\
386+\$1 = module name\
387+\$2 = action name
388+
389+ SetEnvIf Request_URI . proxy-fcgi-pathinfo=full
390+ ProxyPassMatch "/fcgi/(.*)/(.*)" "fcgi://127.0.0.1:9000/$1/$2"
391+
392+## Notes
393+
394+- The cgiapp unit is deprecated, please use fpcgi as much as possible.
395+
396+- 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.
397+
398+- 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.
399+
400+## See also
401+
402+- [[fphttpclient]] Part of fcl-web that can be used stand-alone in client applications
403+- *write me!*fphttpserver Small stand alone Object Pascal web server. Can be used to serve fcl-web CGI applications.
404+- [[CGI Web Programming|CGI_Web_Programming#Debugging_CGI]] Information about debugging CGI applications
405+- [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)
406+
407+---
408+*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