Spring REST,JSON“无法处理托管/反向引用‘defaultReference’”415 不受支持的媒体类型

Posted

技术标签:

【中文标题】Spring REST,JSON“无法处理托管/反向引用‘defaultReference’”415 不受支持的媒体类型【英文标题】:Spring REST, JSON "Can not handle managed/back reference 'defaultReference'" 415 Unsupported Media Type 【发布时间】:2015-03-26 14:13:10 【问题描述】:

我正在尝试使用 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"

当我发布以下 json 时,我得到 error 响应

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"

我已确保我的控制器具有正确的 Mediatype 注释。

@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);
    

经过一些研究并通过查看日志输出,我意识到这是一个误导性错误消息,问题实际上是在序列化/反序列化 Json 时发生

在日志文件中,我发现

2015-01-27 21:08:32.488 警告 15152 --- [nio-9095-exec-1] .c.j.MappingJackson2HttpMessageConverter:评估失败 类型[简单类型,用户类]的反序列化: java.lang.IllegalArgumentException:无法处理托管/返回 引用'defaultReference':反向引用类型(java.util.List)不是 兼容托管类型(用户)

这是我的类用户和类翻译(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.,但根据错误消息我无法弄清楚这里出了什么问题

【问题讨论】:

通过删除 @JsonManagedReference BUT keep` @JsonBackReference 解决了我的问题。 @Sharppoint,你的建议对我有用……很奇怪,但它奏效了! @Sharppoint 考虑将其添加为答案 @Sharppoint 的答案是正确的!也帮了我。 【参考方案1】:

正如@Sharppoint 在 cmets 中所说,我通过删除 @JsonManagedReference 但保留 @JsonBackReference 解决了这个问题。

【讨论】:

也为我工作。面临以下形式的异常:Cannot handle managed/back reference 'my-reference-name' value deserializer of type org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$UriStringDeserializer does not support them【参考方案2】:

对于那些询问的人, 另一种方法是使用 fastxml 的 JsonIdentityInfo 并使用以下内容注释您的类:

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;

*没有足够的代表发表评论。

【讨论】:

谢谢。对于代码。您可以从网络存档中获取死链接 (web.archive.org/web/20140314062757/http://…) 这对我有用,但我还必须为我的两个类添加一个唯一的 id,我通过 private final String id = UUID.randomUUID().toString(); 之类的方法来完成。跨度> 【参考方案3】:

我通过摆脱 JsonManagedReference 和 JsonBackReference 并用 JsonIdentityInfo 替换它来解决它

【讨论】:

我也面临同样的问题。您能否发布您在案例中如何使用 JsonIdentityInfo 的代码?谢谢! 嗨,我也有同样的问题。能否请您发布示例代码。 你是否删除了父类和子类中的所有 JsonManagedReference 和 JsonBackReference 注释?【参考方案4】:

你需要@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);
    

【讨论】:

很遗憾,它并没有解决问题。我还在调查,我的 JSON 帖子本身是否有问题 我猜如果 Jackson 无法将用户对象序列化为 JSON,则此评论适用。在这个问题中(在我的情况下),杰克逊似乎无法从 JSON 中反序列化输入的用户对象。【参考方案5】:

可以通过删除基类中的JsonManagedReference 来解决这个问题。 @JsonBackReference 会在从控制器获取/发布数据时无限地停止递归。

我假设您的 Language 类中有多个 @JsonBackReference。因此,当您发送包含两个类的用户数据时,spring 无法反序列化对象并相应地映射它。

您可以通过简单地从翻译/语言类中删除一个@JsonBackReference 并将其替换为@JsonIgnore/@JsonIdentityInfo 来解决此问题。

这样,您实际上是在执行相同的映射,但相反,您排除了多个@JsonBackReference 到基类,这被明确指出为导致415 Unsupported media type exception 的错误。

【讨论】:

【参考方案6】:

我有同样的错误,我解决了删除所有注释 @JsonBackReference 和 @JsonManagedReference,然后我将 @JsonIdentityInfo 放入所有具有关系的类中 检查Documentation

【讨论】:

【参考方案7】:

解决此问题的另一个好方法是使用@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 步。标记要公开(在任何相关/引用类中)到视图的属性,也使用 @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;

【讨论】:

以上是关于Spring REST,JSON“无法处理托管/反向引用‘defaultReference’”415 不受支持的媒体类型的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data Rest - 在 Json 中禁用自我链接(HAL)

Spring rest json 发布空值

无法从 Spring Boot REST 中的 Hibernate POJO 返回 JSON

Spring 4 mvc REST XML 和 JSON 响应

Spring REST API 中的 Json 模式验证

Spring Rest -> Hibernate 实体到 JSON