当 Content-Type: application/x-www-form-urlencoded header 存在时,Grails RestfulController 不响应 JSON
Grails RestfulController doesn't respond with a JSON when Content-Type: application/x-www-form-urlencoded header is present
我正在尝试为我的应用程序实现一个简单的 RestfulController。
给定以下域 class:
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 请求工作正常,但是当 Content-Type: application/x-www-form-urlencoded
Header 出现时 Post 和 Put 请求到 URL 如 http://localhost:8080/app/api/test.json
无法响应正如预期的那样 JSON 。而是在持久化发送的条目后呈现显示操作视图。
我也试过 Header Accept: application/json
没有效果。
我该如何解决?
编辑:
进一步调查 RestfulController
的源文件和有关 Content Negotiation 的文档部分,我能够通过覆盖保存和更新方法替换行来修复它:
request.withFormat {
与:
withFormat {
这是故意的还是 RestfulController
的实施存在缺陷?
为什么它考虑 Content-Type header 而不是 Accept header 来呈现响应?
如果您的控制器的所有方法始终以 JSON 响应(当有响应 body 时)是可以接受的,您可以使用 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']
}
}
这意味着无论客户端发送哪个 headers、参数等,控制器将始终以 JSON 响应。
抱歉这么久才回复。我在使一切正常工作时遇到了一些麻烦。非常感谢@Dónal 提供的所有帮助。结束使用以下 class 来完成技巧:
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 还不起作用,但至少它起作用了。
我正在尝试为我的应用程序实现一个简单的 RestfulController。 给定以下域 class:
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 请求工作正常,但是当 Content-Type: application/x-www-form-urlencoded
Header 出现时 Post 和 Put 请求到 URL 如 http://localhost:8080/app/api/test.json
无法响应正如预期的那样 JSON 。而是在持久化发送的条目后呈现显示操作视图。
我也试过 Header Accept: application/json
没有效果。
我该如何解决?
编辑:
进一步调查 RestfulController
的源文件和有关 Content Negotiation 的文档部分,我能够通过覆盖保存和更新方法替换行来修复它:
request.withFormat {
与:
withFormat {
这是故意的还是 RestfulController
的实施存在缺陷?
为什么它考虑 Content-Type header 而不是 Accept header 来呈现响应?
如果您的控制器的所有方法始终以 JSON 响应(当有响应 body 时)是可以接受的,您可以使用 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']
}
}
这意味着无论客户端发送哪个 headers、参数等,控制器将始终以 JSON 响应。
抱歉这么久才回复。我在使一切正常工作时遇到了一些麻烦。非常感谢@Dónal 提供的所有帮助。结束使用以下 class 来完成技巧:
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 还不起作用,但至少它起作用了。