Grails json视图:不透明的视图选择-选择另一个目录中的默认模板

时间:2019-01-18 10:28:15

标签: grails view controller

我正在使用Grails 3.3.9和json视图1.2.10。

在另一个测试中,我有一个像这样的url映射

    "/api/test"(resources:'test')

我有一个空的views / test文件夹,并且没有Test域对象。

但是我确实有一个Device域实体,并且在该目录中有一个views / device / _device.gson模板来帮助呈现设备对象。

TestController的show动作如下所示

def show (Device device) {
    if(device == null) {
        render status:404
    } else {
        respond device}
}

这会在域模型中查找设备,然后填充到“显示”参数列表中,然后我对该设备进行响应。

但是,views / test目录中没有show.gson(它是空的)。

但是,当您在浏览器“ http://localhost:8080/api/test/1”中点击网址时,grails会使用“ views / device / _device.gson”模板来呈现响应。

这非常令人困惑,因为很难准确地确定视图响应实际在使用什么(除非您使用view:'xxx'map变量强制使用它。

如何/为什么返回TestController / show action响应,选择“ views / device / _device.gson”模板?

1 个答案:

答案 0 :(得分:0)

(代表问题作者发布的答案)

步骤1),如果您创建了一个新的控制器(我使用了grails create-controller <controller>,然后编辑生成的控制器以从RestfulController<domain type>开始扩展(但不要覆盖任何方法),然后设置URL映射对于您的控制器,在我的示例中为

"/api/org"(resources:'OrgRoleInstance')

在我的模型中具有对应的域类“ OrgRoleInstance”。

create-controller操作还会生成一个空的视图包'views / orgRoleInstance'(不生成gson文件)

a)如果您现在执行运行应用程序并打开URL

http://localhost:8080/api/org

然后您仍然会收到呈现的响应(像这样)!

[{"id":1,"sites":[],"domains":[],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Service_Provider"},"name":"Vodafone","mags":[{"id":2}]},{"id":2,"sites":[],"domains":[],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Maintainer"},"name":"Cisco","mags":[{"id":1}]},{"id":3,"sites":[],"domains":[],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Supplier"},"name":"Cisco","mags":[]},{"id":4,"sites":[{"id":1},{"id":2}],"domains":[{"id":1}],"role":{"enumType":"com.softwood.domain.OrgRoleInstance$OrgRoleType","name":"Customer"},"name":"Acme","mags":[]}]

事实上,有一些“默认框架逻辑”试图呈现从RestfulController继承的“索引”(和“显示”等)动作的默认实现。即使没有预先存在的gson也可以完成此操作-不提供警告。

第2步:下一步停止应用程序(它仍在缓存以前的“默认”行为)。

现在转到空视图目录并创建一个没有模型的“ index.gson”

json ([1,2,3])

然后在您的IDE中重新启动该应用程序。现在,当它启动时,它检测到存在index.gson,并且当您调用url'http://localhost:8080/api/org'时,您得到的是静态列表[1,2,3]的呈现json

第3步:gson视图在幕后使用静态编译,非常巧妙地使之正确无误。但是,在默认的RestfulController索引操作的情况下,将从域模型中选择一个OrgRoleInstances列表,并将其作为照明传递给视图。如果您希望与视图的数据绑定有效,那么如果您的域对象是T类型,则会返回List<T>

代码库中的内部默认值是,如果您返回List<T>进行响应,则假定视图中的数据模型为List<T> <T>List,即在我的示例中为

List<OrgRoleInstance> orgRoleInstanceList在gson视图模型中。现在,使用经过修改的index.gson(您可以在开发模式下不停止/启动服务器的情况下对其进行编辑)

import com.softwood.domain.OrgRoleInstance

model {
    List<OrgRoleInstance> orgRoleInstanceList
}

json {

    recordCount orgRoleInstanceList.size()
}

现在,当您获取URL时,您将得到一个带有列表大小的json响应:

{"recordCount":4}

请注意,如果您向模型中添加一个额外的变量,如下所示:

import com.softwood.domain.OrgRoleInstance

model {
    List<OrgRoleInstance> orgRoleInstanceList
    Integer orgRoleInstanceCount
}

json {

    recordCount orgRoleInstanceList.size()
    size orgRoleInstanceCount
}

并修改index.gson以打印新的Integer变量-则它不是默认响应操作中的数据绑定。

浏览器响应如下:

{"recordCount":4,"size":null}

选项4:-研究调用“模板”类。建立了一个期望List<T>的模型之后,通过数据绑定构建的runTime类型就是“ grails.orm.PagedResultList”的一个实例。这是一个Iterable类型。

在文档中阅读该内容非常令人困惑-但是,如果创建名为_<file>.gson的文件,还不清楚。然后将其视为模板gson文件。

这可以有自己的model / json,但是由于所有这些都是静态编译的,因此您必须获得完全匹配的类型,并在调用模板时(通过g.render或隐式tmpl.<file>

从父视图调用模板时,可以传递List<T>可迭代模型类型,也可以在父视图中进行迭代,然后将每个<T>传递给模板。您必须确保传递给tmpl的模型类型在tmpl模型中声明为相同类型。

例如假设在父视图中为List<T>,在模板中为<T>,则需要为列表中的每个项目调用tmpl。例如 如果您使用的是类似“ _orgRoleInstance.gson”这样的tmpl。

如果您具有这样的父视图(请注意声明了一个<T>,并且模型变量名为'org'

import com.softwood.domain.OrgRoleInstance

model {
    OrgRoleInstance org
}
json {
    id org.id
    name org.name
}

然后,父视图“ index.gson”需要像这样的东西,即在列表中有条目时,它会多次调用tmpl,但是我不得不告诉框架tmpl模型变量名是'org通过传递地图。这将按照您的期望进行渲染。

import com.softwood.domain.OrgRoleInstance

model {
    List<OrgRoleInstance> orgRoleInstanceList
    Integer orgRoleInstanceCount
}

orgRoleInstanceList.each { OrgRoleInstance org ->
    json tmpl.orgRoleInstance(org:org)
}

如果您将tmpl变量声明为“ def org”,它仍然可以工作,但是由于它是静态类型的,因此该变量作为Object的静态实例传递(运行时类型是正确的,但是您不能像静态类型为Object),并且很难整理访问属性所需的类型。

如果希望父视图将模型List<T>变量传递给tmpl,则可以=,但必须确保tmpl中的模型变量为List<T>,否则数据绑定不会工作。

现在在模板中,您可以调用json并遍历列表

例如像这样修改过的tmpl

import com.softwood.domain.OrgRoleInstance

model {
    //OrgRoleInstance org
    List<OrgRoleInstance> orgs
}
json {
    id orgs.id
    name orgs.name
}

和这样的修改后的父视图来调用tmpl:

import com.softwood.domain.OrgRoleInstance

model {
    List<OrgRoleInstance> orgRoleInstanceList
    Integer orgRoleInstanceCount
}

/*
orgRoleInstanceList.each { OrgRoleInstance org ->
    json tmpl.orgRoleInstance(orgs:org)
}*/

//alternate approach
json tmpl.orgRoleInstance (orgs:orgRoleInstanceList)

呈现的是这样的:

{"id":[1,2,3,4],"name":["Vodafone","Cisco","Cisco","Acme"]}

您会注意到,只有一个json子句,因此它首先迭代所有id,然后迭代所有名称。

如果这不是您想要的,则必须遍历列表以依次进行操作

即修改后的tmpl这样会依次遍历每个条目

import com.softwood.domain.OrgRoleInstance

model {
    //OrgRoleInstance org
    List<OrgRoleInstance> orgs
}
json (orgs) {OrgRoleInstance org ->
        id org.id
        name org.name
}

并在浏览器中生成

[{"id":1,"name":"Vodafone"},{"id":2,"name":"Cisco"},{"id":3,"name":"Cisco"},{"id":4,"name":"Acme"}]

对于任何对Grails Views感到困惑的人,我希望这可以说明这在URL映射之间是如何工作的(您可以使用Gradle“ urlMappingsReport”来查看这些内容),并显示将哪些URL映射到了哪些操作。然后,您需要使用与这些操作相同的名称创建的gson视图,并配置gson视图和您创建的任何tmpl,以了解处理代码时JsonView所应用的隐式行为。