我正在使用Grails和AngularJS一个RESTful的Twitter/Facebook的克隆,所以这是一个标准的用户可以发布,用户可以像帖子和用户可以关注其他用户。错误errors.GrailsExceptionResolver - JSONException错位endArray
我正在使用JSON对象编组器,因此将在JSON中呈现域类的hasMany
或belongsTo
的属性。
我插入了User
和Post
域之间建立必要的关系对类似的功能,当我在Bootstrap.groovy
实现他们,如果我发送GET
请求api/posts/
一切工作正常,但问题是,当我使用实现它们按钮。 此按钮发送PUT
请求api/posts/:id
和这个去我PostController
的update()
方法。等被插入到数据库,但如果我再传送GET
请求api/posts
我得到这个错误
....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
这是我的代码
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") as 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] as JSON)
}
}
def show(){
def post = Post.get(params.id)
render post as 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] as 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 am 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") as 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] as JSON)
}
}
def show(){
def post = Post.get(params.id)
render post as 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] as JSON)
}
}
}
我试图消除JSON对象marshallers,并没有再给出错误,但我将无法访问属性hasMany
。所以我猜主要的问题是JSON对象marshallers。我是否错误地执行它们?或者有使用JSON对象编组的替代方法吗?但又一次它工作得很好,因为我实现它们在Bootstrap.groovy
另外,我试图改变post.save(flush:true)
只是post.save()
它并没有给出来的错误,但类似不被保存在数据库中。
以防万一您对JSON的渲染感兴趣。 这是一个GET
请求api/posts/
没有JSON对象marshallers
[
{
"class": "com.patrickjuen.restsocnet.Post",
"id": 4,
"content": "i am 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
}
}
]
使用JSON对象marshallers
[
{
"id": 4,
"content": "i am 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 am 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 am 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 am 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 am user2 guys hehehe",
"dateCreated": "2015-09-25T17:50:58Z",
"user": {
"_ref": "../../../..",
"class": "com.patrickjuen.restsocnet.User"
},
"likers": []
}
]
}
]
},
{
"_ref": "../../../../..",
"class": "com.patrickjuen.restsocnet.Post"
}
]
}
]
}
]
对此有何更新的评论之一插入?我还没有发现任何工作。我尝试过使用GSON并使用深度。我认为这可能与级联有关,但我不确定。 – uLan