Spring REST,JSON "Can not handle managed/back reference 'defaultReference'" 415 不支持的媒体类型
Spring REST, JSON "Can not handle managed/back reference 'defaultReference'" 415 Unsupported Media Type
我正在尝试使用 Spring boot/Spring RestController 后端从 AngularJS 前端 POST 到 http://localhost:9095/translators。
我可以执行 GET,响应如下:
[{"userId":1,"firstName":"John","lastName":"Doe","emailId":"john.doe@inc.com","languages":[{"languageId":1,"languageCode":"gb","source":true}],"translations":[{"translationId":3,"sourceId":1,"sourceText":"Hello","targetId":null,"targetText":null,"translationStatus":"DUE"}],"userType":"TRANSLATOR"}
当我 post 以下 json 时,我得到 错误 响应
POST数据:
{
firstName: "zen",
lastName: "cv",
emailId: "email",
userType: "TRANSLATOR",
languages : [{languageId:1,languageCode:"gb",source:true}]
}
错误:
{
timestamp: 1422389312497
status: 415
error: "Unsupported Media Type"
exception: "org.springframework.web.HttpMediaTypeNotSupportedException"
message: "Content type 'application/json' not supported"
path: "/translators"
}
我已确保我的控制器具有正确的媒体类型注释。
@RestController
@RequestMapping("/translators")
public class TranslatorController {
@Autowired
private UserRepository repository;
@RequestMapping(method = RequestMethod.GET)
public List findUsers() {
return repository.findAll();
}
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User findUser(@PathVariable Long userId) {
return repository.findOne(userId);
}
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public User addTranslator(@RequestBody User user) {
//translation.setTranslationId(null);
return repository.saveAndFlush(user);
}
@RequestMapping(value = "/{translatorId}", method = RequestMethod.PUT)
public User updateTranslation(@RequestBody User updatedUser, @PathVariable Long userId) {
//updatedTranslation.setTranslationId(translationId);
return repository.saveAndFlush(updatedUser);
}
@RequestMapping(value = "/{translatorId}", method = RequestMethod.DELETE)
public void deleteTranslation(@PathVariable Long translationId) {
repository.delete(translationId);
}
}
经过一些研究并查看日志输出,我意识到这是一个误导性的错误消息,问题实际上是在 serializing/deserializing Json
时发生的
在日志文件中,我发现
2015-01-27 21:08:32.488 WARN 15152 --- [nio-9095-exec-1]
.c.j.MappingJackson2HttpMessageConverter : Failed to evaluate
deserialization for type [simple type, class User]:
java.lang.IllegalArgumentException: Can not handle managed/back
reference 'defaultReference': back reference type (java.util.List) not
compatible with managed type (User)
这是我的 class 用户和 class 翻译(getter、setter、构造函数等。为简洁起见省略)
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name = "user_id")
private long userId;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email_id")
private String emailId;
@ManyToMany
@JoinTable(name = "languages_users", joinColumns = { @JoinColumn(name = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "lang_id")})
@JsonManagedReference
private List<Language> languages = new ArrayList<Language>();
@OneToMany(mappedBy = "translator", fetch = FetchType.EAGER)
@JsonManagedReference
private List<Translation> translations;
@Enumerated(EnumType.STRING)
private UserType userType;
}
@Entity
@Table(name = "translations")
public class Translation {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name = "translation_id")
private Long translationId;
@Column(name = "source_lang_id")
private Long sourceId;
@Column(name = "source_text")
private String sourceText;
@Column(name = "target_lang_id")
private Long targetId;
@Column(name = "target_text")
private String targetText;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private TranslationStatus translationStatus;
@ManyToOne
@JoinColumn(name = "translator_id")
@JsonBackReference
private User translator;
}
我的问题是:如何为上述实体正确设置JsonManagedReference 和JsonBackReference?我确实阅读了doc.,但根据错误消息
我无法弄清楚这里出了什么问题
您需要 @ResponseBody
注释,如下所示:
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody public User addTranslator(@RequestBody User user) {
//translation.setTranslationId(null);
return repository.saveAndFlush(user);
}
我通过摆脱 JsonManagedReference 和 JsonBackReference 并将其替换为 JsonIdentityInfo 解决了这个问题
对于那些提问的人,
另一种方法是使用 fasterxml 的 JsonIdentityInfo 并用以下内容注释您的 class:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Account implements java.io.Serializable {
....
private Long id;
}
*没有足够的代表发表评论。
正如@Sharppoint 在评论中所说,我通过删除 @JsonManagedReference
但保留 @JsonBackReference
.
解决了我的问题
我有同样的错误,我解决了删除所有注释@JsonBackReference 和@JsonManagedReference,然后我将@JsonIdentityInfo 放在所有有关系的类中
检查 Documentation
可以通过删除基数 class 中的 JsonManagedReference
来解决此问题。 @JsonBackReference
停止无限递归,而 getting/posting 来自控制器的数据。
我假设您的语言 class 中有多个 @JsonBackReference
。因此,当您发送包含两个 class 的用户数据时,spring 无法反序列化对象并相应地映射它。
您只需从 Translation/Language class 中删除其中一个 @JsonBackReference
并将其替换为 @JsonIgnore
/@JsonIdentityInfo
即可解决此问题。
这样,您实际上是在做相同的映射,但是您排除了 @JsonBackReference
到基数 class 的多个 class ,这被明确指出是导致 [=17 的错误=].
另一个解决这个问题的好方法是 @JsonView
。这个想法是你用视图名称标记你的控制器,然后标记你希望为该视图显示的属性。您特别不要将 backreferenced 属性公开给调用视图。下面是一个极其简化的例子:
想象一下你有这样一对一的关系。这将创建一个循环引用。
@Entity
public class Student {
String name;
Tutor tutor;
}
@Entity
public class Tutor {
String name;
Student student;
}
您现在可以为它们创建视图,几乎与您为 @JsonIgnore
和 @JsonProperty
所做的一样。
第 1 步。创建可用于标记控制器的任意空接口。
public class LearningController {
@GetRequest("/tutors")
@JsonView(TutorView.class) // create an empty interface with a useful name
public Set<Tutor> tutors() {
return tutorRepository.findAll()
}
@GetRequest("/students")
@JsonView(StudentView.class) // create an empty interface with a useful name
public Set<Student> students() {
return studentRepository.findAll()
}
}
第 2 步。标记要向视图公开的属性(在任何相关/引用的 class 中),也使用 @JsonView
注释。
@Entity
public class Student {
@JsonView({StudentView.class, TutorView.class})
String name;
@JsonView({StudentView.class}) // Not visible to @TutorView (no backreference)
Tutor tutor;
}
@Entity
public class Tutor {
@JsonView({StudentView.class, TutorView.class})
String name;
@JsonView(TutorView.class) // Not visible to @StudentView (no backreference)
Student student;
}
我通过添加 @JsonIgnore
解决了类似的问题
我正在尝试使用 Spring boot/Spring RestController 后端从 AngularJS 前端 POST 到 http://localhost:9095/translators。
我可以执行 GET,响应如下:
[{"userId":1,"firstName":"John","lastName":"Doe","emailId":"john.doe@inc.com","languages":[{"languageId":1,"languageCode":"gb","source":true}],"translations":[{"translationId":3,"sourceId":1,"sourceText":"Hello","targetId":null,"targetText":null,"translationStatus":"DUE"}],"userType":"TRANSLATOR"}
当我 post 以下 json 时,我得到 错误 响应
POST数据:
{
firstName: "zen",
lastName: "cv",
emailId: "email",
userType: "TRANSLATOR",
languages : [{languageId:1,languageCode:"gb",source:true}]
}
错误:
{
timestamp: 1422389312497
status: 415
error: "Unsupported Media Type"
exception: "org.springframework.web.HttpMediaTypeNotSupportedException"
message: "Content type 'application/json' not supported"
path: "/translators"
}
我已确保我的控制器具有正确的媒体类型注释。
@RestController
@RequestMapping("/translators")
public class TranslatorController {
@Autowired
private UserRepository repository;
@RequestMapping(method = RequestMethod.GET)
public List findUsers() {
return repository.findAll();
}
@RequestMapping(value = "/{userId}", method = RequestMethod.GET)
public User findUser(@PathVariable Long userId) {
return repository.findOne(userId);
}
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public User addTranslator(@RequestBody User user) {
//translation.setTranslationId(null);
return repository.saveAndFlush(user);
}
@RequestMapping(value = "/{translatorId}", method = RequestMethod.PUT)
public User updateTranslation(@RequestBody User updatedUser, @PathVariable Long userId) {
//updatedTranslation.setTranslationId(translationId);
return repository.saveAndFlush(updatedUser);
}
@RequestMapping(value = "/{translatorId}", method = RequestMethod.DELETE)
public void deleteTranslation(@PathVariable Long translationId) {
repository.delete(translationId);
}
}
经过一些研究并查看日志输出,我意识到这是一个误导性的错误消息,问题实际上是在 serializing/deserializing Json
时发生的在日志文件中,我发现
2015-01-27 21:08:32.488 WARN 15152 --- [nio-9095-exec-1] .c.j.MappingJackson2HttpMessageConverter : Failed to evaluate deserialization for type [simple type, class User]: java.lang.IllegalArgumentException: Can not handle managed/back reference 'defaultReference': back reference type (java.util.List) not compatible with managed type (User)
这是我的 class 用户和 class 翻译(getter、setter、构造函数等。为简洁起见省略)
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name = "user_id")
private long userId;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
@Column(name = "email_id")
private String emailId;
@ManyToMany
@JoinTable(name = "languages_users", joinColumns = { @JoinColumn(name = "user_id")},
inverseJoinColumns = {@JoinColumn(name = "lang_id")})
@JsonManagedReference
private List<Language> languages = new ArrayList<Language>();
@OneToMany(mappedBy = "translator", fetch = FetchType.EAGER)
@JsonManagedReference
private List<Translation> translations;
@Enumerated(EnumType.STRING)
private UserType userType;
}
@Entity
@Table(name = "translations")
public class Translation {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name = "translation_id")
private Long translationId;
@Column(name = "source_lang_id")
private Long sourceId;
@Column(name = "source_text")
private String sourceText;
@Column(name = "target_lang_id")
private Long targetId;
@Column(name = "target_text")
private String targetText;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private TranslationStatus translationStatus;
@ManyToOne
@JoinColumn(name = "translator_id")
@JsonBackReference
private User translator;
}
我的问题是:如何为上述实体正确设置JsonManagedReference 和JsonBackReference?我确实阅读了doc.,但根据错误消息
我无法弄清楚这里出了什么问题您需要 @ResponseBody
注释,如下所示:
@RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody public User addTranslator(@RequestBody User user) {
//translation.setTranslationId(null);
return repository.saveAndFlush(user);
}
我通过摆脱 JsonManagedReference 和 JsonBackReference 并将其替换为 JsonIdentityInfo 解决了这个问题
对于那些提问的人, 另一种方法是使用 fasterxml 的 JsonIdentityInfo 并用以下内容注释您的 class:
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Account implements java.io.Serializable {
....
private Long id;
}
*没有足够的代表发表评论。
正如@Sharppoint 在评论中所说,我通过删除 @JsonManagedReference
但保留 @JsonBackReference
.
我有同样的错误,我解决了删除所有注释@JsonBackReference 和@JsonManagedReference,然后我将@JsonIdentityInfo 放在所有有关系的类中 检查 Documentation
可以通过删除基数 class 中的 JsonManagedReference
来解决此问题。 @JsonBackReference
停止无限递归,而 getting/posting 来自控制器的数据。
我假设您的语言 class 中有多个 @JsonBackReference
。因此,当您发送包含两个 class 的用户数据时,spring 无法反序列化对象并相应地映射它。
您只需从 Translation/Language class 中删除其中一个 @JsonBackReference
并将其替换为 @JsonIgnore
/@JsonIdentityInfo
即可解决此问题。
这样,您实际上是在做相同的映射,但是您排除了 @JsonBackReference
到基数 class 的多个 class ,这被明确指出是导致 [=17 的错误=].
另一个解决这个问题的好方法是 @JsonView
。这个想法是你用视图名称标记你的控制器,然后标记你希望为该视图显示的属性。您特别不要将 backreferenced 属性公开给调用视图。下面是一个极其简化的例子:
想象一下你有这样一对一的关系。这将创建一个循环引用。
@Entity
public class Student {
String name;
Tutor tutor;
}
@Entity
public class Tutor {
String name;
Student student;
}
您现在可以为它们创建视图,几乎与您为 @JsonIgnore
和 @JsonProperty
所做的一样。
第 1 步。创建可用于标记控制器的任意空接口。
public class LearningController {
@GetRequest("/tutors")
@JsonView(TutorView.class) // create an empty interface with a useful name
public Set<Tutor> tutors() {
return tutorRepository.findAll()
}
@GetRequest("/students")
@JsonView(StudentView.class) // create an empty interface with a useful name
public Set<Student> students() {
return studentRepository.findAll()
}
}
第 2 步。标记要向视图公开的属性(在任何相关/引用的 class 中),也使用 @JsonView
注释。
@Entity
public class Student {
@JsonView({StudentView.class, TutorView.class})
String name;
@JsonView({StudentView.class}) // Not visible to @TutorView (no backreference)
Tutor tutor;
}
@Entity
public class Tutor {
@JsonView({StudentView.class, TutorView.class})
String name;
@JsonView(TutorView.class) // Not visible to @StudentView (no backreference)
Student student;
}
我通过添加 @JsonIgnore
解决了类似的问题