You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Grails中REST API调用报错:ProyectoController无count()方法

问题分析与解决方案

我帮你梳理下这个问题,你遇到的MissingMethodException是个典型的Grails REST配置误区——你把@Resource注解用错地方了。

错误根源

Grails的@Resource注解是用来标注Domain类的,它的作用是自动为该Domain生成一套标准化的RESTful控制器和路由。但你把它加在了自定义的ProyectoController上,这会让Grails强制让你的控制器继承RestfulController,而RestfulController依赖Domain类的count()、list()等ORM方法,你的控制器本身不是Domain,自然找不到这些方法,所以抛出了错误。


修复步骤

1. 把@Resource移到Proyecto Domain类

找到你的Proyecto.groovyDomain类,将注解转移到这里:

package estudiolegal

import grails.rest.Resource

@Resource(uri='/api/proyectos', formats=['json', 'xml'])
class Proyecto {
    // 你的Domain字段示例
    String nombre
    Date fechaCreacion
    
    // 关联关系
    static hasMany = [usuarios: Usuario]
    
    // 约束规则
    static constraints = {
        nombre blank: false
    }
}

2. 调整ProyectoController的配置

你的自定义控制器是用来处理脚手架(Scaffold)的HTML页面请求的,和REST接口可以共存。现在需要移除控制器上的@Resource注解,保留原有的脚手架方法:

package estudiolegal
import grails.plugin.springsecurity.annotation.Secured
import static org.springframework.http.HttpStatus.*

@Secured(['ROLE_ADMIN','ROLE_ABOGADO','ROLE_CLIENTE'])
class ProyectoController {
    ProyectoService proyectoService
    static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]

    def index = { redirect action: "list" }
    def create() { respond new Proyecto(params) }
    def save = {
        def proyecto = new Proyecto(params)
        proyecto.addToUsuarios(applicationContext.springSecurityService.currentUser)
        proyecto.save flush: true, failOnError: true
        redirect action: "show", id: proyecto.id
    }
    def edit(Long id) { respond proyectoService.get(id) }
    def update = {
        def proyecto = Proyecto.get(params.id)
        proyecto.properties = params
        proyecto.save flush: true, failOnError: true
        redirect action: "show", id: params.id
    }
    def show (Long id){ respond proyectoService.get(id) }
    def list = {
        def proyectos = Proyecto.list()
        [proyectos: proyectos]
    }
    def delete = {
        def proyecto = Proyecto.get(params.id)
        proyecto.usuarios.each {
            if(it.proyectos.contains(proyecto)) {
                it.removeFromProyectos(proyecto)
            }
        }
        proyecto.delete flush: true, failOnError: true
        redirect action: "index"
    }
    protected void notFound() {
        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.not.found.message', args: [message(code: 'proceso.label', default: 'Proyecto'), params.id])
                redirect action: "index", method: "GET"
            }
            '*'{ render status: NOT_FOUND }
        }
    }
}

3. 配置REST接口的权限

因为你用了Spring Security,需要确保REST接口也受权限控制,有两种方式可选:

  • 方式一:直接在Domain上添加权限注解
    @Resource(uri='/api/proyectos', formats=['json', 'xml'])
    @Secured(['ROLE_ADMIN','ROLE_ABOGADO','ROLE_CLIENTE'])
    class Proyecto {
        // ...Domain内容
    }
    
  • 方式二:自定义REST专用控制器(适合细粒度控制)
    如果你需要对不同HTTP方法设置不同权限,或者添加自定义逻辑,可以单独写一个REST控制器:
    package estudiolegal
    import grails.plugin.springsecurity.annotation.Secured
    import grails.rest.RestfulController
    
    @Secured(['ROLE_ADMIN','ROLE_ABOGADO','ROLE_CLIENTE'])
    class ProyectoRestController extends RestfulController<Proyecto> {
        static responseFormats = ['json', 'xml']
        ProyectoRestController() {
            super(Proyecto)
        }
    
        // 重写父类方法添加自定义逻辑,比如只返回当前用户的项目
        @Override
        def index() {
            def proyectos = Proyecto.where { usuarios.id == springSecurityService.currentUser.id }.list()
            respond proyectos
        }
    }
    
    然后在UrlMappings.groovy中配置路由:
    "/api/proyectos"(controller: "proyectoRest")
    "/api/proyectos/$id"(controller: "proyectoRest", action: [GET: "show", PUT: "update", DELETE: "delete"])
    

4. 测试REST接口

启动应用后,用RestClient请求http://localhost:8080/api/proyectos,记得带上Spring Security的认证信息(比如Bearer Token,因为你用了spring-security-rest插件),现在应该能正常返回JSON/XML格式的数据了。


Grails中正确使用REST服务的总结

Grails提供了三种主流的REST实现方式,按需选择:

  1. 自动生成REST端点:在Domain类上添加@Resource注解,快速生成CRUD接口,适合简单场景。
  2. 继承RestfulController:自定义控制器继承RestfulController<Domain>,重写父类方法实现定制逻辑,适合中等复杂度的需求。
  3. 手动定义端点:在普通控制器中用respond方法返回数据,配合request.withFormat处理多格式请求,适合完全自定义的REST接口。

额外注意点:

  • 确保Domain类的关联关系和映射配置正确
  • 用respond方法替代直接render,Grails会自动根据请求头返回对应格式的响应
  • 配合Spring Security时,务必给REST接口加上对应的权限控制

内容的提问来源于stack exchange,提问作者Bryansq2nt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:07:36