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

时间:2015-01-27 20:27:35

标签: java json hibernate jackson spring-boot

我正在尝试使用Spring boot / Spring RestController后端从AngularJS前端发送到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时,我收到错误响应

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 WARN 15152 --- [nio-9095-exec-1]   .c.j.MappingJackson2HttpMessageConverter:无法评估   类型[simple type,class User]的反序列化:   java.lang.IllegalArgumentException:无法处理托管/返回   引用'defaultReference':后引用类型(java.util.List)不   与托管类型(用户)兼容

这是我的班级用户和班级翻译(为了简洁省略了getter,setter,constructor等)

@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.,根据错误消息

,我无法弄清楚这里有什么问题

7 个答案:

答案 0 :(得分:10)

正如@Sharppoint在评论中所说,我通过删除insertBefore但保留@JsonManagedReference解决了我的问题。

答案 1 :(得分:9)

对于那些问, 另一种方法是使用rapidxml的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;
}

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

答案 2 :(得分:6)

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

答案 3 :(得分: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);
    }

答案 4 :(得分:0)

我有同样的错误,我解决了删除所有注释@JsonBackReference和@JsonManagedReference,然后我把@JsonIdentityInfo放在我的所有关系中 查看Documentation

答案 5 :(得分:0)

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

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

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

这样,您实际上是在进行相同的映射,但是,您排除了对基类的多个@JsonBackReference,这显然是导致415 Unsupported media type exception的错误。

答案 6 :(得分:0)

解决此问题的另一种好方法是使用@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;
}