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

时间:2018-04-23 22:14:07

标签: spring servlets grails tomcat7

我有一个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没有启动。

我相信我已经读过某些地方,我可以通过用Tomcat8替换Tomcat7来解决这个问题;但是现在我在服务器上运行Ubuntu 14.04并且Tomcat8在存储库中没有提供,所以测试它并不是那么简单。

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

1 个答案:

答案 0 :(得分:1)

您可以通过将@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()
}

为方便起见,因为必须对其进行静态编译。