RestfulController 未绑定 POST 中嵌套的 JSON

RestfulController not binding nested JSON in POST

我正在 Grails 2.4.4 中构建 REST API 并依靠 RestfulController 来处理基本的 CRUD 类型功能。前端是在 AngularJS 1.3 中构建的,在大多数情况下,Angular 的 $resource 和 Grails RestfulController 工作完美,不需要我编写大量样板代码。我有一个主要问题,即当我从 Angular 到 Grails POST JSON 时,具有 hasMany 关系的域对象没有按预期绑定。

例如采用以下域对象:

class Task {

    String name
    //auto timestamps
    Date dateCreated
    Date lastUpdated

    static hasMany = [filters:TaskFilter]

    static constraints = {
    }

}

class TaskFilter {

  String filterMetaData 

  static belongsTo = [task:Task]

  static constraints = {
    task column: 'task_id'
  }
}

当我 POST 像这样的对象时:

{name: "Task 1", filters: [{filterMetaData:'some-meta-data'}]}

我在 Grails 中收到以下错误:

| Error 2015-02-10 17:41:34,619 [http-bio-8080-exec-5] ERROR errors.GrailsExceptionResolver  - NullPointerException occurred when processing request: [POST] /api/studies/1/tasks
Stacktrace follows:
Message: null
    Line | Method
->>   99 | $tt__save          in grails.rest.RestfulController
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|    198 | doFilter           in grails.plugin.cache.web.filter.PageFragmentCachingFilter
|     63 | doFilter . . . . . in grails.plugin.cache.web.filter.AbstractFilter
|    104 | processFilterChain in com.odobo.grails.plugin.springsecurity.rest.RestTokenValidationFilter
|     71 | doFilter . . . . . in     ''
|     53 | doFilter           in grails.plugin.springsecurity.web.filter.GrailsAnonymousAuthenticationFilter
|    122 | doFilter . . . . . in com.odobo.grails.plugin.springsecurity.rest.RestAuthenticationFilter
|     82 | doFilter           in grails.plugin.springsecurity.web.authentication.logout.MutableLogoutFilter
|     63 | doFilter . . . . . in com.odobo.grails.plugin.springsecurity.rest.RestLogoutFilter
|     82 | doFilter           in com.brandseye.cors.CorsFilter
|   1145 | runWorker . . . .  in java.util.concurrent.ThreadPoolExecutor
|    615 | run                in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run . . . . . . .  in java.lang.Thread

我不太确定这个错误是怎么回事,因为它很模糊,但是任务和任务过滤器被回滚了,没有任何东西被保存。

奇怪的是,如果我从 TaskFilter 中删除这行代码:

static belongsTo = [task:Task]

一切正常,除了这迫使我有一个 task_task_filter 加入 table 而不是 task_filter [=48] 中更容易理解的 task_id 列=].

过去几天我一直在阅读此处以及各种论坛、博客和邮件列表上的帖子,但没有找到解决方案。大多数帖子都比较老,所以我不知道它们是否适用,但有几个只是简单地说 "You can't bind a JSONArray using Grails" 这看起来很奇怪,因为它是任何中等复杂数据模型的常见要求。

无论如何 - 如果有人能指出一些具体的和最新的 "you can't do it" 或如何实际处理这种情况,我将不胜感激。如果可能的话,我宁愿避免编写自定义解析代码来管理它,因为 RestfulController 的其余部分就像一个魅力。此外,如果有任何解释为什么当我删除关系的 belongsTo 端时这会起作用,也许这会帮助我理解这里发生的事情。

以防万一有人想知道我的 RestfulController 子类是这样的:

class TaskController extends RestfulController<Task> {

  static responseFormats = ['json', 'xml']

  TaskController() {
    super(Task)
  }
}

在与 grails 开发团队进行多次讨论后,我得知我对这应该如何工作的理解并不是 Grails 的工作方式。我假设(错误地)因为数据绑定将 TaskFilter 添加到 Task 中,所以它的行为就像在 Task 上调用 addToFilters() 一样,但不管集合是如何创建的。我不知道这个系列到底是什么,但它确实 不会 保存它们..

JIRA 已关闭 "behaves as expected"。

此后我的解决方法是我的 TaskController

@Override
protected Task createResource() {
  Task task = super.createResource()
  if (task.filters) {
    task.filters.each {filter ->
      task.addToFilters(filter)
    }
  }
  task
}

不幸的是,基于 RestfulController 的应用程序中的任何关联都需要这种相同类型的代码来处理嵌套资源。我仍然认为 Grails 应该在框架中处理这个问题,如果我有时间处理它,我可能会尝试将该功能添加到代码中。