Javascript 为什么在DataSnap REST服务器上两次调用OnUserAuthenticate?

Javascript 为什么在DataSnap REST服务器上两次调用OnUserAuthenticate?,javascript,delphi,xmlhttprequest,delphi-xe,datasnap,Javascript,Delphi,Xmlhttprequest,Delphi Xe,Datasnap,我在DelphiXE中使用DataSnap创建了一个RESTWeb服务。我使用XMLHttpRequestJavaScript对象调用服务器方法。我在XMLHttpRequest Open方法的第四个和第五个可选参数中传递用于身份验证的用户名和密码 当我在Delphi中加载DataSnap服务器并将调试器连接到IIS(我使用的是IIS 7.5)时,我可以看到第一次调用其中一个服务器方法会导致DSAuthenticationManager的OnUserAuthentication事件被调用两次 在

我在DelphiXE中使用DataSnap创建了一个RESTWeb服务。我使用XMLHttpRequestJavaScript对象调用服务器方法。我在XMLHttpRequest Open方法的第四个和第五个可选参数中传递用于身份验证的用户名和密码

当我在Delphi中加载DataSnap服务器并将调试器连接到IIS(我使用的是IIS 7.5)时,我可以看到第一次调用其中一个服务器方法会导致DSAuthenticationManager的OnUserAuthentication事件被调用两次

在第一次调用中,OnUserAuthenticate事件处理程序的用户和密码参数是空字符串。在第二次调用中,我在XMLHttpRequest打开方法中传递的用户名和密码出现在这些参数中

一旦用户通过身份验证,使用XMLHttpRequest对任何服务器方法的任何后续调用都会导致对OnUserAuthenticate事件处理程序的单个调用,用户的用户名和密码分别出现在user和password参数中

我已经检查了许多DataSnap类的源代码,但没有找到会造成这种影响的代码,这让我认为这种行为可能起源于浏览器或IIS

为什么在第一次服务器方法调用时两次调用OnUserAuthenticate,是什么产生了这种效果


为了回应Mat DeLong的回答,我展示了一个用于调用服务器方法的通用函数。此特定方法进行同步调用。baseURL参数通常是类似于的字符串,方法可能是myservermethod。其他参数用于将参数传递给服务器方法:

function getJSONSync(baseURL, method) {
  var request = new XMLHttpRequest();
  var url = baseURL + method;
  for (var i= 2; i < arguments.length; i++) {
    url += "/" + arguments[i];
  }
  request.open("GET", encodeURI(url), false);
  request.send(null);
  if (request.status != 200) {
    throw new Error(request.statusText);
  } 
  return jQuery.parseJSON(request.responseText);
}
函数getJSONSync(baseURL,方法){ var request=new XMLHttpRequest(); var url=baseURL+方法; for(var i=2;i
编辑:Lex Li似乎是正确的,IIS仅在第一次请求失败时使用基本身份验证。此外,OnUserAuthenticate在每个会话中只能调用一次也是正确的。在检查了他提供的链接之后,很明显我没有捕获并返回会话id。下面是一个代码示例,这次是异步的。它捕获sessionid并将其存储在隐藏字段中。然后,它在Pragma头中的每个后续调用中将该会话id传递回

function getJSONAsync(callback, baseURL, method) {
  var request = new XMLHttpRequest();
  var timedout = false;
  request.onreadystatechange = function() {
     if (request.readyState !== 4) { return }
    if (timedout) { return }
    if (request.status >= 200 && request.status < 300)
    {
      callback(jQuery.parseJSON(request.responseText));
      //store the sessionid in a hidden field
      $("#sessionid").val(request.getResponseHeader('Pragma').split(',')[0].split("=")[1]);
    }
  }
  var url = baseURL + method;
  for (var i= 3; i < arguments.length; i++) {
    url += "/" + arguments[i];
  }
  request.open("GET", encodeURI(url), true);
  //pass the sessionid in the Pragma header, if available
  if (! $("#sessionid").val() == "") {
    request.setRequestHeader("Pragma", "dssession="+$("#sessionid").val());
  }
  //time out if request does not complete in 10 seconds
  var timer = setTimeout(function() { timedout = true; request.abort(); }, 10000);
  request.send(null);
}
函数getJSONAsync(回调、baseURL、方法){ var request=new XMLHttpRequest(); var-timedout=false; request.onreadystatechange=函数(){ if(request.readyState!==4){return} if(timedout){return} 如果(request.status>=200&&request.status<300) { 回调(jQuery.parseJSON(request.responseText)); //将sessionid存储在隐藏字段中 $(“#sessionid”).val(request.getResponseHeader('Pragma').split(',')[0]。split(“=”[1]); } } var url=baseURL+方法; for(var i=3;i

编辑:当我第一次发布这两个代码示例时,我在XMLHttpRequest打开方法的第四个和第五个参数中包含了示例用户名和密码。在测试期间,我明确地传递了这些值。不建议像这样传递参数,因此我在这次编辑中删除了这些参数。如果省略这些密码,并且不使用其他方法传递它们(例如在第一个REST服务器方法调用的头中),浏览器将显示一个对话框,询问用户此信息。提供用户名和密码后,浏览器将在会话的其余部分记住此信息。如果关闭然后重新打开浏览器并调用另一个REST服务器方法,您将再次被询问用户名和密码。当然,这是假设您通过OnUserAuthenticate事件处理程序拒绝无效用户。

我无法确定为什么您在没有看到服务器代码和XHR使用的确切JavaScript的情况下看到这种行为。OnUserAuthenticate旨在为会话调用一次

您可以在此处看到XE DataSnap REST协议:

第一个调用将返回会话ID,所有未来的调用都将指定该ID。如果完成此操作,则除第一个调用外,所有调用都使用给定的会话ID跳过身份验证过程,并且仅在分配了OnUserAuthorize事件的情况下转到OnUserAuthorize事件

如果您看到OnUserAuthenticate被调用了两次,那么这很可能是一个bug,除非它在XHR或IIS配置中有些奇怪


如果您能提供更详细的信息,我很乐意仔细查看。

嗯,这是其他技术(如ASP.NET)的典型IIS行为

初始请求不包含您的用户凭据,并触发401响应。然后第二个请求发送您的用户凭据,然后IIS可以返回200

不确定这是否适用于DataSnap,但如果捕获网络数据包,您可能会发现它


Mat:谢谢你的回复。我将DataSnap服务器作为纯REST服务器调用,而不使用任何代理生成的代码。例如,我直接使用XMLHttpRequest(我没有使用pro