Grails 应用程序 response.outputStream << 自升级到 Grails 3.3 后失败

Grails application response.outputStream << fails since upgrade to Grails 3.3

我有一个 Grails 应用程序,最近从 2.5 升级到 3.3。大体上一切正常,但今天我们 运行 遇到了一个似乎与其他人共享但我找不到解决方案的问题。

在控制器中,我有一个方法可以将字符串附加到 response.outputStream。

代码现在显示为

    response.status = OK.value()
    response.contentType = 'text/csv;charset=UTF-8'
    response.setHeader "Content-disposition", "attachment; filename=rcCandidate.csv"
    response.outputStream << converted
    response.outputStream.flush()
    response.outputStream.close()

基于此处的建议

http://sergiodelamo.es/grails-tips-how-to-output-csv-from-a-grails-3-controller/

此代码在我的测试环境中执行得很好

$ grails -version
| Grails Version: 3.3.5
| Groovy Version: 2.4.15
| JVM Version: 1.8.0_162

但在生产服务器上严重失败

$ java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)

$ apt list | grep tomcat
tomcat7/trusty-security,trusty-updates,now 7.0.52-1ubuntu0.13 all [installed]

报告的失败以:

开头
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: javax/servlet/WriteListener

后跟堆栈跟踪,然后是这个

Caused by: java.lang.NoClassDefFoundError: javax/servlet/WriteListener

然后是关于 WriteListener 的更多堆栈跟踪和类似消息

我看到了替换此行的建议

provided "org.springframework.boot:spring-boot-starter-tomcat"

compile "org.springframework.boot:spring-boot-starter-tomcat"

但正如这里指出的那样

https://docs.grails.org/latest/guide/deployment.html

这不是一个好主意,事实上,当我尝试时,tomcat 没有启动。

我相信我在某处读到过,我可以通过将 Tomcat7 替换为 Tomcat8 来解决这个问题;但是现在我在服务器上 运行 Ubuntu 14.04 并且存储库中不提供 Tomcat8,因此测试它并不是很简单。

有人对我有什么建议吗?提前致谢。

您可以通过在您的方法中添加@CompileStatic 来解决这个问题,但这并不总是可行的。我们通过添加静态实用方法在我们的应用程序中解决了这个问题:

@CompileStatic
public static sendResponseData(ServletOutputStream outputStream, String s) { // but this could be byte[] s or InputStream s or whatever you need
    outputStream << s
}

然后调用它而不是左移操作。

您可能需要添加额外的方法签名,以便它们可以静态编译,但概念是相同的。如果我没记错的话,这里的左移运算符使用了一些默认情况下(在 Tomcat 7 上)不包含但也不需要的注释或其他内容(显然我不记得细节了!)。

请注意,我们还添加了

@CompileStatic
public static flushOutputStream(ServletOutputStream outputStream) {
    outputStream.flush()
}

为了方便起见,因为那个也必须静态编译。