我在 Delphi XE 中使用 DataSnap 创建了一个 REST Web 服务。我正在使用 XMLHttpRequest JavaScript 对象调用服务器方法。我在 XMLHttpRequest Open 方法的第四个和第五个可选参数中传递用户名和密码以进行身份验证。
当我在 Delphi 中加载我的 DataSnap 服务器并将调试器附加到 IIS(我使用的是 IIS 7.5)时,我可以看到第一次调用其中一个服务器方法会导致 DSAuthenticationManager 的 OnUserAuthenticate 事件被调用两次。
在第一次调用中,OnUserAuthenticate 事件处理程序的用户和密码参数是空字符串。在第二次调用中,我在 XMLHttpRequest Open 方法中传递的用户名和密码出现在这些参数中。
一旦用户通过身份验证,任何使用 XMLHttpRequest 对任何服务器方法的后续调用都会导致对 OnUserAuthenticate 事件处理程序的一次调用,用户的用户名和密码分别出现在用户和密码参数中。
我检查了许多 DataSnap 类的源代码,但没有找到会导致这种效果的代码,这使我认为这种行为可能源自浏览器或 IIS。
为什么 OnUserAuthenticate 在第一次服务器方法调用时被调用两次,是什么产生了这种效果?
为了回应 Mat DeLong 的回答,我展示了我用来调用服务器方法的通用函数之一。此特定方法进行同步调用。baseURL 参数通常是类似于http://mydomain.com/myservice/myservice.dll/datasnap/rest/tservermethods1的字符串,方法可能是 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);
}
编辑:Lex Li 似乎是正确的,IIS 仅在第一次请求失败时才使用基本身份验证。此外,Mat 正确的是 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);
}
编辑:当我第一次发布这两个代码示例时,我在 XMLHttpRequest open 方法的第四个和第五个参数中包含了示例用户名和密码。我在测试期间明确地传递了这些值。不建议传递这样的参数,因此我在此编辑中删除了这些参数。如果您省略这些密码,并且不使用其他方法(例如在您的第一个 REST 服务器方法调用的标头中)传递它们,浏览器将显示一个对话框,询问用户此信息。提供用户名和密码后,浏览器将在会话的剩余部分中记住此信息。如果您关闭然后重新打开浏览器并调用另一个 REST 服务器方法,您将再次被要求输入用户名和密码。这当然假设,