i making restful twitter/facebook clone using grails , angularjs, it's standard user can post, user can posts , user can follow other users.
i using json object marshallers attributes of hasmany
or belongsto
of domain class rendered in json.
i have inserted necessary relationships between user
, post
domain feature , when implement them in bootstrap.groovy
, if send get
request api/posts/
works fine problem when implement them using button. button sends put
request api/posts/:id
, goes update()
method of postcontroller
. being inserted database if send again get
request api/posts
error
....error | 2015-09-26 00:54:35,986 [http-bio-8090-exec-9] error errors.grailsexceptionresolver - jsonexception occurred when processing request: [get] /restsocnet/api/posts misplaced endarray.. stacktrace follows: message: misplaced endarray. line | method ->> 202 | value in grails.converters.json - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 162 | convertanother in '' | 202 | value . . . . . . in '' | 134 | render in '' | 150 | render . . . . . . in '' | 19 | index in com.patrickjuen.restsocnet.postcontroller | 198 | dofilter . . . . . in grails.plugin.cache.web.filter.pagefragmentcachingfilter | 63 | dofilter in grails.plugin.cache.web.filter.abstractfilter | 118 | processfilterchain in grails.plugin.springsecurity.rest.resttokenvalidationfilter | 84 | dofilter in '' | 53 | dofilter . . . . . in grails.plugin.springsecurity.web.filter.grailsanonymousauthenticationfilter | 143 | dofilter in grails.plugin.springsecurity.rest.restauthenticationfilter | 62 | dofilter . . . . . in grails.plugin.springsecurity.web.authentication.logout.mutablelogoutfilter | 82 | dofilter in com.brandseye.cors.corsfilter | 1142 | runworker . . . . in java.util.concurrent.threadpoolexecutor | 617 | run in java.util.concurrent.threadpoolexecutor$worker ^ 745 | run . . . . . . . in java.lang.thread
these codes
user.groovy
package com.patrickjuen.restsocnet class user implements serializable { private static final long serialversionuid = 1 transient springsecurityservice string username string password boolean enabled = true boolean accountexpired boolean accountlocked boolean passwordexpired static hasmany = [posts: post, likedpost: post] static mappedby = [posts: "user"] user(string username, string password) { this() this.username = username this.password = password } @override int hashcode() { username?.hashcode() ?: 0 } @override boolean equals(other) { is(other) || (other instanceof user && other.username == username) } @override string tostring() { username } set<role> getauthorities() { userrole.findallbyuser(this)*.role } def beforeinsert() { encodepassword() } def beforeupdate() { if (isdirty('password')) { encodepassword() } } protected void encodepassword() { password = springsecurityservice?.passwordencoder ? springsecurityservice.encodepassword(password) : password } static transients = ['springsecurityservice'] static constraints = { username blank: false, unique: true password blank: false likedpost nullable: true } static mapping = { password column: '`password`' posts lazy: false, sort: 'datecreated', order: 'desc' likedpost lazy: false } } package com.patrickjuen.restsocnet import grails.converters.json //import grails.plugin.springsecurity.annotation.secured import org.springframework.security.access.annotation.secured @secured(['isfullyauthenticated()']) class postcontroller { static allowedmethods = [save: "post", update: "put", delete: "delete"] def springsecurityservice def index() { render post.list(sort: "datecreated", order: "desc") json } def save(){ def newpost = new post(request.json) if(!newpost.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) println currentuser newpost.user = currentuser newpost.save(failonerror: true) // currentuser.addtoposts(newpost) render (['success': true] json) } } def show(){ def post = post.get(params.id) render post json } def update(){ def post = post.findbyid(params.id) if(!post.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) post.addtolikers(currentuser) post.save(flush: true) render(['success': true] json) } } }
post.groovy
package com.patrickjuen.restsocnet class post { string content date datecreated user user static belongsto = user static hasmany = [likers: user] // static mappedby = [likers: "likedpost"] static constraints = { likers nullable: true } static mapping = { likers lazy: false } }
bootstrap.groovy
import com.patrickjuen.restsocnet.post import com.patrickjuen.restsocnet.role import com.patrickjuen.restsocnet.user import com.patrickjuen.restsocnet.userrole import grails.converters.json class bootstrap { def init = { servletcontext -> json.registerobjectmarshaller(user) { def returnarray = [:] returnarray['id'] = it.id returnarray['username'] = it.username returnarray['posts'] = it.posts return returnarray } json.registerobjectmarshaller(post) { def returnarray = [:] returnarray['id'] = it.id returnarray['content'] = it.content returnarray['datecreated'] = it.datecreated returnarray['user'] = it.user returnarray['likers'] = it.likers return returnarray } def role = new role(authority: "role_user") def user1 = new user(username: "user1", password: "password") def user2 = new user(username: "user2", password: "password") role.save() user1.save() user2.save() def post1 = new post(content: "new post number 1") def post2 = new post(content: "new post number 2") def post3 = new post(content: "one more ") def post4 = new post(content: "i user2 guys hehehe") post1.save() post2.save() post3.save() post4.save() user1.addtoposts(post1) user1.addtoposts(post2) user1.addtoposts(post3) user2.addtoposts(post4) post2.addtolikers(user2) post2.addtolikers(user1) post1.addtolikers(user1) userrole.create(user1, role, true) userrole.create(user2, role, true) } def destroy = { } }
postcontroller.groovy
package com.patrickjuen.restsocnet import grails.converters.json //import grails.plugin.springsecurity.annotation.secured import org.springframework.security.access.annotation.secured @secured(['isfullyauthenticated()']) class postcontroller { static allowedmethods = [save: "post", update: "put", delete: "delete"] def springsecurityservice def index() { render post.list(sort: "datecreated", order: "desc") json } def save(){ def newpost = new post(request.json) if(!newpost.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) println currentuser newpost.user = currentuser newpost.save(failonerror: true) // currentuser.addtoposts(newpost) render (['success': true] json) } } def show(){ def post = post.get(params.id) render post json } def update(){ def post = post.findbyid(params.id) if(!post.haserrors()){ def currentuser = user.get(springsecurityservice.principal.id) post.addtolikers(currentuser) post.save(flush: true) render(['success': true] json) } } }
i tried removing json object marshallers , doesn't give out error anymore won't able access attributes of hasmany
. guess main problem json object marshallers. implementing them wrongly? or there alternative using json object marshallers? yet again works fine implement them in bootstrap.groovy
also, tried changing post.save(flush:true)
post.save()
doesn't give out error not being saved in database.
just in case interested in json being rendered. get
request api/posts/
without json object marshallers
[ { "class": "com.patrickjuen.restsocnet.post", "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:39:10z", "likers": [], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 2 } }, { "class": "com.patrickjuen.restsocnet.post", "id": 3, "content": "one more", "datecreated": "2015-09-25t17:39:10z", "likers": [], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 1 } }, { "class": "com.patrickjuen.restsocnet.post", "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:39:10z", "likers": [ { "class": "com.patrickjuen.restsocnet.user", "id": 1 }, { "class": "com.patrickjuen.restsocnet.user", "id": 2 } ], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 1 } }, { "class": "com.patrickjuen.restsocnet.post", "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:39:10z", "likers": [ { "class": "com.patrickjuen.restsocnet.user", "id": 1 } ], "user": { "class": "com.patrickjuen.restsocnet.user", "id": 1 } } ]
with json object marshallers
[ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 2, "username": "user2", "posts": [ { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" } ] }, "likers": [] }, { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 1, "username": "user1", "posts": [ { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" } ] } ] }, "likers": [] }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" } ] } ] }, "likers": [ { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.post" }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" } ] } ] }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "id": 1, "content": "new post number 1", "datecreated": "2015-09-25t17:50:58z", "user": { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.post" } ] }, "likers": [ { "id": 1, "username": "user1", "posts": [ { "id": 3, "content": "one more", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] }, { "id": 2, "content": "new post number 2", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [ { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.user_$$_javassist_5" }, { "id": 2, "username": "user2", "posts": [ { "id": 4, "content": "i user2 guys hehehe", "datecreated": "2015-09-25t17:50:58z", "user": { "_ref": "../../../..", "class": "com.patrickjuen.restsocnet.user" }, "likers": [] } ] } ] }, { "_ref": "../../../../..", "class": "com.patrickjuen.restsocnet.post" } ] } ] } ]
i found solution. code needs inserted in config.groovy
grails.converters.json.circular.reference.behaviour = "insert_null"
i saw in 1 of comments in article http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-object-marshallers/
Comments
Post a Comment