Grails REST 控制器响应不正确的内容类型

Posted

技术标签:

【中文标题】Grails REST 控制器响应不正确的内容类型【英文标题】:Grails REST controller responds with incorrect content type 【发布时间】:2015-07-27 16:47:48 【问题描述】:

我正在尝试编写一个应始终以 JSON 响应的 Grails REST 控制器。控制器如下图:

class TimelineController 

    static allowedMethods = [index: "GET"]
    static responseFormats = ['json']

    TimelineService timelineService

    def index(TimeLineCommand command) 
        List<TimelineItem> timeline = timelineService.currentUserTimeline(command)
        respond timeline
    

我正在使用 respond 方法,它是 Grails 的 REST 支持的一部分,因此内容协商用于确定要呈现的响应类型。在这种特殊情况下,我希望选择 JSON,因为控制器指定

    static responseFormats = ['json']

此外,我编写(并在 Spring 中注册)以下渲染器来自定义为 List&lt;TimelineItem&gt; 返回的 JSON 格式

class TimelineRenderer implements ContainerRenderer<List, TimelineItem> 

    @Override
    Class<List> getTargetType() 
        List
    

    @Override
    Class<TimelineItem> getComponentType() 
        TimelineItem
    

    @Override
    void render(List timeline, RenderContext context) 

        context.contentType = MimeType.JSON.name
        def builder = new JsonBuilder()

        builder.call(
            [items: timeline.collect  TimelineItem timelineItem ->

                def domainInstance = timelineItem.item

                return [
                        date: timelineItem.date,
                        type: domainInstance.class.simpleName,
                        item: [
                                id   : domainInstance.id,
                                value: domainInstance.toString()
                        ]
                ]
            ]
        )

        builder.writeTo(context.writer)
    

    @Override
    MimeType[] getMimeTypes() 
        [MimeType.JSON] as MimeType[]
    

我写了一些功能测试,可以看到虽然我的渲染器被调用了,但是解析出来的内容类型是text/html,所以控制器返回了404,因为它找不到具有预期名称的GSP。

我强烈怀疑这个问题与使用自定义渲染器有关,因为我有另一个几乎相同的控制器,它不使用自定义渲染器并且它正确解析了内容类型。

【问题讨论】:

Accept 标头是否设置为 application/json @dmahapatro 我不想使用 Accept 标头进行内容解析,我总是想返回 JSON。我认为将static responseFormats = ['json'] 添加到控制器应该可以确保这一点 您能否详细说明一下您究竟为什么要使用respond?当您不使用不同的 mime 类型时,您可以完全摆脱 respond 方法,而改用 render myList as JSON @MarioDavid 因为如果respond 被调用,Grails 将使用我的自定义渲染器来生成 JSON。如果我使用myList as JSON,那么将使用默认渲染器。 应该创建一个 JIRA 问题。我可以在 2.4.4 和 2.5.0 中复制它。仍在调查中。 【参考方案1】:

看起来你必须在index.gsp下创建一个空白(至少)

grails-app/views/timeline/

使渲染器工作。我已成功取回内容类型为application/json

这种行为让我很困惑,我仍在研究它。这值得一个 JIRA 问题。如果您需要,我可以将我的虚拟应用推送到 github。

更新: 在 github 中创建的问题(带有示例应用的链接)。https://github.com/grails/grails-core/issues/716

【讨论】:

是的,如果您可以将您的虚拟应用推送到 GitHub 和/或提出一个 JIRA,这将非常有帮助。自己做了一些研究后,我开始怀疑不可能有一个类型的容器渲染器,除非你也有一个用于该类型的单独的渲染器。但是,文档没有提到这一点,我看不出有什么合乎逻辑的理由,所以我同意它值得一个 JIRA。 App 类似于问题中描述的内容。这是github中的问题。 github.com/grails/grails-core/issues/716 在 JIRA 问题和演示应用上做得很好,我已对该问题添加了评论【参考方案2】:

    在 Config.groovy 中,需要指定grails.mime.types。 详细信息可以在这里找到:Grails 2.3.11 Content Negotiation。至少你需要在 Config.groovy 中有以下内容:

    grails.mime.types = [
        json: ['application/json', 'text/json']
    ]
    

    如果您想使用自定义 JSON 响应,建议使用render someMap as JSON

    关于您的 404 问题,您需要在控制器操作中执行 response.setContentType('application/json')。 Grails 的默认响应格式是 html,如果没有指定 contentType,它会寻找 gsp 文件来呈现。

【讨论】:

2.如果我使用render someMap as JSON,那么将使用默认的 JSON 渲染,即不会调用TimelineRenderer 3.我应该不需要设置内容类型头,Grails的内容解析应该选择基于static responseFormats = ['json']的JSON@ render([timeline:timeline ] as grails.converters.JSON) 试过这个吗? Grails 控制器有自己的关键字“response”。你可以这样做response.contentType = 'application/json'; response.setContentLength(content.bytes.size()); response.outputStream &lt;&lt; content.bytes; 这里的内容是json的序列化字符串。

以上是关于Grails REST 控制器响应不正确的内容类型的主要内容,如果未能解决你的问题,请参考以下文章

如何保护 Grails 中的所有 REST 请求和响应

Grails控制器渲染方法render vs respond

Grails - Spring Security REST - 302 响应网络::ERR_TOO_MANY_REDIRECTS

使用 Grails Spring Security Plugin Core 和 REST 从用户响应中省略密码字段

如何自定义 Grails Spring Security REST 登录响应 HTTP 代码

Grails 文件上传未使用正确的内容类型