我有一个 AJAX 请求,它可能有两种可能的结果:
- 服务器响应一条消息,我应该将其放在
<div>
- 服务器以 HTML 页面响应,在这种情况下,我需要用新页面替换当前页面并更改地址(客户端在请求之前知道地址)。
如果我有需要处理这两种情况的 AJAX 请求,解决方案是什么?
url = "http://example.com"
ajax.request(callback)
function callback(response) {
if (case2(response)) {
history.pushState({}, "New page", url);
document.innerHTML = response
} else {
updateDiv(response)
}
}
我对实现第一个分支的正确方法感兴趣,或者服务器是否可以以某种方式编写一个标头,使浏览器将响应作为通常的 HTTP 响应处理并更新页面位置和内容,例如重定向给定内容。
我知道服务器可以返回链接而不是页面,但在这种情况下,客户端上需要一个额外的阶段 - 重定向,然后在服务器上填充新页面。
坦率地说,我认为这种方法基本上被设计打破了。你不应该在那个地方做出这个决定。例如,ajax 响应只能指示应该加载一个全新的页面,然后在对新 URL 的第二个(非 ajax)请求中生成新内容。
如果你被迫采取你已经走的路,并且响应内容不是很大,你可以尝试Javascript-URI。基本上,javascript:"string"
形式的 URI 将加载一个新页面,该字符串是该页面的源代码。因此,如果response
已经是一个字符串,只需将javascript:response
分配给window.location.href
就足够了。也许你必须事先做一些逃跑。而且我不知道这种方法的跨浏览器兼容性如何。
<a href="javascript:response">load</a>
也是可能的。
其变体是不使用变量名称构建 URL,而是使用实际的字符串数据。喜欢
function source2url(src) {
// make valid javascript string from source text
var esc1 = src
.replace(/''/g, '''''')
.replace(/''/g, '''''')
.replace(/'x0A/g, '''x0A')
.replace(/'x0D/g, '''x0D');
// make valid url from that
return "javascript:'" + encodeURIComponent(esc1) + "'";
}
window.location.href = source2url(response);
当然,这将生成相当大的 URI。而且地址栏中将始终包含Javascript-URI。
更新
类似的方法是在数据 URI 中使用 base64 编码。维基百科条目解释了它是如何工作的,包括一个javascript示例。但是,您必须以某种方式对内容进行base64编码。(注意:您可以使用带或不带 base64 编码的数据 URI。您必须查看是什么为您的特定内容提供了更短的 URI。
我曾经遇到过类似的问题。返回了完整的错误页面,而不是简单的 HTML 代码段。我们最终通过更改逻辑来解决此问题,但这是我找到的解决方案之一:
document.open();
document.write(responseText);
document.close();
我们放弃这个的原因是在IE上存在一些问题。我没有浪费任何时间来调查原因,但是在尝试写入字符串时,它抛出了"访问被拒绝"异常。我认为有一些<meta>
标签使IE感到困惑,或者可能是条件注释,我不确定。(当我使用一些简单的页面时,它起作用了...
底线是:你不应该这样做,但如果你没有其他事情可以做(比如返回一个 url 字符串),上面的代码可能会起作用。
如果响应是有效的XML,这真的很容易。
var new_doc = (new DOMParser).parseFromString(response, "application/xml");
document.replaceChild(document.adoptNode(new_doc.doctype), document.doctype);
document.replaceChild(document.adoptNode(new_doc.documentElement), document.documentElement);
由于请求是为了更新答案,这是我使用 HTML5 的 History API 和 jQuery 的解决方案。通过将PHP和HTML部分合并到一个文件中,它应该可以轻松运行。
我的解决方案允许 AJAX 返回以下内容:
- 通过 AJAX 的消息,用于更新
<div>
容器。 - 一个 URL,它会导致浏览器重定向到 URL
- 一个完整的 HTML 页面,它调用历史记录 API 的
history.pushState()
将当前 URL 添加到浏览器的历史记录中,并将页面上的整个 HTML 替换为从 AJAX 返回的 HTML。
.PHP
这只是通过 AJAX 调用 PHP 脚本时需要返回的示例。它演示如何对标志进行编码以确定 AJAX 调用是否应更新容器或加载新页面,以及如何通过 JSON 到 json_encode 返回其结果。为了完整起见,我将这个脚本测试命名为.php。
<?php
// Random messages to return
$messages = array(
'Stack Overflow',
'Error Message',
'Testing'
);
// If the page was requested via AJAX
if( isset( $_POST['ajax']))
{
$response = array(
'redirect' => // Flag to redirect
( rand() % 2 == 0) ? true : false,
'load_html' => // Flag to load HTML or do URL redirect
( rand() % 2 == 0) ? true : false,
'html' => // Returned HTML
'<html><head><title>AJAX Loaded Title</title></head><body>It works!</body></html>',
'title' => 'History API previous title',
'message' => // Random message
$messages[ (rand() % count( $messages)) ]
);
echo json_encode( $response);
exit;
}
.JS
由于我使用的是jQuery,让我们从它开始。下面提交一个 AJAX POST 到服务器,在 URL test.php 到上面的 PHP 脚本。请注意,它还将 POST 参数ajax
设置为true
,使 PHP 脚本能够检测到它收到了 AJAX 请求。dataType
字段告诉 jQuery 服务器的响应将以 JSON 格式,并且它应该在响应回调中将该 JSON 解码为 JSON 对象。最后,成功接收 AJAX 响应时触发的success
回调根据从服务器发送的标志确定要执行的操作。
$.ajax({
type: 'POST',
url: "/test.php",
data: {ajax : true},
dataType: "json",
success: function( json) {
if( json.redirect) {
if( json.load_html) {
// If the History API is available
if( !(typeof history.pushState === 'undefined')) {
history.pushState(
{ url: redirect_url, title: document.title},
document.title, // Can also use json.title to set previous page title on server
redirect_url
);
}
// Output the HTML
document.open();
document.write( json.html);
document.close();
}
else {
window.location = redirect_url;
}
}
else {
$('#message').html( json.message);
}
},
});
.HTML
这是我测试文件的完整 HTML 源代码。我在FF4 - FF8中对其进行了测试。请注意,jQuery 提供了 ready
方法来防止 JS 在加载 DOM 之前执行。我还使用了Google托管的jQuery,因此您无需将jQuery的副本上传到服务器即可进行测试。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.0/jquery.min.js" type="text/javascript"></script>
<title>Default Page</title>
<script type="text/javascript"">
$( document).ready( function() {
$('#ajax_link').click( function() {
var redirect_url = "/test.php";
$.ajax({
type: 'POST',
url: "/test.php",
data: {ajax : true},
dataType: "json",
success: function( json) {
if( json.redirect) {
if( json.load_html) {
// If the History API is available
if( !(typeof history.pushState === 'undefined')) {
history.pushState(
{ url: redirect_url, title: document.title},
document.title, // Can also use json.title to set previous page title on server
redirect_url
);
}
document.open();
document.write( json.html);
document.close();
}
else {
window.location = redirect_url;
}
}
else {
$('#message').html( json.message);
}
},
});
})
});
</script>
</head>
<body>
<div id="message">The default contents of the message</div>
<a id="ajax_link" href="#">Fire AJAX</a>
</body>
</html>
给正文<body id="page">
一个id,你的另一个div将被<div id="message"></div>
现在你的ajax看起来像
$.ajax({
url:'myAjax.php',
data:{datakey:datavalue},
dataType:"JSON",
success: function (response) {
if(response.message=="your message")
{
$('#message').html(response.content);
}
else
{
$('#page').html(response.content);
}
}
});
T-Bull所说...整个过程在这里是错误的。
你只是把事情复杂化了,你知道事实上:
我知道服务器可以返回链接而不是页面,但是 在这种情况下,客户端上需要一个额外的阶段 - 重定向,然后在服务器上填充新页面。
停止复杂化并开始做好它...
- 客户端第一次打开页面,因此,跟踪它
$_SESSION['tmp_client_id'] = 'client_'.session_id();
如果客户端已经订阅,显然更好,无论如何,将内容放入临时表中或放入另一个会话变量等...... - 客户填写表格;
- 客户提交表格;
- 发出 AJAX 请求;
- 将变量存储在
tmp_client_tbl
$_POST
内,并具有唯一的tmp_client_id
或只是$_SESSION['client_'.session_id()] = json_encode($_POST);
- 结果 #1 ? 在
</div>
中显示消息 - 结果 #2 ? 刷新页面并检查
if( isset($_SESSION['client_'.session_id()])) {
如果是这样,让我们再次显示带有填充字段的表单:} else {
显示空表单; -
SELECT * FROM tmp_client_tbl WHERE tmp_client_id = '{$_SESSION['tmp_client_id']}'
或json_decode($_SESSION['client_'.session_id()]);
-
$form_data = $mysql_rows;
或$json_array;
-
foreach($form_data as $name => $value) { echo "<input name='$name' value='$value' />" }
以忍者的方式,假设您有这种表单构建器数组,其中$form = array('text' => array('name','lastname'), 'select' => array('countries'), ... )
,或者只是通过<input name='lastname' value='{$lastname}' />
,其中字段值预先填充了空变量; - 时间已过,发生错误,浏览器关闭?
session_destroy();
或unset($_SESSION['client_'.session_id()]);