类别页面上的奇怪编码问题

我正在尝试追踪一个非 Docker 化安装中的奇怪问题(我意识到对此类安装的支持有限/不存在,所以只是想知道可能出了什么问题——我们内部的打包团队使用了“开发人员构建”说明来弄清楚如何构建必要的包)。我已确认该问题特定于我们的安装方式——我的基础设施团队不愿使用 Docker 化安装(他们更喜欢自己构建一切),因此我运行了沙盒实例,这些实例是 Docker 化和非 Docker 化的,并带有我们数据库的副本,以便验证问题所在,而这绝对是我们安装方式的产物。

从 3.3.2 升级到 3.3.3 时,我们的一些非英语论坛管理员注意到使用重音字符的版块的“关于”文本未正确编码:

有趣的是,我们可以看到标题以及所有其他文本都已正确编码。事实上,用于“关于”消息的消息本身也已正确编码:

我通过编辑它并在类别页面上看到更改来确认这是相同的文本。

所以这是类别页面上渲染该文本的特定问题。

查看浏览器中的 document.characterSet,它被正确识别为 UTF-8。数据库也显示格式为 UTF-8。

我想知道是否有人能指出类别页面上渲染此文本的方式有何不同。我猜是某个未正确构建的 Ruby 包(可能缺少 UTF-8 支持)用于渲染该文本,而不是系统上的其他文本,或者某个处理“关于”消息文本并截断它的东西(我注意到这里就是这种情况;但是,我们还有一个指向外部法语论坛的链接,该链接是非截断的消息,但我猜它仍然由相同的代码进行评估)。

感谢任何提示。我有点困惑。

我有时会看到它是正确的:

拉取原始的 categories.json 文件显示,只有 excerpt 是错误的:

        "description": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_text": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_excerpt": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",

try.discourse.org 上创建相同的类别并检查 categories.json 会得到正确的结果:

        "description": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_text": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",
        "description_excerpt": "Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).",

我不确定在您的安装中追踪此问题的下一步是什么,但也许专注于生成 excerpt 的代码路径会有所帮助,并且要知道这是由某种将 UTF-8 编码解释为 iso-8859-1 的东西引起的。

是的,这是我的猜测——生成摘要的程序很可能就是正确的地方。只是不确定它在代码中的具体位置。但知道要查找“excerpt”这个词肯定很有帮助——谢谢!

它确实让我觉得在某个时候它是以 iso-8859-1 编码传输的,所以感谢您的确认(我不能 100% 确定那是我看到的错误编码,但它看起来是正确的)。

您在 try.discourse.org 上看到的内容和我看到的 Docker 化安装中的内容一样(嗯,是编码正确的最终结果 :))

谢谢!

您可以轻松地使用以下方法进行检查:

○ → ipython3

In [1]: 'Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüística castellana
   ...: , de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualq
   ...: uiera de las variedades latinoamericanas, etc.).'.encode('utf-8').decode('iso-8859-1')
Out[1]: 'Esta sección del Foro se dedica a las personas usuarias de openSUSE que forman parte de la comunidad lingüÃ\xadstica castellana, de tal forma que dichas personas puedan consultar y participar en el foro en dicha lengua (sea el dialecto español o cualquiera de las variedades latinoamericanas, etc.).'

我的猜测是,有些东西在进行某种缓存。这没什么帮助,但这就是我会尝试寻找的东西。

除非他们就是讨厌 Docker,否则他们可以用 discourse_docker 来构建自己的镜像。然后他们就能确切地知道发生了什么,而不必信任别人的镜像。

太棒了,谢谢。

我曾认为可能是这种情况,但更改消息后得到了更新,所以我认为不是缓存问题——而是某些东西编码不正确。

我提出了一些选项,但最终,基础设施团队选择只使用通过构建服务构建的包。我不认为这是“我们讨厌 Docker”的问题(尽管 podman 可能是他们更想使用的),而是他们用来以相同方式管理一切的配置管理工具的一种使用方式。拥有一个使用 Docker/podman 的一次性配置会给他们正在使用的 CI/CD 设置带来其他复杂性(至少据我所知)。

所以最终,我设置了两个沙箱来确定问题的根源,以便我可以向正确的地方报告它们;不幸的是,这意味着当出现我们构建方式的问题时,我必须追查我们与标准的基于 Docker 的安装有什么不同,以便我们能够修复它。

我明白他们为什么认为Discourse 的方式很疯狂,并且他们确实希望一切都在一个统一的系统下进行管理。

但是。我上一个坚持使用他们喜欢的工具的客户,最终付给我将近 20 个小时的工作,才得到了一个可用的备份来迁移到 discourse.org 托管。再往前一个客户,付了更多的钱来调整他们的自定义设置,以防止他们的网站每周崩溃几次,一年后他们又付了更多的钱让我将他们迁移到 discourse.org 托管。:slight_smile:

祝你好运!

3 个赞

感谢您的建议。好消息是,我们生产系统的备份在 Docker 化安装中运行得很好(我已经测试过),所以如果/当他们决定使用基于 Docker 的安装是正确的方法时,我们将处于有利地位。我们拥有大量数据(几年前从 vBulletin 迁移到 Discourse),总体来说一切都运行得相当好,偶尔会有些小问题。

因此,我们对 Discourse 的工作方式有了很多了解,总的来说这是一件好事。 :slight_smile:

看起来 /categories.json 是一个 API 端点,而不是一个已创建然后读取的静态文件,所以我想这有助于将问题限制在 Ruby 或 JavaScript 中。我找到了这个端点的模式所在,但由于我对 Ruby 不太熟悉(多年来我积累了很多编程语言经验,所以即使我不会用它们编程,阅读大多数语言对我来说也不是问题——我可以很容易地抓住要点),但看起来 JavaScript 主要在浏览器中执行,而 Ruby 在服务器上执行(尽管我注意到也安装了 nodejs,所以这种概括可能不完全正确)。

如果我能找到处理 /categories 的函数(因为看起来末尾的 .json 只是告诉代码如何格式化输出;例如,我在 /top/top.rss 中看到了类似的行为),那么这将缩小我需要查看代码的范围,并且可以告诉我需要检查哪些 Ruby gem(我相当确定这将是 Ruby 代码)已正确构建。

2 个赞

似乎是摘录功能特有的问题——我刚注意到这在我们的搜索结果页面上也会发生:

(例如)

文本是:

我没看到“分享”按钮。

这是我自己回复用户(panorain)时引用过的内容。我是偶然发现这个问题的,因为当我尝试查看自己的活动时,会收到一个 500 服务器错误,并且 /logs 的输出显示 lib/excerpt_parser.rb 中出现了一个运行时错误“输入字符串不能为空”。

似乎有几件事都指向摘录处理中的某个问题,但仅限于开发环境的安装。

在我基于 Docker 的安装中,我可以无误地查看我的活动;奇怪的是,该安装中的数据库是从最近的生产服务器备份恢复的——而问题就存在于那里。

2 个赞

看起来我们已将 nokogiri 升级到 1.17.2,我看到 Dockerized 版本是 1.16.7 - 我怀疑这就是问题的原因。我将尝试撤销该更新(以及同时更新的任何其他内容)。

我降级了我们的包以再次使用 nokogiri 1.16。我不明白的是,每当我更新一个 gem 以减少重复打包时,我都会检查 main 中是否有相关的更改,但没有任何更改。除非我错过了什么。

        "description": "Witaj w polskiej sekcji społeczności openSUSE!",
        "description_text": "Witaj w polskiej sekcji społeczności openSUSE!",
        "description_excerpt": "Witaj w polskiej sekcji społeczności openSUSE!",

如您所见,我们有两次正确的文本,只有当它通过 PrettyText.excerpt 时才会损坏。这在 main 中是如何处理的?

@hendersj 我已经在准备一个 main 包,以便我们可以用数据库的副本进行测试。

我猜它是在 DEV: Update nokogiri to 1.18.1 (#30554) · discourse/discourse@affe26f · GitHub 中处理的。

但我想知道……在 lib/retrieve_title.rb

doc = Nokogiri.HTML5(html, encoding:)

这不应该是:

doc = Nokogiri.HTML5(html, encoding: Encoding::UTF_8)
1 个赞

@pfaffman 这个奇怪的代码也存在于 3.4.0 版本中。:slight_smile:

你能检查一下是否真的应该用空的编码来调用它:setting?

您认为有任何原因吗?UTF-8 是默认设置。

[1] pry(main)> Nokogiri::VERSION
=> "1.18.2"

[2] pry(main)> t = '<div>Witaj w polskiej sekcji społeczności openSUSE!</div>'
=> "<div>Witaj w polskiej sekcji społeczności openSUSE!</div>"

[3] pry(main)> Nokogiri.HTML5(t).to_s
=> "<html><head></head><body><div>Witaj w polskiej sekcji społeczności openSUSE!</div></body></html>"

[4] pry(main)> Nokogiri.HTML5(t, encoding: Encoding::UTF_8).to_s
=> "<html><head></head><body><div>Witaj w polskiej sekcji społeczności openSUSE!</div></body></html>"

[5] pry(main)> Nokogiri.HTML5(t).to_s == Nokogiri.HTML5(t, encoding: Encoding::UTF_8).to_s
=> true

retrieve_title 函数用于从外部 URL(例如 Youtube)提取标题,虽然我对此代码路径不熟悉,但很惊讶这会是您的问题来源。

如果您在做其他事情(例如在自定义插件中使用此函数),那么那里的 encoding 参数来自获取资源的 content-type 标头:

        if !encoding && content_type = _response["content-type"]&.strip&.downcase
          if content_type =~ /charset="?([a-z0-9_-]+)"?/
            encoding = Regexp.last_match(1)
            encoding = nil if !Encoding.list.map(&:name).map(&:downcase).include?(encoding)
          end
        end

        max_size = max_chunk_size(uri) * 1024
        title = extract_title(current, encoding)

因此,可以怀疑响应的 Web 服务器报告了不正确的 content-type。

因为该补丁中的所有其他调用都指定了 encoding: parameters。

只有 retrieve title 中的调用没有。这似乎不一致。并且未能正确处理 UTF-8 编码是导致此线程的整个讨论。

啊:

是以下内容的简写:

doc = Nokogiri.HTML5(html, encoding: encoding)

在那里强制 UTF8 会破坏从 Web 服务器解析非 UTF8 响应的功能。

感谢您的澄清。回到打包 3.4.0。