以下是此错误的实际情况:
XPage 本质上是 servlet……XPage 中发生的一切都只是 servlet 引擎之上的层。基本上,servlet 可以将两种类型的数据发送回任何发起连接的对象(例如浏览器):文本和二进制。
一个普通的 XPage 发送文本——特别是 HTML。一些 xAgent 还发送文本,例如 JSON 或 XML。然而,在任何这些场景中,Domino 都使用 Java Writer来发送响应内容,因为 Writer 已针对发送字符数据进行了优化。
当我们需要发送二进制内容时,我们使用OutputStream代替,因为流已针对发送通用字节数据进行了优化。因此,如果我们发送 PDF、DOC/XLS/PPT、图像等,我们需要使用流,因为我们发送的是二进制数据,而不是文本。
问题(您很快就会看到,这是一个双关语)是我们每个响应只能使用一个。
一旦任何 HTTP 客户端被告知响应的内容类型是什么,它就会假设如何处理该内容。因此,如果您告诉它 expect application/pdf
,它只会接收二进制数据。相反,如果你告诉它期望application/json
,它期望只接收字符数据。如果响应包含与承诺的内容类型不匹配的任何数据,则几乎总是会使整个响应无效。
因此,Domino 以其无限的智慧保护我们不犯这个错误,只允许我们在一个请求中发送一个或另一个,如果我们不遵守该规则则抛出异常。
不幸的是……如果在我们尝试发送二进制内容时我们的代码中有任何异常,Domino 想要向消费者报告……它试图调用输出编写器发送 HTML 报告出现问题。除非我们已经获得了输出流的句柄,所以不允许 Domino 获得输出写入器的句柄,因为这将违反它自己的规则,即每个响应只使用一个句柄。反过来,这会引发您报告的异常,掩盖实际导致问题的异常(在您的情况下,可能是ClassNotFoundException)。
那么我们如何确保我们看到了真正的问题,而不是这种误导呢?我们try
:
try {
/*
* Move all your existing code here...
*/
} catch (e) {
print("Error generating dynamic PDF: " + e.toString());
} finally {
facesContext.responseComplete();
}
这是首选方法有两个原因:
- 如果我们的代码出现问题,我们不会让 Domino 抛出异常。相反,我们记录它(而不是
print
用来将它发送到控制台并记录,你也可以将它扔到 OpenLog,或者你喜欢的任何日志机制)。这意味着 Domino 不会尝试向用户报告错误,因为我们已经承诺我们已经向自己报告了错误。
- 通过将关键
facesContext.responseComplete()
调用(最终告诉 Domino 不要发送自己的任何内容)移动到finally
块中,可以确保它会被执行。如果我们将它留在try
块中,如果发生异常,它将被跳过,因为我们会直接跳到catch
... 所以即使 Domino 没有报告我们的异常,因为我们捕获了它,它仍然会尝试调用响应作家,因为我们没有告诉它不要。
如果您遵循上述模式,并且您的代码有问题,那么浏览器将收到一个不完整或损坏的文件,但日志会告诉您出了什么问题,而不是报告与根本原因无关的错误问题。