Ir al contenido



Foto

Llamadas CORS (Cross-Origin) en Datasnap


  • Por favor identifícate para responder
8 respuestas en este tema

#1 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 05 agosto 2016 - 04:37

Hola amigos, al añadir validación de usuarios/manejo de sesiones a un Servidor Datasnap REST que estoy escribiendo me ha surgido un problema, y espero que me podáis sugerir alguna forma de atacarlo.

 

Los clientes acceden al servidor Datasnap mediante AngularJS, y como el servidor IIS que corre el módulo ISAPI del Datasnap y el servidor del front-end AngularJS se encuentran en equipos diferentes, el navegador identifica sus llamadas como CORS (Cross-Origin) y bloquea el envío del código de sesión en un Header Pragma.

 

El navegador sustituye el Header Pragma que envía Angular al servidor Datasnap con el correspondiente dssession (código de sesión), sustituyéndolo por un Header Access-Control-Request-Headers con el valor "Pragma" en una solicitud CORS Options (parece que lo llaman preflight). Datasnap no sabe como responder a ello y genera un "command closed or unassigned" TDSServiceException.

 

Al no ver ninguna solución, he intentado sortearlo, evitando el paso de esa sesión mediante un Header personalizado (que es lo que dispara la solicitud de permisos CORS), y enviando ese código de sesion directamente por la URL, capturandolo al entrar en el Datasnap y volviendo a poner manulamente ese código de sesión dentro de un Header Pragma para que Datasnap pueda manejar las sesiones con su mecanismo ya predefinido.

 

Para ello he modificado el evento WebModuleBeforeDispath :


delphi
  1. procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  2. var Token: string;
  3. begin
  4. Response.SetCustomHeader('Access-Control-Allow-Origin','*'); // Allow CORS calls
  5.  
  6. Token := TIdHTTPAppRequest(Request).Query; // Set session on Pragma from the URL
  7. if Copy(Token, 1, 10) = 'dssession=' then begin
  8. TIdHTTPAppRequest(Request).GetRequestInfo.RawHeaders.AddValue('Pragma', Token);
  9. end;
  10.  
  11. if FServerFunctionInvokerAction <> nil then
  12. FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
  13. end;

Esto funciona bien en mi entorno de pruebas, como aplicación StandAlone. Pero cuando lo compilo como módulo ISAPI para ponerlo en el servidor de producción, parece que no construye correctamente esos headers Pragma con los códigos de sesión.

 

El caso es que no estoy muy seguro de donde está exactamente el problema, ya que no podido activar la depuración de ese módulo ISAPI, pero yo imagino que no recupera correctamente el código de sesión de la URL, no le debe aparece disponible en Request.Query (o Request.Content).

 

¿ Alguno de vosotros se ha tenido que pelear con algo parecido ?.

 

Gracias.


  • 0

#2 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.130 mensajes
  • LocationMéxico

Escrito 05 agosto 2016 - 07:30

Hola Marc, 

 

El request.content lo puedes obtener desde el evento HTTPSoapPascalInvoker1BeforeDispatchEvent() en el evento WebModuleBeforeDispatch() no está disponible.

 

Algo así


delphi
  1. procedure TWebModule3.HTTPSoapPascalInvoker1BeforeDispatchEvent(
  2. const MethodName: string; const Request: TStream);
  3. begin
  4. fRequest := TStringList.Create;
  5. fRequest.LoadFromStream(Request);
  6. fRequest.SaveToFile(Ruta + 'Request.xml');
  7. end; 

Saludos


  • 0

#3 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 05 agosto 2016 - 10:33

Hola Marc, 

 

El request.content lo puedes obtener desde el evento HTTPSoapPascalInvoker1BeforeDispatchEvent() en el evento WebModuleBeforeDispatch() no está disponible.

 

Algo así


delphi
  1. procedure TWebModule3.HTTPSoapPascalInvoker1BeforeDispatchEvent(
  2. const MethodName: string; const Request: TStream);
  3. begin
  4. fRequest := TStringList.Create;
  5. fRequest.LoadFromStream(Request);
  6. fRequest.SaveToFile(Ruta + 'Request.xml');
  7. end; 

Saludos

 

Muchas gracias, es muy interesante lo que dices, lamentablemente el servidor REST Datasnap no tiene un HTTPSoapPascalInvoker1, el lunes voy a repasar los otros componentes que sí estan disponibles (WebFileDispatcher1, ...) y tienen un evento BeforeDispatch.


  • 0

#4 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 12 agosto 2016 - 02:51

Finalmente he encontrado una buena solución para este problema, programando el Servidor Datasnap para que responda como se supone que debe responder a las solicitudes CORS.


delphi
  1. procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  2. begin
  3. Response.SetCustomHeader('Access-Control-Allow-Origin','*');
  4.  
  5. if Trim(Request.GetFieldByName('Access-Control-Request-Headers')) <> '' then begin
  6. Response.SetCustomHeader('Access-Control-Allow-Headers', Request.GetFieldByName('Access-Control-Request-Headers'));
  7. Handled := True;
  8. end;
  9.  
  10. if FServerFunctionInvokerAction <> nil then
  11. FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
  12. end;

Cuando se encuentra una solicitud CORS Access-Control-Request-Headers notificando que se pretende envir un Header personalizado, le devolvemos una respuesta Access-Control-Allow-Headers para confirmar que deseamos recibirlo.

 

Es importante establecer Handled a True, para se de por terminada esta interacción y Datasnap no intente seguir tratándola como la llamada normal a un método.


  • 1

#5 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.409 mensajes
  • LocationRepública Dominicana

Escrito 09 julio 2018 - 01:37

Tú última solución me acaba de funcionar con éxito!, Gracias Marc.

 

Finalmente he encontrado una buena solución para este problema, programando el Servidor Datasnap para que responda como se supone que debe responder a las solicitudes CORS.


delphi
  1. procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  2. begin
  3. Response.SetCustomHeader('Access-Control-Allow-Origin','*');
  4.  
  5. if Trim(Request.GetFieldByName('Access-Control-Request-Headers')) <> '' then begin
  6. Response.SetCustomHeader('Access-Control-Allow-Headers', Request.GetFieldByName('Access-Control-Request-Headers'));
  7. Handled := True;
  8. end;
  9.  
  10. if FServerFunctionInvokerAction <> nil then
  11. FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
  12. end;

Cuando se encuentra una solicitud CORS Access-Control-Request-Headers notificando que se pretende envir un Header personalizado, le devolvemos una respuesta Access-Control-Allow-Headers para confirmar que deseamos recibirlo.

 

Es importante establecer Handled a True, para se de por terminada esta interacción y Datasnap no intente seguir tratándola como la llamada normal a un método.


  • 0

#6 ELKurgan

ELKurgan

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 554 mensajes
  • LocationEspaña

Escrito 09 julio 2018 - 11:08

Gracias por compartir la solución

 

:ap: 

 

Un saludo


  • 0

#7 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.409 mensajes
  • LocationRepública Dominicana

Escrito 18 julio 2018 - 01:26

Bueno, sigo teniendo inconvenientes con esto del CORS.. ya seguiré indagando.

 

Saludos.


  • 0

#8 andresrobanesky

andresrobanesky

    Newbie

  • Miembros
  • Pip
  • 1 mensajes

Escrito 15 abril 2019 - 04:58

Saludos desde Colombia.  Amigoooo Me salvaste mi vida, De verdad mil gracias por este gran aporteeee.

 

Éste if salvó mi vida 

 

if Trim(Request.GetFieldByName('Access-Control-Request-Headers')) <> '' then begin
Response.SetCustomHeader('Access-Control-Allow-Headers', Request.GetFieldByName('Access-Control-Request-Headers'));
Handled := True;
end;

 

 

Tenia problemas en la authenticación con seguridad básica desde front-end angular 7. Cuando enviaba las peticiones el componente authenticationmanager no tomaba el usuario y el password. Estaba dando mil vueltas en Angular creyendo que el problema era de Angular cuando era de Delphi. De verdad mil y mil gracias.

 

 

 

 

 

Finalmente he encontrado una buena solución para este problema, programando el Servidor Datasnap para que responda como se supone que debe responder a las solicitudes CORS.


delphi
  1. procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
  2. begin
  3. Response.SetCustomHeader('Access-Control-Allow-Origin','*');
  4.  
  5. if Trim(Request.GetFieldByName('Access-Control-Request-Headers')) <> '' then begin
  6. Response.SetCustomHeader('Access-Control-Allow-Headers', Request.GetFieldByName('Access-Control-Request-Headers'));
  7. Handled := True;
  8. end;
  9.  
  10. if FServerFunctionInvokerAction <> nil then
  11. FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
  12. end;

Cuando se encuentra una solicitud CORS Access-Control-Request-Headers notificando que se pretende envir un Header personalizado, le devolvemos una respuesta Access-Control-Allow-Headers para confirmar que deseamos recibirlo.

 

Es importante establecer Handled a True, para se de por terminada esta interacción y Datasnap no intente seguir tratándola como la llamada normal a un método.


  • 1

#9 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.130 mensajes
  • LocationMéxico

Escrito 15 abril 2019 - 05:49

Hola andresrobanesky

 

Bienvenido a delphiaccess, que bien que te ha servido éste hilo. (y)

 

Saludos


  • 0