发送 POST 数据时阻止 Safari 规范化 Unicode?

Posted

技术标签:

【中文标题】发送 POST 数据时阻止 Safari 规范化 Unicode?【英文标题】:Prevent Safari from normalizing Unicode when sending POST data? 【发布时间】:2020-12-26 17:21:52 【问题描述】:

显然 Safari 在发送 POST 数据时会将 Unicode 标准化,而所有其他主流浏览器只是发送它们所提供的内容。

规范化似乎是在数据通过网络发送之前发生的,并且对数据使用normalize() 不起作用(Safari 强制执行 NFC,无论提供什么)。

当请求带有重音字符的文件名时,这会成为一个问题,该字符在 NFC 和 NFD 格式中具有不同的代码点。解释基本上归结为Unicode equivalence 中的“组合字符”与“预组合字符”。

话虽如此,假设一个 API 不会在后端进行自己的规范化,并且需要一个字符串数组(文件名),那么在使用 Safari 时是否可以在前端发送正确的文件名?

Unicode 规范化问题的一个例子:

const str = 'Rosé'

const nfc = str.normalize()
const nfd = str.normalize('NFD')

console.log(nfc === nfd) // false

console.log(nfc.codePointAt(3)) // 233
console.log(nfd.codePointAt(3)) // 101

console.log(nfc.codePointAt(4)) // undefined
console.log(nfd.codePointAt(4)) // 769

一个最小的、可重现的例子:

请注意 Chrome 和 Safari 之间的控制台日志差异。

const isCorrectForm = (path, form) => path === path.normalize(`NF$form`)

const fetchData = async() => 
  const sourcePathC = '\u00e9'; // "é"
  const sourcePathD = '\u0065\u0301'; // "é"

  await fetch('https://httpbin.org/post', 
      method: 'POST',
      headers: 
        'Content-Type': 'application/json'
      ,
      body: JSON.stringify(
        pathsFormC: [sourcePathC],
        pathsFormD: [sourcePathD]
      ),
    )
    .then((response) => response.json())
    .then((data) => 
      const responseData = JSON.parse(data.data);
      const responsePathC = responseData.pathsFormC[0];
      const responsePathD = responseData.pathsFormD[0];

      console.log(
        isSourcePathCFormC: isCorrectForm(sourcePathC, 'C'),
        isSourcePathDFormD: isCorrectForm(sourcePathD, 'D'),
        isResponsePathCFormC: isCorrectForm(responsePathC, 'C'),
        isResponsePathDFormD: isCorrectForm(responsePathD, 'D'),
      );
    );


fetchData();

【问题讨论】:

Héhé 似乎我自己在编辑问题时被这个困住了:我首先从 Firefox(我的主浏览器)尝试,然后从 Safari 尝试查看它是否确实可重现,并从该 Safari 发布。 .. 重复***.com/questions/11176603/… 也许这个简单的更改可以工作,但无法在 Safari 上进行测试。 jsfiddle.net/ez1u37qg 【参考方案1】:

也许一个简单的解决方案就是这个。我说可能是因为这没有在 Safari 上测试(很遗憾)。

const isCorrectForm = (path, form) => path === path.normalize(`NF$form`)

const fetchData = async() => 
  const sourcePathC = encodeURIComponent('\u00e9'); // "é"
  const sourcePathD = encodeURIComponent('\u0065\u0301'); // "é"

  console.log(sourcePathC, sourcePathD);

  await fetch('https://httpbin.org/post', 
      method: 'POST',
      headers: 
        'Content-Type': 'application/json'
      ,
      body: JSON.stringify(
        pathsFormC: [sourcePathC],
        pathsFormD: [sourcePathD]
      ),
    )
    .then((response) => response.json())
    .then((data) => 
      const responseData = JSON.parse(data.data);
      const responsePathC = decodeURIComponent(responseData.pathsFormC[0]);
      const responsePathD = decodeURIComponent(responseData.pathsFormD[0]);

      console.log(responsePathC, responsePathD);

      console.log(
        isSourcePathCFormC: isCorrectForm(sourcePathC, 'C'),
        isSourcePathDFormD: isCorrectForm(sourcePathD, 'D'),
        isResponsePathCFormC: isCorrectForm(responsePathC, 'C'),
        isResponsePathDFormD: isCorrectForm(responsePathD, 'D'),
      );
    );


fetchData();

【讨论】:

编码可以工作,但它需要访问更改后端。 @jabacchetta 是的,确实需要这样后端更改。 @jabacchetta 如果这是 Safari 的内置功能,我不确定您是否只能从客户端绕过它。困扰我的是你写的Normalization appears to be happening right before the data is sent over the wire...。如果它发生在javascript 执行之后,那么是否有可能绕过这个客户端? 是的,我得出的结论是,只做客户端是不可能的。而且,事实上,编码是我给后端团队的建议。我不相信我们会在这里得到答案,所以我会继续奖励你。

以上是关于发送 POST 数据时阻止 Safari 规范化 Unicode?的主要内容,如果未能解决你的问题,请参考以下文章

IOS safari 和 Chrome 阻止了我的 websocket django 频道

Safari不包含COOKIE到第二个CORS请求中

移动Safari:在关注输入时阻止滚动页面

单个PHP“退出;”声明阻止 Safari 中的 HTML5 视频

有没有办法阻止 Symfony2 为某些路径发送会话 cookie?

如何阻止MonoDevelop在调试时打开Safari窗口?