关于其余部分:为什么默认的Controller实现会发送带有内部错误的崩溃信息?

Why the default Controller implementation sends crashes with internal error?

我在rest-api下生成了控制器,grails应用程序配置文件。控制器没有任何改变,仅添加了一些println调用。

对于呼叫curl -X PUT -d name=petr2 -d phone=338 localhost:8080/TSCell/3,我有{"message":"Internal server error","error":500}响应。在调试中,我可以看到,该错误在最终的respond TSCell, [status: OK, view:"show"]调用之后发生。

更新方法的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Transactional
def update(TSCell tSCell) {
    println"in update method"
    if (tSCell == null) {
        transactionStatus.setRollbackOnly()
        render status: NOT_FOUND
        return
    }

    if (tSCell.hasErrors()) {
        transactionStatus.setRollbackOnly()
        respond tSCell.errors, view:'edit'
        return
    }

    tSCell.save flush:true

    respond tSCell, [status: OK, view:"show"]
}

和堆栈跟踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
ERROR org.grails.web.errors.GrailsExceptionResolver - IllegalArgumentException occurred when processing request: [PUT] /TSCell/3
Model variable [TSCell] of with value [class zcrm.api.TSCell] type [java.lang.Class] is not of the correct type [zcrm.api.TSCell]. Stacktrace follows:
java.lang.reflect.InvocationTargetException: null
    at org.grails.core.DefaultGrailsControllerClass$ReflectionInvoker.invoke(DefaultGrailsControllerClass.java:210)
    at org.grails.core.DefaultGrailsControllerClass.invoke(DefaultGrailsControllerClass.java:187)
    at org.grails.web.mapping.mvc.UrlMappingsInfoHandlerAdapter.handle(UrlMappingsInfoHandlerAdapter.groovy:90)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:883)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
    at org.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:77)
    at org.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:67)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)
Caused by: grails.views.ViewRenderException: Error rendering view: Model variable [TSCell] of with value [class zcrm.api.TSCell] type [java.lang.Class] is not of the correct type [zcrm.api.TSCell]
    at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:33)
    at grails.views.mvc.GenericGroovyTemplateView.renderMergedOutputModel(GenericGroovyTemplateView.groovy:71)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
    at grails.views.mvc.renderer.DefaultViewRenderer.render(DefaultViewRenderer.groovy:105)
    at grails.artefact.controller.RestResponder$Trait$Helper.internalRespond(RestResponder.groovy:188)
    at grails.artefact.controller.RestResponder$Trait$Helper.respond(RestResponder.groovy:98)
    at zcrm.api.TSCellController$$EQ0icN2W.$tt__update(TSCellController.groovy:64)
    at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
    at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
    at grails.transaction.GrailsTransactionTemplate$2.doInTransaction(GrailsTransactionTemplate.groovy:96)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
    at grails.transaction.GrailsTransactionTemplate.execute(GrailsTransactionTemplate.groovy:93)
    ... 14 common frames omitted
Caused by: java.lang.IllegalArgumentException: Model variable [TSCell] of with value [class zcrm.api.TSCell] type [java.lang.Class] is not of the correct type [zcrm.api.TSCell]
        at grails.views.WritableScriptTemplate.make(WritableScriptTemplate.groovy:138)
    at grails.plugin.json.view.api.internal.DefaultGrailsJsonViewHelper.prepareWritable(DefaultGrailsJsonViewHelper.groovy:736)
    at grails.plugin.json.view.api.internal.DefaultGrailsJsonViewHelper$7.writeTo(DefaultGrailsJsonViewHelper.groovy:713)
    at grails.plugin.json.view.JsonViewTemplate.json(JsonViewTemplate.groovy:126)
    at grails.plugin.json.view.JsonViewTemplate.json(JsonViewTemplate.groovy:149)
    at zcrm_api_TSCell_show_gson.run(zcrm_api_TSCell_show_gson:7)
    at grails.plugin.json.view.JsonViewTemplate.doWrite(JsonViewTemplate.groovy:35)
    at grails.views.AbstractWritableScript.writeTo(AbstractWritableScript.groovy:30)
    ... 26 common frames omitted

谢谢。


该代码不应编译,但是Groovy有时过于"有用",因此无法通过。您为实例变量命名与其类TSCell相同,这真是一个有趣的代码块,以了解Groovy如何处理实例和静态方法调用之间的歧义。

对于第一行,由于您具有TSCell TSCell,因此编译器有可能知道左侧的是类名,右侧的是实例变量,因为没有其他有效的解释令牌。

在第三行中,不清楚是否正在检查类或实例变量是否为null,但是我在本地尝试了此操作,它是实例变量。

TSCell.hasErrors()可以解释为对类的静态方法调用或对实例的调用,但是由于该方法不是静态的,因此Groovy在实例上调用它并成功。必须将相同的逻辑应用于save调用,但是同样,由于它不是静态方法,因此它在实例上进行调用并成功。

然后在方法的最后一行,kaboom,您的运气在四个成功的调用之后就用光了。 respond方法有一些重载,您最终调用了respond(Object, Map),这对TSCell类或TSCell类的实例有效。 Groovy选择了一个不是您想要的内容和respond方法内部不支持的内容。

Groovy共享Java的变量和类命名约定,即类名以大写字母开头,实例变量名以小写字母开头。只是告诉人们这是一种好方法,这是一回事,但是像这样的一个例子使使用大写实例变量名的一个不好的主意变得更加显而易见(您应该能够查看变量,而不必查看其声明以了解它是一个类还是var名称),以及为什么使用与该类相同的名称更糟糕。


此错误可能是由于您使用与类名相同的名称命名参数引起的。

我建议您将变量名更改为小写(这是常规和Java命名约定)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def update(TSCell tsCell) { //you can also just write tsCell without type
    println"in update method"
    if (tsCell == null) {
        transactionStatus.setRollbackOnly()
        render status: NOT_FOUND
        return
    }

    if (tsCell.hasErrors()) {
        transactionStatus.setRollbackOnly()
        respond tsCell.errors, view:'edit'
        return
    }

    tsCell.save flush:true

    respond tsCell, [status: OK, view:"show"]
}


问题出在呼叫curl中。如文档所述Grails has built in support for Content negotiation using either the HTTP Accept header, an explicit format request parameter or the extension of a mapped URI.

因此将-H"Accept: application/xml"添加到上述curl调用中解决了该问题。