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实现方式,按需选择:
- 自动生成REST端点:在Domain类上添加
@Resource注解,快速生成CRUD接口,适合简单场景。 - 继承RestfulController:自定义控制器继承
RestfulController<Domain>,重写父类方法实现定制逻辑,适合中等复杂度的需求。 - 手动定义端点:在普通控制器中用
respond方法返回数据,配合request.withFormat处理多格式请求,适合完全自定义的REST接口。
额外注意点:
- 确保Domain类的关联关系和映射配置正确
- 用
respond方法替代直接render,Grails会自动根据请求头返回对应格式的响应 - 配合Spring Security时,务必给REST接口加上对应的权限控制
内容的提问来源于stack exchange,提问作者Bryansq2nt
相关产品推荐
相关产品推荐

