Javascript jQuery(form).serialize()失败,为“0”;URIError:格式错误的URI序列“;

Javascript jQuery(form).serialize()失败,为“0”;URIError:格式错误的URI序列“;,javascript,jquery,character-encoding,urlencode,Javascript,Jquery,Character Encoding,Urlencode,我有一个web应用程序,允许用户对彼此的帖子进行评论。我们使用jQuery.ajax()向服务器发送新的注释,在我们的测试中它似乎工作可靠 jQuery(.post-form.add-comment”)。关于(“提交”,函数(事件){ event.preventDefault(); jQuery.ajax({ 类型:“POST”, url:“/comment”, 数据:jQuery(this.serialize()) }); }); 但是,我们会自动收集用户的客户端JavaScript错误日志

我有一个web应用程序,允许用户对彼此的帖子进行评论。我们使用
jQuery.ajax()
向服务器发送新的注释,在我们的测试中它似乎工作可靠

jQuery(.post-form.add-comment”)。关于(“提交”,函数(事件){
event.preventDefault();
jQuery.ajax({
类型:“POST”,
url:“/comment”,
数据:jQuery(this.serialize())
});
});
但是,我们会自动收集用户的客户端JavaScript错误日志(使用Sentry),偶尔会出现如下错误:

URIError:格式错误的URI序列jquery.min.js:4:25041 这个错误似乎阻止了将评论发送到我们的服务器,因此我们无法判断用户试图发布的内容可能导致了这个错误


发生此错误的原因是什么?我们如何防止它?

出于这样或那样的原因,正在尝试提交包含我们可能称之为“无效字符”的注释。保留从
\uD800
\uDFFF
的Unicode代码点,以便UCS-2和UTF-16文本编码可以使用它们的成对来标识其他有效的Unicode字符代码点,否则这些代码点将超出这些编码的范围。对于大多数现代编码,包括UTF-16,这些代码点只允许出现在有效对中,在转换为另一种编码时可以映射到有效字符代码点;它们永远不能作为独立的“角色”存在

不幸的是,JavaScript在UTF-16标准化之前选择了UCS-2,UCS-2允许您自己包含代理字符,而无需成对生成有效的代码点。因为JavaScript允许它,浏览器也接受它作为输入。这是一个复杂的问题,但在大多数情况下,它实际上并不像您所经历的那样妨碍用户。如果您的表单没有使用JavaScript,您的用户将能够提交包含未配对代理的注释,而不会出现错误。那怎么办

浏览器采用一种常见的编码不兼容方法:任何无法转换为目标编码的字符都将替换为
Unicode替换字符
\uFFFD
。当对提交的典型表单数据进行编码时,浏览器会自动执行此替换。但是,
jQuery.serialize()
没有这样的逻辑,它调用的用于编码表单值的内置
encodeURIComponent
函数也没有这样的逻辑。相反,它只是抛出您看到的
URIError
。您可以在ECMAScript 9规范的中找到指定的此错误

encodeURIComponent('\uD83D')//URIError:格式错误的URI序列
要在JavaScript中重现类似浏览器的表单行为,您需要查找并替换以下任何实例:在
\uD800
\uDBFF
范围内出现“高代理项”,而在
\uDC00
\uDFFF
范围内出现“低代理项”,反之亦然。可能看起来像这样:

const replaceUnpairedSurrogates=s=>s
.replace(/[\uD800-\uDBFF]+([^\uDC00-\uDFFF]|$)/g,'�$1')
.替换(/(^ |[^\uD800-\uDBFF])[\uDC00-\uDFFF]+/g,'$1�');
(此函数满足Unicode标准要求的“转换过程约束”,因为它确保替换不会损坏以下有效字符。它不符合可选的“最大子部分替换”约定,因为它可能会将连续的未配对代理项字符向下折叠为单个替换字符。)

您当前正在使用
jQuery.serialize(this)
对表单数据进行编码,这不允许我们在编码表单值之前对其进行转换。但是
jQuery.serialize(this)
jQuery.param(jQuery.serializeArray(this))
一样,为我们提供了一个应用替换的位置:

jQuery(.post-form.add-comment”)。关于(“提交”,函数(事件){
event.preventDefault();
const data=jQuery.param(
序列化数组(this).map(
({name,value})=>{
名称:替换未配对代理(名称),
值:替换未配对的代理项(值),
})
)
);
jQuery.ajax({
类型:“POST”,
url:“/comment”,
数据:数据
});
});
为了进行测试,您可以运行以下操作以显示一个“无效字符”进行复制:

prompt('Copy this:','\uD83D')