使用链接对象正确解组 Angular FormGroup POST
Properly de-marshall Angular FormGroup POST with linked objects
TL;DR;
在 Angular 2.x + Spring Data REST + Spring Boot 1.4 应用程序中,如何定义 JSON 对象Spring 可以解组 到域模型中的引用?
详情
例如,如果食谱书包含同时创建的 labels
和 recipes
,并且每个 recipe
都引用一个或多个 labels
,则代表可能看起来像:
book: {
title: 'Cook book',
labels:[{ <--- array index used in labelRef below
name: 'fruit',
description: 'Recipe with fruit.'
},{
name: 'vegetable',
description: 'Recipe with vegetables.'
},{
name: 'fish',
description: 'Recipe with fish.'
}],
recipes:[{
title: 'Sweet corn and onion salad',
description: 'Simple, quick, and refreshing corn salad',
labels: [{
labelRef: 1 <--- using array index as reference
}]
}]
}
其中labelRef
的数组使用labels
中的数组索引作为客户端标签标识符(需要保证数组顺序...)。由于 labels
和 recipes
同时发布,因此 label
或 recipe
都没有永久标识符。食谱书的 Angular 表单是动态构建的,因此用户可以根据需要添加任意数量的标签和食谱:
let book = this._fb.group({
title: this._fb.control(''),
labels: this._fb.array([this.buildLabel()]),
recipes: this._fb.array([this.buildRecipe()])
});
buildLabel(): FormGroup {
return this._fb.group({
name: this._fb.control(''),
description: this._fb.control('')
});
}
buildRecipe(): FormGroup {
return this._fb.group({
title: this._fb.control(''),
description: this._fb.control(''),
labels: this._fb.array([])
});
}
buildLabelReference(index: number): FormGroup {
return this._fb.group({
labelIndex: this._fb.control(index)
});
}
SpringData JPA + Lombok中的中间层表示Label
:
@Data
@Entity
@Table(name = "LABEL")
public class Label {
@Id
@Column(nullable = false)
private Long id;
private String name = "";
private String description = "";
@ManyToOne(optional=false)
@JoinColumn(name="book_id")
private Book book;
}
Recipe
:
@Data
@Entity
@Table(name = "RECIPE")
public class Recipe {
@Id
@Column(nullable = false)
private Long id;
private String title = "";
private String description = "";
@OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
@Column(nullable = false)
private List<Label> labels = new ArrayList<>();
@ManyToOne(optional=false)
@JoinColumn(name="book_id")
private Book book;
}
Book
:
@Data
@Entity
@Table(name = "BOOK")
public class Book {
@Id
@Column(nullable = false)
private Long id;
private String title = "";
@OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
@Column(nullable = false)
private Set<Label> labels = new HashSet<>();
@OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
@Column(nullable = false)
private List<Recipe> recipes = new ArrayList<>();
}
Book
在 @RepositoryRestController
中正确解编:
@Slf4j
@RepositoryRestController
public class BookRespositoryRestController {
private final BookRepository bookRepository;
private final LabelRepository labelRepository;
@RequestMapping(method = RequestMethod.POST,value = "/books")
public @ResponseBody PersistentEntityResource post(@RequestBody Book book,
PersistentEntityResourceAssembler assembler) {
Book entity = processPost(book);
return assembler.toResource(entity);
}
配方标签除外(因为领域模型不知道labelRef
)。
那么,我如何在我的 Angular TypeScript 应用程序中构造一个合适的 labelRef
,以便 Book
也解组每个 [=] 中的 label
链接20=] 对象?
TL;DR;
不幸的是,它(目前)还不受支持。但是您可以使用一些自定义代码来添加支持。
详情
有两个基本问题需要解决:定义一个JSON对象引用;并且,编码 client-side JSON 对象引用并解码 server-side JSON 对象引用。
对 JSON 个对象的引用
有很多关于 "standard" JSON 参考的提议(例如参见“Standard way of referencing an object by identity (for, eg, circular references)?”),但截至 2017 年没有 "standard"。
多个请求
如果可以拆分请求,请先提交引用的对象,然后使用唯一的相对 server-side 路径作为对对象的引用:
/labels/1: {
name: 'fruit',
description: 'Recipe with fruit.'
}
/labels/2: {
name: 'vegetable',
description: 'Recipe with vegetables.'
}
/labels/3: {
name: 'fish',
description: 'Recipe with fish.'
}
然后第二个请求使用 server-side 路径:
recipes:[{
title: 'Sweet corn and onion salad',
description: 'Simple, quick, and refreshing corn salad',
labels: [{
label: '/labels/1'
}]
}]
Spring Data REST 无需任何自定义代码即可支持此模型(尽管在更复杂的模型中您可能需要 patch-up 任何 bi-directional 指针)。
单个请求
如果您需要使用单个请求,请选择 JSON 对象引用模型并对您的引用进行相应编码。 server-side、de-marshall/de-serialize数据使用自定义代码。
Spring 数据 REST 支持 adding custom (de)serializers to Jackson's ObjectMapper。或者,数据传输对象可以执行 de-marhalling.
我使用了JSON路径并在服务器端向数据传输对象添加了de-serializer:
book: {
title: 'Cook book',
labels:[{
name: 'fruit',
description: 'Recipe with fruit.'
},{
name: 'vegetable',
description: 'Recipe with vegetables.'
},{
name: 'fish',
description: 'Recipe with fish.'
}],
recipes:[{
title: 'Sweet corn and onion salad',
description: 'Simple, quick, and refreshing corn salad',
labels: [{
label: '$..labels[1]' <--- using JSONPath reference
}]
}]
}
TL;DR;
在 Angular 2.x + Spring Data REST + Spring Boot 1.4 应用程序中,如何定义 JSON 对象Spring 可以解组 到域模型中的引用?
详情
例如,如果食谱书包含同时创建的 labels
和 recipes
,并且每个 recipe
都引用一个或多个 labels
,则代表可能看起来像:
book: {
title: 'Cook book',
labels:[{ <--- array index used in labelRef below
name: 'fruit',
description: 'Recipe with fruit.'
},{
name: 'vegetable',
description: 'Recipe with vegetables.'
},{
name: 'fish',
description: 'Recipe with fish.'
}],
recipes:[{
title: 'Sweet corn and onion salad',
description: 'Simple, quick, and refreshing corn salad',
labels: [{
labelRef: 1 <--- using array index as reference
}]
}]
}
其中labelRef
的数组使用labels
中的数组索引作为客户端标签标识符(需要保证数组顺序...)。由于 labels
和 recipes
同时发布,因此 label
或 recipe
都没有永久标识符。食谱书的 Angular 表单是动态构建的,因此用户可以根据需要添加任意数量的标签和食谱:
let book = this._fb.group({
title: this._fb.control(''),
labels: this._fb.array([this.buildLabel()]),
recipes: this._fb.array([this.buildRecipe()])
});
buildLabel(): FormGroup {
return this._fb.group({
name: this._fb.control(''),
description: this._fb.control('')
});
}
buildRecipe(): FormGroup {
return this._fb.group({
title: this._fb.control(''),
description: this._fb.control(''),
labels: this._fb.array([])
});
}
buildLabelReference(index: number): FormGroup {
return this._fb.group({
labelIndex: this._fb.control(index)
});
}
SpringData JPA + Lombok中的中间层表示Label
:
@Data
@Entity
@Table(name = "LABEL")
public class Label {
@Id
@Column(nullable = false)
private Long id;
private String name = "";
private String description = "";
@ManyToOne(optional=false)
@JoinColumn(name="book_id")
private Book book;
}
Recipe
:
@Data
@Entity
@Table(name = "RECIPE")
public class Recipe {
@Id
@Column(nullable = false)
private Long id;
private String title = "";
private String description = "";
@OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
@Column(nullable = false)
private List<Label> labels = new ArrayList<>();
@ManyToOne(optional=false)
@JoinColumn(name="book_id")
private Book book;
}
Book
:
@Data
@Entity
@Table(name = "BOOK")
public class Book {
@Id
@Column(nullable = false)
private Long id;
private String title = "";
@OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
@Column(nullable = false)
private Set<Label> labels = new HashSet<>();
@OneToMany(cascade={CascadeType.ALL}, mappedBy="book", orphanRemoval=true)
@Column(nullable = false)
private List<Recipe> recipes = new ArrayList<>();
}
Book
在 @RepositoryRestController
中正确解编:
@Slf4j
@RepositoryRestController
public class BookRespositoryRestController {
private final BookRepository bookRepository;
private final LabelRepository labelRepository;
@RequestMapping(method = RequestMethod.POST,value = "/books")
public @ResponseBody PersistentEntityResource post(@RequestBody Book book,
PersistentEntityResourceAssembler assembler) {
Book entity = processPost(book);
return assembler.toResource(entity);
}
配方标签除外(因为领域模型不知道labelRef
)。
那么,我如何在我的 Angular TypeScript 应用程序中构造一个合适的 labelRef
,以便 Book
也解组每个 [=] 中的 label
链接20=] 对象?
TL;DR;
不幸的是,它(目前)还不受支持。但是您可以使用一些自定义代码来添加支持。
详情
有两个基本问题需要解决:定义一个JSON对象引用;并且,编码 client-side JSON 对象引用并解码 server-side JSON 对象引用。
对 JSON 个对象的引用
有很多关于 "standard" JSON 参考的提议(例如参见“Standard way of referencing an object by identity (for, eg, circular references)?”),但截至 2017 年没有 "standard"。
多个请求
如果可以拆分请求,请先提交引用的对象,然后使用唯一的相对 server-side 路径作为对对象的引用:
/labels/1: {
name: 'fruit',
description: 'Recipe with fruit.'
}
/labels/2: {
name: 'vegetable',
description: 'Recipe with vegetables.'
}
/labels/3: {
name: 'fish',
description: 'Recipe with fish.'
}
然后第二个请求使用 server-side 路径:
recipes:[{
title: 'Sweet corn and onion salad',
description: 'Simple, quick, and refreshing corn salad',
labels: [{
label: '/labels/1'
}]
}]
Spring Data REST 无需任何自定义代码即可支持此模型(尽管在更复杂的模型中您可能需要 patch-up 任何 bi-directional 指针)。
单个请求
如果您需要使用单个请求,请选择 JSON 对象引用模型并对您的引用进行相应编码。 server-side、de-marshall/de-serialize数据使用自定义代码。
Spring 数据 REST 支持 adding custom (de)serializers to Jackson's ObjectMapper。或者,数据传输对象可以执行 de-marhalling.
我使用了JSON路径并在服务器端向数据传输对象添加了de-serializer:
book: {
title: 'Cook book',
labels:[{
name: 'fruit',
description: 'Recipe with fruit.'
},{
name: 'vegetable',
description: 'Recipe with vegetables.'
},{
name: 'fish',
description: 'Recipe with fish.'
}],
recipes:[{
title: 'Sweet corn and onion salad',
description: 'Simple, quick, and refreshing corn salad',
labels: [{
label: '$..labels[1]' <--- using JSONPath reference
}]
}]
}