从 Grails RestfulController 索引/搜索操作中渲染元数据以进行分页

Posted

技术标签:

【中文标题】从 Grails RestfulController 索引/搜索操作中渲染元数据以进行分页【英文标题】:Render metadata for pagination from Grails RestfulController index/search actions 【发布时间】:2014-07-19 09:21:17 【问题描述】:

我正在寻找最佳实践/解决方案,以使“响应”方法在生成的 json 中生成额外的元数据以及从数据库中获取的实体集合。

基本上,我想在使用 angularJS 和 Restangular 插件构建的前端单页应用程序 (SPA) 中使用该元数据来实现分页。

PS:angularJS 的 $resource 或 Restangular 期望收集结果为 JS 数组。

Standard Grails JsonCollectionRenderer/JsonRenderer 会忽略在 map 参数中提供给“respond”的元数据。

我遇到了以下实现自定义 JsonRenderer 的文章,但我正在寻找更简单/灵活的解决方案,通过在 resources.groovy 中调整自定义 JsonCollectionRenderer 来“响应”输出元数据

http://groovyc.net/non-trivial-restful-apis-in-grails-part-2/

我的 RestfulController:

@Secured(value=["hasRole('ROLE_USER')"])
class DrugController extends RestfulController<Drug> 

static scaffold = true
static responseFormats = ['html', 'json', 'xml', 'hal']
static allowedMethods = [show: "GET"]

DrugController() 
    super(Drug, true)


@Override
def index(Integer max) 
    params.max = Math.min(max ?: 10, 100)
    // We pass which fields to be rendered with the includes attributes,
    // we exclude the class property for all responses. ***when includes are defined excludes are ignored.
    //params.fetch = [recordTypeRs:"eager"] from params.fields???
    respond resource.list(params),
            [includes: includeFields, excludes: ['class', 'errors', 'version'],
             metadata: [total: countResources(), psize: params.max, offset: params.offset?:0],
             model: [("$resourceNameInstanceCount".toString()): countResources()]]


@Override
def show(Drug drug) 
    JSON.use("deep") 
        respond drug,
                [includes: includeFields, excludes: ['class', 'errors', 'version']]
    


private getIncludeFields() 
    params.fields?.tokenize(',')


def search(Integer max) 
    params.max = Math.min(max ?: 10, 100)
    def c = Drug.createCriteria()
    def results = c.list(params) 
        //Your criteria here with params.q
        and 
            like('ndc', params.ndc?params.ndc+'%':'%')
            like('recordTypeJ.j017', params.labelerName?'%'+params.labelerName+'%':'%')
            like('recordTypeE.e017', params.productName?'%'+params.productName+'%':'%')
        
        //cache(true)
    
    log.debug(results.totalCount)
    respond results, model:[drugCount: results.totalCount]

我的 resources.groovy 中有以下内容。

// register Renderers/CollectionRenderers for all domain classes in the application.
for (domainClass in grailsApplication.domainClasses) 
    "json$domainClass.shortNameCollectionRenderer"(JsonCollectionRenderer, domainClass.clazz)
    "json$domainClass.shortNameRenderer"(JsonRenderer, domainClass.clazz)
    "hal$domainClass.shortNameCollectionRenderer"(HalJsonCollectionRenderer, domainClass.clazz)
    "hal$domainClass.shortNameRenderer"(HalJsonRenderer, domainClass.clazz)
 

【问题讨论】:

【参考方案1】:

基于来自

的想法

http://mrhaki.blogspot.com/2013/12/grails-goodness-rendering-partial.html

http://groovyc.net/non-trivial-restful-apis-in-grails-part-2/

    创建自定义 CollectionRenderer 例如, SumoJsonCollectionRenderer.groovy

import grails.converters.JSON
import grails.rest.render.RenderContext

import grails.rest.render.json.JsonCollectionRenderer
import org.codehaus.groovy.grails.web.mime.MimeType

import groovy.transform.CompileStatic
import static groovy.transform.TypeCheckingMode.SKIP

@CompileStatic
class SumoJsonCollectionRenderer extends JsonCollectionRenderer

    SumoJsonCollectionRenderer(Class componentType) 
        super(componentType)
    

    public SumoJsonCollectionRenderer(Class componentType, MimeType... mimeTypes) 
        super(componentType, mimeTypes)
    

    @CompileStatic(SKIP)
    @Override
    protected void renderJson(object, RenderContext context) 
        log.debug(object)
        log.debug(object.size())
        log.debug(object.getTotalCount())
        Map tObject = ['results':object]
        if(context.arguments?.metadata) 
            tObject['metadata'] = context.arguments.metadata
        
        super.renderJson(tObject,context)
    

在 resource.groovy 注册自定义 CollectionRenderer


for (domainClass in grailsApplication.domainClasses) 
    "json$domainClass.shortNameCollectionRenderer"(SumoJsonCollectionRenderer, domainClass.clazz)
    "json$domainClass.shortNameRenderer"(JsonRenderer, domainClass.clazz)
    "hal$domainClass.shortNameCollectionRenderer"(HalJsonCollectionRenderer, domainClass.clazz)
    "hal$domainClass.shortNameRenderer"(HalJsonRenderer, domainClass.clazz)

请求/响应


http://api.mydomain.com:8080/ApiApp/drugs.json?max=5&fields=ndc,id

"results":["id":1,"ndc":"000020803031","id":2,"ndc":"000021200011","id":3,"ndc":"000021407011","id":4,"ndc":"000021975901","id":5,"ndc":"000023004751"],"metadata":"total":851,"psize":5,"offset":0

【讨论】:

以上是关于从 Grails RestfulController 索引/搜索操作中渲染元数据以进行分页的主要内容,如果未能解决你的问题,请参考以下文章

Grails - grails-spring-security-rest - 无法从 application.yml 加载 jwt 机密

如何从源代码安装 Grails 插件?

从 Grails 应用程序外部插入数据时,Grails 如何设置 _idx 字段?

从 Intellij IDEA 开始的慢速 Grails 测试

grails无法从插件中找到视图

Grails - 从 Javascript 方法调用控制器和渲染模板