我正在使用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”模板?
答案 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所应用的隐式行为。