老张小站

  1. 欢迎光临

    感谢访问老张的博客!

  • 1
24

Discuz! X 下Ajax返回内容无法显示、页面无法跳转的处理方法

分类 网站技术/村民张先生 发布于 2025-11-18 02:00
0

在部分情况下,例如Chrome 142版本,安装“鼠标手势”扩展后,Discuz登录无跳转(实际已登录成功),管理操作等Ajax浮窗无法显示。可进行如下修改。

1、static/js/common.js 中查找 ajaxpost 函数,将该函数整体替换为以下3个(为现代化浏览器增加xhr方式):

// 旧逻辑:通过隐藏 iframe 提交表单
function ajaxpost_iframe(formid, showid, waitid, showidclass, submitbtn, recall) {
	var waitid = typeof waitid == 'undefined' || waitid === null ? showid : (waitid !== '' ? waitid : '');
	var showidclass = !showidclass ? '' : showidclass;
	var ajaxframeid = 'ajaxframe';
	var ajaxframe = $(ajaxframeid);
	var curform = $(formid);
	var formtarget = curform.target;

	var handleResult = function() {
		var s = '';
		var evaled = false;

		showloading('none');
		try {
			s = $(ajaxframeid).contentWindow.document.XMLDocument.text;
		} catch(e) {
			try {
				s = $(ajaxframeid).contentWindow.document.documentElement.firstChild.wholeText;
			} catch(e) {
				try {
					s = $(ajaxframeid).contentWindow.document.documentElement.firstChild.nodeValue;
				} catch(e) {
					s = '内部错误,无法显示此内容';
				}
			}
		}
		if(s != '' && s.indexOf('ajaxerror') != -1) {
			evalscript(s);
			evaled = true;
		}
		if(showidclass) {
			if(showidclass != 'onerror') {
				$(showid).className = showidclass;
			} else {
				showError(s);
				ajaxerror = true;
			}
		}
		if(submitbtn) {
			submitbtn.disabled = false;
		}
		if(!evaled && (typeof ajaxerror == 'undefined' || !ajaxerror)) {
			ajaxinnerhtml($(showid), s);
		}
		ajaxerror = null;
		if(curform) curform.target = formtarget;
		if(typeof recall == 'function') {
			recall();
		} else {
			eval(recall);
		}
		if(!evaled) evalscript(s);
		ajaxframe.loading = 0;
		if(!BROWSER.firefox || BROWSER.safari) {
			$('append_parent').removeChild(ajaxframe.parentNode);
		} else {
			setTimeout(
				function(){
					$('append_parent').removeChild(ajaxframe.parentNode);
				},
				100
			);
		}
	};
	if(!ajaxframe) {
		var div = document.createElement('div');
		div.style.display = 'none';
		div.innerHTML = '<iframe name="' + ajaxframeid + '" id="' + ajaxframeid + '" loading="1"></iframe>';
		$('append_parent').appendChild(div);
		ajaxframe = $(ajaxframeid);
	} else if(ajaxframe.loading) {
		return false;
	}

	_attachEvent(ajaxframe, 'load', handleResult);

	showloading();
	curform.target = ajaxframeid;
	var action = curform.getAttribute('action');
	action = hostconvert(action);
	curform.action = action.replace(/\&inajax\=1/g, '')+'&inajax=1';
	curform.submit();
	if(submitbtn) {
		submitbtn.disabled = true;
	}
	doane();
	return false;
}

// 新逻辑:优先用 XHR 提交表单(需要配合 ajaxpost_iframe 和 ajaxpost 入口)
function ajaxpost_xhr(formid, showid, waitid, showidclass, submitbtn, recall) {
	if (!window.XMLHttpRequest) {
		return false;
	}

	var waitid = typeof waitid == 'undefined' || waitid === null ? showid : (waitid !== '' ? waitid : '');
	var showidclass = !showidclass ? '' : showidclass;
	var curform = $(formid);
	if (!curform) {
		return false;
	}

	var action = curform.getAttribute('action');
	action = hostconvert(action);
	// 与旧逻辑一致:统一拼接 &inajax=1
	var url = action.replace(/\&inajax\=1/g, '') + '&inajax=1&ajaxv=2';

	var method = (curform.method || 'POST').toUpperCase();
	var xhr;
	try {
		xhr = new XMLHttpRequest();
	} catch (e) {
		return false;
	}

	showloading();
	if (submitbtn) {
		submitbtn.disabled = true;
	}

	// 使用浏览器内建 API 从表单生成 x-www-form-urlencoded 字符串
	var body = '';
	try {
		var fd = new FormData(curform);
		// 仅在 URL 中确实包含 inajax=1 时,过滤掉 loginsubmit 字段
		if (url.indexOf('inajax=1') != -1) {
			fd.delete('loginsubmit');
		}
		var usp = new URLSearchParams(fd);
		body = usp.toString();
	} catch (e) {
		body = '';
	}

	if (method == 'GET') {
		// GET 的话,把 body 拼到 URL 上
		if (body) {
			url += (url.indexOf('?') == -1 ? '?' : '&') + body;
		}
	}

	xhr.open(method, url, true);
	xhr.setRequestHeader('X-DZ-AJAX', '2');

	// 只有 POST 时需要设置 Content-Type 和发送 body
	if (method == 'POST') {
		xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	}

	xhr.onreadystatechange = function() {
		if (xhr.readyState == 4) {
			showloading('none');
			if (submitbtn) {
				submitbtn.disabled = false;
			}

			if (xhr.status >= 200 && xhr.status < 300) {
				var respText = xhr.responseText;
				var handled = false;

				// 1)JSON 分支(预留:如果未来后端实现了 JSON)
				if (respText) {
					try {
						var data = JSON.parse(respText);
						if (data && typeof data === 'object' && data.status) {
							handled = true;
							if (data.html && showid && $(showid)) {
								ajaxinnerhtml($(showid), data.html);
							}
							if (data.redirect) {
								window.location.href = data.redirect;
								return;
							}
							if (data.reload) {
								window.location.reload();
								return;
							}
						}
					} catch (e) {}
				}

				// 2)兜底:Discuz 旧式 XML + CDATA(尽量复用旧 ajaxpost_iframe 的行为)
				if (!handled) {
					var s = respText || '';
					var cdataMatch = s.match(/<!\[CDATA\[([\s\S]*)\]\]>/);
					if (cdataMatch && cdataMatch[1]) {
						s = cdataMatch[1];
					}

					var evaled = false;

					// 与旧逻辑一致:优先处理 ajaxerror 情况
					if (s != '' && s.indexOf('ajaxerror') != -1) {
						try {
							evalscript(s);
							evaled = true;
						} catch (e) {}
					}

					if (showidclass) {
						if (showidclass != 'onerror') {
							if (showid && $(showid)) {
								$(showid).className = showidclass;
							}
						} else {
							if (typeof showError == 'function') {
								try {
									showError(s);
								} catch (e) {}
							}
							window.ajaxerror = true;
						}
					}

					if (!evaled && (typeof ajaxerror == 'undefined' || !ajaxerror)) {
						if (showid && $(showid)) {
							ajaxinnerhtml($(showid), s);
						}
					}
					window.ajaxerror = null;

					if (!evaled) {
						try {
							evalscript(s);
						} catch (e) {}
					}
				}

				// 3)保持原 recall 行为
				if (typeof recall == 'function') {
					recall();
				} else if (typeof recall == 'string' && recall) {
					try { eval(recall); } catch (e) {}
				}

			} else {
				// XHR 请求失败时,回退原 iframe 行为
				ajaxpost_iframe(formid, showid, waitid, showidclass, submitbtn, recall);
			}
		}
	};

	try {
		if (method == 'POST') {
			xhr.send(body);
		} else {
			xhr.send(null);
		}
	} catch (e) {
		return false;
	}

	doane();
	return true;
}

// 对外统一入口:优先 XHR,失败再用 iframe
function ajaxpost(formid, showid, waitid, showidclass, submitbtn, recall) {
	// 如果 XHR 通路成功受理,则不再走 iframe
	if(ajaxpost_xhr(formid, showid, waitid, showidclass, submitbtn, recall)) {
		return false;
	}
	// 否则回退到原来的 iframe 模式
	return ajaxpost_iframe(formid, showid, waitid, showidclass, submitbtn, recall);
}

2、由于XHR Ajax提交的内容是UTF-8编码的,如何你的站点是GBK编码,还需要进行转码。

在 source/function/function_core.php 的末尾增加下面的函数:

// 统一兼容 XHR Ajax (ajaxv=2 / X-DZ-AJAX: 2) 在 GBK 站点上的 UTF-8 提交参数
function fix_request_charset() {
    // 只对 GBK 站点做兼容
    if (defined('CHARSET') && CHARSET != 'gbk') {
        return;
    }

    // 只兼容新版 XHR 通路:
    // 1)URL 中明确带 ajaxv=2
    // 2)请求头中带 X-DZ-AJAX: 2
    $is_new_ajax = false;
    if (isset($_GET['ajaxv']) && $_GET['ajaxv'] == '2') {
        $is_new_ajax = true;
    } elseif (!empty($_SERVER['HTTP_X_DZ_AJAX']) && $_SERVER['HTTP_X_DZ_AJAX'] == '2') {
        $is_new_ajax = true;
    }

    if (!$is_new_ajax) {
        // 老的同步请求、老的 iframe Ajax 不动
        return;
    }

    // 内部小工具:判断字符串中是否包含 UTF-8 多字节模式
    $has_utf8_multibyte = function($str) {
        if (!is_string($str) || $str === '') {
            return false;
        }
        // 匹配常见 UTF-8 多字节序列:2字节、3字节、4字节
        return preg_match(
            '/[\xC0-\xDF][\x80-\xBF]|' .
             '[\xE0-\xEF][\x80-\xBF]{2}|' .
             '[\xF0-\xF7][\x80-\xBF]{3}/',
            $str
        ) ? true : false;
    };

    // 对 $_GET 和 $_POST 做一次 UTF-8 → GBK 的“按需转换”
    foreach (array(&$_GET, &$_POST) as &$super) {
        foreach ($super as $k => $v) {
            if (!is_string($v)) {
                continue;
            }
            if ($v === '') {
                continue;
            }

            // 先判断是否包含 UTF-8 多字节模式,
            // 避免对纯数字、纯英文、已是 GBK 的字段做无谓转换
            if (!$has_utf8_multibyte($v)) {
                continue;
            }

            // 看起来像 UTF-8,多数情况直接按 UTF-8 → GBK 转
            $converted = diconv($v, 'UTF-8', 'GBK');
            if ($converted !== '' && $converted !== false) {
                $super[$k] = $converted;
            }
        }
    }
}

然后再 source/class/discuz/discuz_application.php 中查找 $this->_init_input(); ,在其下方增加:

        if(function_exists('fix_request_charset')) {
            fix_request_charset();
        }
欢迎转载分享,转载请注明 来源:大张小站 https://www.zhang.cq.cn/20252474.html
若您喜欢这篇文章,欢迎订阅老张小站以获得最新内容。 / 欢迎交流探讨,请发电子邮件至 mail[at]vdazhang.com 。


欢迎谈谈你的看法(无须登录) *正文中请勿包含"http://"否则将被拦截