作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在使用Grails和AngularJS创建RESTful twitter / facebook克隆,因此它是标准用户可以发布,用户可以喜欢发布,并且用户可以关注其他用户。
我正在使用JSON对象编码器,以便 Realm 类的hasMany
或belongsTo
的属性将在JSON中呈现。
我已经为类似功能在User
和Post
域之间插入了必要的关系,当我在Bootstrap.groovy
中实现它们时,如果我向GET
发送了api/posts/
请求,则一切正常,但问题是当我使用按钮实现它们时。
此按钮将PUT
请求发送到api/posts/:id
,然后转到我的update()
的PostController
方法。像被插入到数据库中,但是如果我再次向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
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)
}
}
}
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
}
}
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 = {
}
}
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)
}
}
}
hasMany
的属性。所以我想主要的问题是JSON对象编码器。我执行错误吗?还是有使用JSON对象编码器的替代方法?但是再次奏效,因为我在
Bootstrap.groovy
中实现了它们
post.save(flush:true)
更改为
post.save()
,它不会发出错误,但类似内容未保存在数据库中。
GET
的
api/posts/
请求
[
{
"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
}
}
]
[
{
"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"
}
]
}
]
}
]
最佳答案
我刚刚找到了解决方案。只需将此代码插入Config.groovy
grails.converters.json.circular.reference.behaviour = "INSERT_NULL"
我在这篇文章的评论之一中看到了它http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-object-marshallers/
关于json - 错误error.GrailsExceptionResolver-JSONException的endArray放错了位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32788143/
我是一名优秀的程序员,十分优秀!