Grails 3 and Json: "Request method 'POST' not supported" - json

I'm porting a working webapp from Grails 2.3 to 3.0.1. When I post the Json string {"command":"ping"} to the server i get the following result:
{"timestamp":1429380750958,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/rps/request"}
Here's the controller:
import org.grails.web.json.JSONObject
class RequestController {
def jsonManagerService
def index() {
JSONObject json = request.JSON
if(!json){
render "{json or gtfo}"
return
}
render jsonManagerService.parseJson(json)
}
}
Here's the JsonManagerService:
import grails.transaction.Transactional
import org.grails.web.json.JSONObject
#Transactional
class JsonManagerService {
def parseJson(JSONObject o) {
switch (o.command){
case("ping"):
return '{"result":"pong"}'
break;
default:
return '{"result":"unknown command"}'
}
}
}
And here's my UrlMappings.groovy (it's the default one):
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
"404"(view:'/notFound')
}
}
It looks like a Spring related issue. All the search on this matter provided no results. Any idea?
Edit: thanks #dmahapatro, added UrlMappingsgroovy. Corrected the controller, dumb mistake, but result is still the same.

Related

How do you serialize a Kotlin Map from json?

I am trying to serialize a kotlin.collections.Map from Json using Ktor and I continue to get an error, basically telling me it doesn't know how. I assumed this case was basic.
val beans = beans {
bean("oauthClient") {
HttpClient(CIO) {
expectSuccess = true
install(DefaultRequest){
url("${env["oauth.url"]!!}/ms_oauth/oauth2/endpoints/oauthservice/tokens")
}
install(Auth) {
basic {
credentials {
BasicAuthCredentials(username = env["client.id"]!!, password = env["client.secret"]!!)
}
}
}
install(ContentNegotiation) {
json()
}
}
}
bean {
val oauthResults: Map<String,Any> = runBlocking { // blows up here
ref<HttpClient>("oauthClient").submitForm("${env["oauth.url"]!!}/ms_oauth/oauth2/endpoints/oauthservice/tokens",
Parameters.build {
append("grant_type", "password")
append("scope", "bug.rest.public")
append("username", env["ssoUsername"]!!)
append("password", env["ssoPassword"]!!)
}
).body()
}
}
}
error:
Caused by: io.ktor.client.call.NoTransformationFoundException:
No transformation found: class io.ktor.utils.io.ByteBufferChannel -> class kotlin.collections.Map
Do I have to explicitly enable something? Or am I making a different mistake?
Update
I've since tried to marshal it into a data object and I get the same error, but referencing that object:
#Serializable
data class Oauth constructor(
#SerialName("expires_in")
val expiration: Int,
#SerialName("token_type")
val tokenType:String,
#SerialName("access_token")
val accessToken: String
)
error:
io.ktor.client.call.NoTransformationFoundException:
No transformation found: class io.ktor.utils.io.ByteBufferChannel -> class com.company.Oauth

JSON Marshaller for each action in controller (Grails)

In grails how to have JSON.registerObjectMarshaller for each action in controller.
Here is an example
My User domain object:
String username
String empId
String attendanceID
String password
String firstName
in my controller:
def myaction1() {
def user=User.getAll()
// XXX here i want to return just username and empId
render user as JSON
}
def myaction2() {
def user=User.getAll()
// XXX here i want to return just username and firstName
render user as JSON
}
While it may be a bit overkill for such as simple domain, and you could likely get away with just returning a Map of your data, the question is still valid.
How do you register custom named marshallers?
Typically you will do this inside your grails-app/conf/BootStrap.groovy (or a new file grails-app/conf/CustomMarshallersBootStrap.groovy if you want to keep things clean). An example of this might look like this:
// Bootstrap.groovy
import grails.converters.JSON
import com.example.User
class BootStrap {
def init = { servletContext ->
JSON.createNamedConfig("userEmployeeView", {
JSON.registerObjectMarshaller(User) { User o ->
return [
username: o.username,
empId: o.empId
]
}
})
JSON.createNamedConfig("userOtherView", {
JSON.registerObjectMarshaller(User) { User o ->
return [
username: o.username,
firstName: o.firstName
]
}
})
}
def destroy = { }
}
This will register two named marshallers which you can use in your controller(s) like this:
// UserController.groovy
package com.example
import grails.converters.JSON
class UserController {
def action1() {
def users = User.getAll()
JSON.use("userEmployeeView") {
render users as JSON
}
}
def action2() {
def users = User.getAll()
JSON.use("userOtherView") {
render users as JSON
}
}
}
The above uses named marshllers which allows you to control which JSON representation (actually just a Map) will be used when creating the final JSON output.
Hope this helps, and forgive any typos as I wrote this off the top of my head.

Grails JSON converter and JSONObject code breaks when moved to src/groovy

I am trying to move some code from a grails service file into src/groovy for better reuse.
import grails.converters.JSON
import org.codehaus.groovy.grails.web.json.JSONObject
class JsonUtils {
// seems like a clunky way to accomplish converting a domainObject
// to its json api like object, but couldn't find anything better.
def jsonify(obj, ArrayList removeableKeys = []) {
def theJson = obj as JSON
def reParsedJson = JSON.parse(theJson.toString())
removeableKeys.each { reParsedJson.remove(it) }
return reParsedJson
}
// essentially just turns nested JSONObject.Null things into java null things
// which don't get choked on down the road so much.
def cleanJson(json) {
if (json instanceof List) {
json = json.collect(cleanJsonList)
} else if (json instanceof Map) {
json = json.collectEntries(cleanJsonMap)
}
return json
}
private def cleanJsonList = {
if (it instanceof List) {
it.collect(cleanJsonList)
} else if (it instanceof Map) {
it.collectEntries(cleanJsonMap)
} else {
(it.class == JSONObject.Null) ? null : it
}
}
private def cleanJsonMap = { key, value ->
if (value instanceof List) {
[key, value.collect(cleanJsonList)]
} else if (value instanceof Map) {
[key, value.collectEntries(cleanJsonMap)]
} else {
[key, (value.class == JSONObject.Null) ? null : value]
}
}
}
but when I try to call jsonify or cleanJson from services I get MissingMethodExceptions
example call from grails service file:
def animal = Animal.read(params.animal_id)
if (animal) json.animal = JsonUtils.jsonify(animal, ['tests','vaccinations','label'])
error:
No signature of method: static org.JsonUtils.jsonify() is applicable for argument types: (org.Animal, java.util.ArrayList) values: [ ...]]\ Possible solutions: jsonify(java.lang.Object, java.util.ArrayList), jsonify(java.lang.Object), notify()
Also tried making the jsonify take an animal jsonify(Animal obj, ...) then it just said Possible solutions: jsonify(org.Animal, ...
The cleanJson method was meant to deal with JSONObject.Null things which have caused problems for us before.
example call:
def safeJson = JsonUtils.cleanJson(json) // json is request.JSON from the controller
error:
groovy.lang.MissingMethodException: No signature of method: static org.JsonUtils.cleanJson() is applicable for argument types: (org.codehaus.groovy.grails.web.json.JSONObject) values: [[...]]
Possible solutions: cleanJson(org.codehaus.groovy.grails.web.json.JSONObject)
All this code worked as it is when it was in service file. I am running grails 2.3.11 BTW
You've declared jsonify() and cleanJson() as instance methods and try to use them as static. Declare them as static and it should work:
class JsonUtils {
def static jsonify(obj, ArrayList removeableKeys = []) {
(...)
}
def static cleanJson(json) {
(...)
}
}
You need to define jsonify() and cleanJson() as static in order to call them statically.

Test RestfulController with Grails

I'm trying to write some integration tests for a RestfulController in Grails 2.4.0 responding in JSON format. The index()-Method is implemented like this:
class PersonController extends RestfulController<Person> {
...
def index(final Integer max) {
params.max = Math.min(max ?: 10, 100)
respond listAllResources(params), [includes: includeFields]
}
...
}
The integration test looks like this:
void testListAllPersons() {
def controller = new PersonController()
new Person(name: "Person", age: 22).save(flush:true)
new Person(name: "AnotherPerson", age: 31).save(flush:true)
controller.response.format = 'json'
controller.request.method = 'GET'
controller.index()
assertEquals '{{"name":"Person", "age": "22"},{"name":"AnotherPerson", "age": "31"}}', controller.response.json
}
What i don't understand is controller.response.json only contains the "AnotherPerson" instead of both entries.
When i start the server with run-app und test it with a Rest-Client i get both entries.
Any Ideas?
You haven't included enough information to say for sure what the problem is but the following test passes with 2.4.0.
The domain class:
// grails-app/domain/com/demo/Person.groovy
package com.demo
class Person {
String name
Integer age
}
The controller:
// grails-app/controllers/com/demo/PersonController.groovy
package com.demo
class PersonController extends grails.rest.RestfulController<Person> {
PersonController() {
super(Person)
}
def index(final Integer max) {
params.max = Math.min(max ?: 10, 100)
respond listAllResources(params)
}
}
The test:
// test/unit/com/demo/PersonControllerSpec.groovy
package com.demo
import grails.test.mixin.TestFor
import spock.lang.Specification
#TestFor(PersonController)
#Mock(Person)
class PersonControllerSpec extends Specification {
void "test index includes all people"() {
given:
new Person(name: "Person", age: 22).save(flush:true)
new Person(name: "AnotherPerson", age: 31).save(flush:true)
when:
request.method = 'GET'
response.format = 'json'
controller.index()
then:
response.status == 200
response.contentAsString == '[{"class":"com.demo.Person","id":1,"age":22,"name":"Person"},{"class":"com.demo.Person","id":2,"age":31,"name":"AnotherPerson"}]'
}
}
I simplified the example a little too much. I used a named object marshaller which i created (incorrect) in bootstrap.groovy like this:
JSON.createNamedConfig('simplePerson') { converterConfig ->
converterConfig.registerObjectMarshaller(Person) {
JSON.registerObjectMarshaller(Person) {
def map = [:]
map['name'] = it.name
map['age'] = it.age
return map
}
}
}
And used it in the controller:
...
JSON.use("simplePerson")
...
The problem is solved by creating the object marshaller like this:
JSON.createNamedConfig('simplePerson') { converterConfig ->
converterConfig.registerObjectMarshaller(Person) {
def map = [:]
map['name'] = it.name
map['age'] = it.age
return map
}
}

grails render collection as single json object

For example I have next Domain class
User{
String name
}
Also I have 2 objects of this class
new User(name: "John").save()
new User(name: "Alex").save()
How should look "list" action in UserController to represent User.list() in JSON format like this
{1: "John", 2: "Alex"}
Let me be more precise. I want something like this:
UserController{
def list = {
render(contentType: "text/json") {
User.list().each {user->
user.id = user.name
}
}
}
But sadly this isn't working.
Try the array structure,
def list = {
render(contentType: "text/json") {
results = array {
User.list().each {user->
result "${user.id}" : "${user.name}"
}
}
}
}
I couldn't find solution with JSONBuilder API. Because of that I made my solution with help of org.codehaus.jackson.
response.setContentType("text/json")
JsonGenerator g = jsonFactory.createJsonGenerator(response.getOutputStream())
g.writeStartObject()
for (user in users) {
g.writeStringField(user.id.toString(), user.name)
}
g.writeEndObject()
g.close()
When I want to encode something as JSON in grails, I put everything in maps:
render ['1':john.name, '2':alex.name] as JSON
Starting from #aldrin answer, a correction is needed for GRAILS3 json rendering because array directive is no more working (and is more correct 'application' instead of 'text'), so the solution must be
def list = {
def ulist = User.list()
render(contentType: "application/json") {
results(ulist) { user ->
userid user.id
username user.name
}
}
}