1

我正在尝试为我的应用程序实现一个简单的 RestfulController。给定以下域类:

class Test {
    String name
    int someInteger

    static constraints = {
    }
}

及其控制器:

class TestController extends RestfulController<Test>{
    TestController() {
        super(Test)
    }
}

在 conf/UrlMappings.groovy 我添加了以下条目:

"/api/$controller?(.${format})?" {
    action = [POST: "save", PUT: "save", GET: "index", DELETE:"error"]
}

"/api/$controller/$id?(.${format})?" {
    action = [POST: "update", PUT: "update", GET: "show", DELETE: "delete"]
}

Get 请求工作正常,但 Post 和 Put 请求发送到 URL http://localhost:8080/app/api/test.json,例如Content-Type: application/x-www-form-urlencoded)无法按预期以 JSON 响应。而是在保留发送的条目后呈现显示操作视图。

我也尝试使用Accept: application/json没有效果的 Header。

我该如何解决?

编辑:

进一步调查RestfulController的源文件和有关内容协商的文档部分我能够通过覆盖替换行的保存和更新方法来修复它:

request.withFormat {

和:

withFormat {

是故意的还是在实施上有缺陷RestfulController?为什么它考虑 Content-Type 标头而不是 Accept 标头来呈现响应?

4

2 回答 2

2

如果您的所有控制器方法始终使用 JSON 响应(当有响应主体时)是可以接受的,您可以使用responseFormats来实现这一点,如下所示:

class TestController extends RestfulController<Test>{

    static responseFormats = ['json']

    TestController() {
        super(Test)
    }

    def customJsonAction() {
        respond Something.get(params.id)
    }

    def someActionThatRendersGsp() {
        render view: 'myGsp', model: [foo: 'bar']
    }
}

这意味着无论客户端发送哪些标头、参数等,控制器都将始终以 JSON 响应。

于 2016-07-10T16:43:48.593 回答
0

很抱歉这么久才回复。我在让一切正常工作时遇到了一些麻烦。非常感谢@Dónal 的所有帮助。结束使用以下类来解决问题:

import org.codehaus.groovy.grails.web.servlet.HttpHeaders;
import org.springframework.http.HttpStatus;

import grails.artefact.Artefact;
import grails.rest.RestfulController;
import grails.transaction.Transactional;

@Artefact("Controller")
@Transactional(readOnly = true)
class MyRestfulController<T> extends RestfulController<T> {

    public MyRestfulController(Class<T> resource, boolean readOnly = false) {
        super(resource, readOnly);
    }

    @Override
    @Transactional
    def save() {
        if(handleReadOnly()) {
            return
        }
        T instance = createResource(getParametersToBind())

        instance.validate()
        if (instance.hasErrors()) {
            respond instance.errors, view:'create' // STATUS CODE 422
            return
        }

        instance.save flush:true

        def formatHolder = params.format ? this : request
        formatHolder.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: "${resourceName}.label".toString(), default: resourceClassName), instance.id])
                redirect instance
            }
            '*' {
                response.addHeader(HttpHeaders.LOCATION,
                        g.createLink(
                                resource: this.controllerName, action: 'show',id: instance.id, absolute: true,
                                namespace: hasProperty('namespace') ? this.namespace : null ))
                respond instance, [status: HttpStatus.CREATED]
            }
        }
    }

    @Override
    @Transactional
    def update() {
        if(handleReadOnly()) {
            return
        }

        T instance = queryForResource(params.id)
        if (instance == null) {
            notFound()
            return
        }

        instance.properties = getParametersToBind()

        if (instance.hasErrors()) {
            respond instance.errors, view:'edit' // STATUS CODE 422
            return
        }

        instance.save flush:true

        def formatHolder = params.format ? this : request
        formatHolder.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: "${resourceClassName}.label".toString(), default: resourceClassName), instance.id])
                redirect instance
            }
            '*'{
                response.addHeader(HttpHeaders.LOCATION,
                        g.createLink(
                                resource: this.controllerName, action: 'show',id: instance.id, absolute: true,
                                namespace: hasProperty('namespace') ? this.namespace : null ))
                respond instance, [status: HttpStatus.OK]
            }
        }
    }

}

通过使用def formatHolder = params.format ? this : request然后调用formatHolder.withFormat,我现在能够独立于请求格式覆盖响应格式。

它还不适用于 Accept Header,但至少它有效。

于 2016-07-14T13:26:22.483 回答