如果使用 JsonSubTypes,HATEOAS 链接是错误的
HATEOAS links are wrong if using JsonSubTypes
我正在使用 spring 数据休息 Mongo 来公开具有多个子类型的 class。当我这样做时,HATEOAS 会根据实际实例化类型而不是公共基类型来划分结果。这会导致链接不正确,并导致分页无用,因为它是一个混合类型列表。
我已经尝试将 @Relation 标记显式添加到所有涉及的 classes 中,但它似乎根本没有任何影响。无论有没有它,我得到的结果都是一样的。
我正在使用 spring 启动依赖项 2.1.8.RELEASE 以及 spring-boot-starter-data-rest 和 spring-cloud-dependencies Greenwich.SR1
基础class:
@Relation(collectionRelation = "notifications", value="notifications")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "notificationType")
@JsonSubTypes({
@JsonSubTypes.Type(value = ModelNotification.class, name = Notification.MODEL_NOTIFICATION),
@JsonSubTypes.Type(value = BasicNotification.class, name = Notification.BASIC_NOTIFICATION)
})
public class Notification extends UUIDEntity implements Serializable {
private static final long serialVersionUID = 8199210081144334378L;
public static final String MODEL_NOTIFICATION = "MODEL_NOTIFICATION";
public static final String BASIC_NOTIFICATION = "BASIC_NOTIFICATION";
public enum Severity {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
}
public Notification() {
this.notificationType = BASIC_NOTIFICATION;
}
@JsonProperty("notificationType")
private String notificationType;
@JsonProperty("createdDate")
@CreatedDate
private Instant createdDate;
@JsonProperty("lastModifiedDate")
@LastModifiedDate
private Instant lastModifiedDate;
@JsonProperty("createdBy")
@CreatedBy
private String createdBy;
@JsonProperty("lastModifiedBy")
@LastModifiedBy
private String lastModifiedBy;
@JsonProperty("severity")
private Severity severity;
@JsonProperty("message")
private String message;
无附加会员版本:
@Relation(collectionRelation = "notifications", value="notifications")
public class BasicNotification extends Notification implements Serializable {
private static final long serialVersionUID = 8063077545983014320L;
}
以及扩展版本:
@Relation(collectionRelation = "notifications", value="notifications")
public class ModelNotification extends Notification implements Serializable {
private static final long serialVersionUID = 3700576594274374440L;
@JsonProperty("storedModel")
private StoredModel storedModel;
public ModelNotification() {
super();
this.setNotificationType(Notification.MODEL_NOTIFICATION);
}
我希望,如果添加 @Relation 标记,所有结果都会出现在通知下,这是 spring 数据休息端点的正确 url。请注意,所有端点都正常工作,但只有 HATEOAS 部分不正确,并且捆绑会产生问题。访问时:/api/notifications
我回来了:
{
"_embedded" : {
"modelNotifications" : [ {
"notificationType" : "MODEL_NOTIFICATION",
"createdDate" : "2019-10-02T15:53:42.127Z",
"lastModifiedDate" : "2019-10-02T15:53:42.127Z",
...
[SNIP FOR BREVITY]
...
} ]
},
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/modelNotification/ef81c342-29d3-48fb-bab3-d416e80bc5f6"
},
"modelNotification" : {
"href" : "http://fastscore:8088/api/modelNotification/ef81c342-29d3-48fb-bab3-d416e80bc5f6"
}
}
} ],
"notifications" : [ {
"notificationType" : "BASIC_NOTIFICATION",
"createdDate" : "2019-10-02T15:52:10.261Z",
"lastModifiedDate" : "2019-10-02T15:52:10.261Z",
"createdBy" : "anonymousUser",
"lastModifiedBy" : "anonymousUser",
"severity" : "INFO",
"message" : "Interval Process Completed Successfully",
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/notifications/93fa5d6b-1457-4fa6-976c-cfdddc422976"
},
"notification" : {
"href" : "http://fastscore:8088/api/notifications/93fa5d6b-1457-4fa6-976c-cfdddc422976"
}
}
...
[SNIP]
...
},
},
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/notifications{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://fastscore:8088/api/profile/notifications"
},
"search" : {
"href" : "http://fastscore:8088/api/notifications/search"
}
},
"page" : {
"size" : 20,
"totalElements" : 4,
"totalPages" : 1,
"number" : 0
}
}
这显然不正确,因为 modelNotifications 不是 spring 数据休息端点。此外,这会使分页变得无用,就好像我有大量通知一样,分页仅适用于那些,即使我只有一个,我每次都会收到 modelNotifications ...所以在第二页上,我会收到第二页的通知,但在第二页上仍然有 modelNotifications,即使我只有一个条目。
这种呈现 HATEOAS 支持无法使用 spring 数据剩余。
好吧,玩了一整天之后,@Relation 标记似乎在这种情况下根本不起作用。所以我决定找一个解决办法。我所做的是实现一个 RelProvider 来正确检查继承和 return 正确的集合名称:
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class NotificationRelProvider implements RelProvider {
@Override
public String getItemResourceRelFor(Class<?> aClass) {
return "notification";
}
@Override
public String getCollectionResourceRelFor(Class<?> aClass) {
return "notifications";
}
@Override
public boolean supports(Class<?> aClass) {
return Notification.class.isAssignableFrom(aClass);
}
}
然后我标记了 spring 数据存储库,如文档中所述:
@ExposesResourceFor(Notification.class)
@RepositoryRestResource()
public interface NotificationRepository extends MongoRepository<Notification, UUID> {
List<Notification> findAllBySeverityOrderByCreatedDateDesc(@Param("severity") Notification.Severity severity);
}
完成此操作后,我现在得到了正确且预期的行为:
"_embedded" : {
"notifications" : [ {
"notificationType" : "BASIC_NOTIFICATION",
"createdDate" : "2019-10-02T23:06:16.802Z",
"lastModifiedDate" : "2019-10-02T23:06:16.802Z",
"createdBy" : "anonymousUser",
"lastModifiedBy" : "anonymousUser",
"severity" : "INFO",
"message" : "Interval Process Completed Successfully",
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/notifications/eb214276-2880-43e0-8c7f-1519b1e7e343"
},
"notification" : {
"href" : "http://fastscore:8088/api/notifications/eb214276-2880-43e0-8c7f-1519b1e7e343"
}
}
}, {
"notificationType" : "MODEL_NOTIFICATION",
"createdDate" : "2019-10-02T23:07:32.649Z",
"lastModifiedDate" : "2019-10-02T23:07:32.649Z",
"createdBy" : "anonymousUser",
"lastModifiedBy" : "anonymousUser",
....
所以这解决了原始错误,但这是一种手动过程,而不是仅使用文档中描述的注释。不幸的是,这意味着我必须为存储库中的每个基础 class 创建这些 RelProvider classes 之一,但至少它可以工作并且可用于分页。
我正在使用 spring 数据休息 Mongo 来公开具有多个子类型的 class。当我这样做时,HATEOAS 会根据实际实例化类型而不是公共基类型来划分结果。这会导致链接不正确,并导致分页无用,因为它是一个混合类型列表。
我已经尝试将 @Relation 标记显式添加到所有涉及的 classes 中,但它似乎根本没有任何影响。无论有没有它,我得到的结果都是一样的。
我正在使用 spring 启动依赖项 2.1.8.RELEASE 以及 spring-boot-starter-data-rest 和 spring-cloud-dependencies Greenwich.SR1
基础class:
@Relation(collectionRelation = "notifications", value="notifications")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "notificationType")
@JsonSubTypes({
@JsonSubTypes.Type(value = ModelNotification.class, name = Notification.MODEL_NOTIFICATION),
@JsonSubTypes.Type(value = BasicNotification.class, name = Notification.BASIC_NOTIFICATION)
})
public class Notification extends UUIDEntity implements Serializable {
private static final long serialVersionUID = 8199210081144334378L;
public static final String MODEL_NOTIFICATION = "MODEL_NOTIFICATION";
public static final String BASIC_NOTIFICATION = "BASIC_NOTIFICATION";
public enum Severity {
TRACE,
DEBUG,
INFO,
WARN,
ERROR,
FATAL
}
public Notification() {
this.notificationType = BASIC_NOTIFICATION;
}
@JsonProperty("notificationType")
private String notificationType;
@JsonProperty("createdDate")
@CreatedDate
private Instant createdDate;
@JsonProperty("lastModifiedDate")
@LastModifiedDate
private Instant lastModifiedDate;
@JsonProperty("createdBy")
@CreatedBy
private String createdBy;
@JsonProperty("lastModifiedBy")
@LastModifiedBy
private String lastModifiedBy;
@JsonProperty("severity")
private Severity severity;
@JsonProperty("message")
private String message;
无附加会员版本:
@Relation(collectionRelation = "notifications", value="notifications")
public class BasicNotification extends Notification implements Serializable {
private static final long serialVersionUID = 8063077545983014320L;
}
以及扩展版本:
@Relation(collectionRelation = "notifications", value="notifications")
public class ModelNotification extends Notification implements Serializable {
private static final long serialVersionUID = 3700576594274374440L;
@JsonProperty("storedModel")
private StoredModel storedModel;
public ModelNotification() {
super();
this.setNotificationType(Notification.MODEL_NOTIFICATION);
}
我希望,如果添加 @Relation 标记,所有结果都会出现在通知下,这是 spring 数据休息端点的正确 url。请注意,所有端点都正常工作,但只有 HATEOAS 部分不正确,并且捆绑会产生问题。访问时:/api/notifications
我回来了:
{
"_embedded" : {
"modelNotifications" : [ {
"notificationType" : "MODEL_NOTIFICATION",
"createdDate" : "2019-10-02T15:53:42.127Z",
"lastModifiedDate" : "2019-10-02T15:53:42.127Z",
...
[SNIP FOR BREVITY]
...
} ]
},
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/modelNotification/ef81c342-29d3-48fb-bab3-d416e80bc5f6"
},
"modelNotification" : {
"href" : "http://fastscore:8088/api/modelNotification/ef81c342-29d3-48fb-bab3-d416e80bc5f6"
}
}
} ],
"notifications" : [ {
"notificationType" : "BASIC_NOTIFICATION",
"createdDate" : "2019-10-02T15:52:10.261Z",
"lastModifiedDate" : "2019-10-02T15:52:10.261Z",
"createdBy" : "anonymousUser",
"lastModifiedBy" : "anonymousUser",
"severity" : "INFO",
"message" : "Interval Process Completed Successfully",
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/notifications/93fa5d6b-1457-4fa6-976c-cfdddc422976"
},
"notification" : {
"href" : "http://fastscore:8088/api/notifications/93fa5d6b-1457-4fa6-976c-cfdddc422976"
}
}
...
[SNIP]
...
},
},
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/notifications{?page,size,sort}",
"templated" : true
},
"profile" : {
"href" : "http://fastscore:8088/api/profile/notifications"
},
"search" : {
"href" : "http://fastscore:8088/api/notifications/search"
}
},
"page" : {
"size" : 20,
"totalElements" : 4,
"totalPages" : 1,
"number" : 0
}
}
这显然不正确,因为 modelNotifications 不是 spring 数据休息端点。此外,这会使分页变得无用,就好像我有大量通知一样,分页仅适用于那些,即使我只有一个,我每次都会收到 modelNotifications ...所以在第二页上,我会收到第二页的通知,但在第二页上仍然有 modelNotifications,即使我只有一个条目。
这种呈现 HATEOAS 支持无法使用 spring 数据剩余。
好吧,玩了一整天之后,@Relation 标记似乎在这种情况下根本不起作用。所以我决定找一个解决办法。我所做的是实现一个 RelProvider 来正确检查继承和 return 正确的集合名称:
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
public class NotificationRelProvider implements RelProvider {
@Override
public String getItemResourceRelFor(Class<?> aClass) {
return "notification";
}
@Override
public String getCollectionResourceRelFor(Class<?> aClass) {
return "notifications";
}
@Override
public boolean supports(Class<?> aClass) {
return Notification.class.isAssignableFrom(aClass);
}
}
然后我标记了 spring 数据存储库,如文档中所述:
@ExposesResourceFor(Notification.class)
@RepositoryRestResource()
public interface NotificationRepository extends MongoRepository<Notification, UUID> {
List<Notification> findAllBySeverityOrderByCreatedDateDesc(@Param("severity") Notification.Severity severity);
}
完成此操作后,我现在得到了正确且预期的行为:
"_embedded" : {
"notifications" : [ {
"notificationType" : "BASIC_NOTIFICATION",
"createdDate" : "2019-10-02T23:06:16.802Z",
"lastModifiedDate" : "2019-10-02T23:06:16.802Z",
"createdBy" : "anonymousUser",
"lastModifiedBy" : "anonymousUser",
"severity" : "INFO",
"message" : "Interval Process Completed Successfully",
"_links" : {
"self" : {
"href" : "http://fastscore:8088/api/notifications/eb214276-2880-43e0-8c7f-1519b1e7e343"
},
"notification" : {
"href" : "http://fastscore:8088/api/notifications/eb214276-2880-43e0-8c7f-1519b1e7e343"
}
}
}, {
"notificationType" : "MODEL_NOTIFICATION",
"createdDate" : "2019-10-02T23:07:32.649Z",
"lastModifiedDate" : "2019-10-02T23:07:32.649Z",
"createdBy" : "anonymousUser",
"lastModifiedBy" : "anonymousUser",
....
所以这解决了原始错误,但这是一种手动过程,而不是仅使用文档中描述的注释。不幸的是,这意味着我必须为存储库中的每个基础 class 创建这些 RelProvider classes 之一,但至少它可以工作并且可用于分页。