对于最新的稳定版本,请使用 Spring Data REST 4.3.1Spring中文文档

对于最新的稳定版本,请使用 Spring Data REST 4.3.1Spring中文文档

Spring Data REST 显示导出的领域模型的默认视图。但是,有时,由于各种原因,您可能需要更改该模型的视图。本节介绍如何定义投影和摘录,以提供简化和简化的资源视图。Spring中文文档

预测

请考虑以下域模型:Spring中文文档

@Entity
public class Person {

  @Id @GeneratedValue
  private Long id;
  private String firstName, lastName;

  @OneToOne
  private Address address;
  …
}

前面示例中的对象具有多个属性:PersonSpring中文文档

现在假设我们创建了一个相应的存储库,如下所示:Spring中文文档

interface PersonRepository extends CrudRepository<Person, Long> {}

默认情况下,Spring Data REST 导出此域对象,包括其所有属性。 并导出为纯数据对象。关于该属性有两个选项。一种选择是还为对象定义存储库,如下所示:firstNamelastNameaddressAddressSpring中文文档

interface AddressRepository extends CrudRepository<Address, Long> {}

在这种情况下,资源将属性呈现为其相应资源的 URI。如果我们在系统中查找“Frodo”,我们可以看到这样的 HAL 文档:PersonaddressAddressSpring中文文档

{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons/1"
    },
    "address" : {
      "href" : "http://localhost:8080/persons/1/address"
    }
  }
}

还有另一种方法。如果域对象没有自己的存储库定义,则Spring Data REST将包含资源中的数据字段,如以下示例所示:AddressPersonSpring中文文档

{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "address" : {
    "street": "Bag End",
    "state": "The Shire",
    "country": "Middle Earth"
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons/1"
    }
  }
}

但是,如果您根本不想要细节怎么办?同样,默认情况下,Spring Data REST 导出其所有属性(除了 )。您可以通过定义一个或多个投影来为 REST 服务的使用者提供替代方法。以下示例显示了不包括地址的投影:addressidSpring中文文档

@Projection(name = "noAddresses", types = { Person.class }) (1)
interface NoAddresses { (2)

  String getFirstName(); (3)

  String getLastName(); (4)
}
1 注释将其标记为投影。该属性提供 投影的名称,我们稍后会更详细地介绍。这些属性以此投影为目标,仅应用于对象。@ProjectionnametypesPerson
2 它是一个 Java 接口,使其成为声明式的。
3 它导出 .firstName
4 它导出 .lastName

投影只有 和 的 getter ,这意味着它不提供任何地址信息。假设您有一个单独的资源存储库,Spring Data REST的默认视图与前面的表示形式略有不同,如以下示例所示:NoAddressesfirstNamelastNameAddressSpring中文文档

{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons/1{?projection}", (1)
      "templated" : true (2)
    },
    "address" : {
      "href" : "http://localhost:8080/persons/1/address"
    }
  }
}
1 此资源有一个新选项:。{?projection}
2 URI 是 URI 模板。self

若要查看资源的投影,请查找 。localhost:8080/persons/1?projection=noAddressesSpring中文文档

提供给查询参数的值与 中指定的值相同。它与投影接口的名称无关。projection@Projection(name = "noAddress")

您可以有多个投影。Spring中文文档

请参阅投影以查看示例项目。我们鼓励您尝试一下。

Spring Data REST查找投影定义,如下所示:Spring中文文档

  • 在与实体定义(或其子包之一)相同的包中找到的任何接口都将注册。@ProjectionSpring中文文档

  • 您可以使用 手动注册投影。RepositoryRestConfiguration.getProjectionConfiguration().addProjection(…)Spring中文文档

无论哪种情况,投影接口都必须具有注释。@ProjectionSpring中文文档

查找现有预测

Spring Data REST公开了应用程序级配置文件语义(ALPS)文档,这是一种微元数据格式。要查看 ALPS 元数据,请点击根资源公开的链接。如果向下导航到资源的 ALPS 文档(即 ),您可以找到有关资源的许多详细信息。在类似于以下示例的块中列出了投影以及有关 REST 转换的详细信息:profilePerson/alps/personsPersonGETSpring中文文档

{ …
  "id" : "get-person", (1)
  "name" : "person",
  "type" : "SAFE",
  "rt" : "#person-representation",
  "descriptors" : [ {
    "name" : "projection", (2)
    "doc" : {
      "value" : "The projection that shall be applied when rendering the response. Acceptable values available in nested descriptors.",
      "format" : "TEXT"
    },
    "type" : "SEMANTIC",
    "descriptors" : [ {
      "name" : "noAddresses", (3)
      "type" : "SEMANTIC",
      "descriptors" : [ {
        "name" : "firstName", (4)
        "type" : "SEMANTIC"
      }, {
        "name" : "lastName", (4)
        "type" : "SEMANTIC"
      } ]
    } ]
  } ]
},
…
1 ALPS 文档的这一部分显示了有关和资源的详细信息。GETPerson
2 这部分是选项。projection
3 此部分包含投影。noAddresses
4 此投影提供的实际属性包括 和 。firstNamelastName

如果符合以下条件,则选取投影定义并将其提供给客户端:Spring中文文档

  • 使用注释标记并位于域类型的同一包(或子包)中,或@ProjectionSpring中文文档

  • 使用 手动注册。RepositoryRestConfiguration.getProjectionConfiguration().addProjection(…)Spring中文文档

引入隐藏数据

到目前为止,在本节中,我们已经介绍了如何使用投影来减少呈现给用户的信息。投影还可以引入通常看不见的数据。例如,Spring Data REST会忽略用注释标记的字段或getter。请考虑以下域对象:@JsonIgnoreSpring中文文档

@Entity
public class User {

	@Id @GeneratedValue
	private Long id;
	private String name;

	@JsonIgnore private String password; (1)

	private String[] roles;
  …
1 Jackson's 用于防止字段序列化为 JSON。@JsonIgnorepassword

前面示例中的类可用于存储用户信息以及与 Spring Security 的集成。如果创建一个 ,该字段通常会被导出,这不太好。在前面的示例中,我们通过在场上应用杰克逊来防止这种情况发生。UserUserRepositorypassword@JsonIgnorepasswordSpring中文文档

如果字段的相应 getter 函数上,Jackson 也不会将字段序列化为 JSON。@JsonIgnore

但是,预测引入了仍然为该领域服务的能力。可以创建以下投影:Spring中文文档

@Projection(name = "passwords", types = { User.class })
interface PasswordProjection {

  String getPassword();
}

如果创建并使用了这样的投影,则会回避放置在 上的指令。@JsonIgnoreUser.passwordSpring中文文档

这个例子可能看起来有点做作,但是,使用更丰富的域模型和许多投影,可能会意外地泄露这些细节。由于Spring Data REST无法辨别此类数据的敏感性,因此您可以避免这种情况。

投影还可以生成虚拟数据。假设您有以下实体定义:Spring中文文档

@Entity
public class Person {

  ...
  private String firstName;
  private String lastName;

  ...
}

您可以创建一个投影,将前面示例中的两个数据字段组合在一起,如下所示:Spring中文文档

@Projection(name = "virtual", types = { Person.class })
public interface VirtualProjection {

  @Value("#{target.firstName} #{target.lastName}") (1)
  String getFullName();

}
1 Spring 的注解允许您插入一个 SpEL 表达式,该表达式获取目标对象并将其 和 属性拼接在一起以呈现只读 .@ValuefirstNamelastNamefullName
1 注释将其标记为投影。该属性提供 投影的名称,我们稍后会更详细地介绍。这些属性以此投影为目标,仅应用于对象。@ProjectionnametypesPerson
2 它是一个 Java 接口,使其成为声明式的。
3 它导出 .firstName
4 它导出 .lastName
1 此资源有一个新选项:。{?projection}
2 URI 是 URI 模板。self
提供给查询参数的值与 中指定的值相同。它与投影接口的名称无关。projection@Projection(name = "noAddress")
请参阅投影以查看示例项目。我们鼓励您尝试一下。
1 ALPS 文档的这一部分显示了有关和资源的详细信息。GETPerson
2 这部分是选项。projection
3 此部分包含投影。noAddresses
4 此投影提供的实际属性包括 和 。firstNamelastName

如果符合以下条件,则选取投影定义并将其提供给客户端:Spring中文文档

  • 使用注释标记并位于域类型的同一包(或子包)中,或@ProjectionSpring中文文档

  • 使用 手动注册。RepositoryRestConfiguration.getProjectionConfiguration().addProjection(…)Spring中文文档

1 Jackson's 用于防止字段序列化为 JSON。@JsonIgnorepassword
如果字段的相应 getter 函数上,Jackson 也不会将字段序列化为 JSON。@JsonIgnore
这个例子可能看起来有点做作,但是,使用更丰富的域模型和许多投影,可能会意外地泄露这些细节。由于Spring Data REST无法辨别此类数据的敏感性,因此您可以避免这种情况。
1 Spring 的注解允许您插入一个 SpEL 表达式,该表达式获取目标对象并将其 和 属性拼接在一起以呈现只读 .@ValuefirstNamelastNamefullName

摘录

摘录是自动应用于资源集合的投影。例如,您可以按如下方式更改:PersonRepositorySpring中文文档

@RepositoryRestResource(excerptProjection = NoAddresses.class)
interface PersonRepository extends CrudRepository<Person, Long> {}

前面的示例指示Spring Data REST在将资源嵌入到集合或相关资源时使用投影。NoAddressesPersonSpring中文文档

摘录投影不会自动应用于单个资源。它们必须有意识地应用。摘录投影旨在提供集合数据的默认预览,但在提取单个资源时不然。请参阅为什么摘录投影不会自动应用于Spring Data REST项目资源?,以讨论该主题。

除了更改默认渲染之外,摘录还具有其他渲染选项,如下一节所示。Spring中文文档

摘录常用数据

在编写域对象时,会出现 REST 服务的常见情况。例如,a 存储在一个表中,它们的相关存储在另一个表中。默认情况下,Spring Data REST将人员的URI作为客户端必须导航的URI提供。但是,如果消费者总是获取这些额外的数据是很常见的,那么摘录投影可以将这额外的数据内联,从而为您节省额外的数据。为此,您可以定义另一个摘录投影,如下所示:PersonAddressaddressGETSpring中文文档

@Projection(name = "inlineAddress", types = { Person.class }) (1)
interface InlineAddress {

  String getFirstName();

  String getLastName();

  Address getAddress(); (2)
}
1 此投影已命名为 。inlineAddress
2 此投影添加 ,返回字段。在投影中使用时,它会导致信息内联。getAddressAddress

您可以将其代入定义中,如下所示:PersonRepositorySpring中文文档

@RepositoryRestResource(excerptProjection = InlineAddress.class)
interface PersonRepository extends CrudRepository<Person, Long> {}

这样做会导致 HAL 文档如下所示:Spring中文文档

{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "address" : { (1)
    "street": "Bag End",
    "state": "The Shire",
    "country": "Middle Earth"
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons/1"
    },
    "address" : { (2)
      "href" : "http://localhost:8080/persons/1/address"
    }
  }
}
1 数据直接包含在内联中,因此您无需导航即可获取数据。address
2 仍然提供指向资源的链接,因此仍然可以导航到其自己的资源。Address

请注意,前面的示例是本章前面所示示例的混合。您可能需要通读它们以遵循最终示例的进展。Spring中文文档

配置存储库会更改默认行为。如果您已经发布了版本,这可能会对服务的使用者造成重大更改。@RepositoryRestResource(excerptProjection=…​)
摘录投影不会自动应用于单个资源。它们必须有意识地应用。摘录投影旨在提供集合数据的默认预览,但在提取单个资源时不然。请参阅为什么摘录投影不会自动应用于Spring Data REST项目资源?,以讨论该主题。
1 此投影已命名为 。inlineAddress
2 此投影添加 ,返回字段。在投影中使用时,它会导致信息内联。getAddressAddress
1 数据直接包含在内联中,因此您无需导航即可获取数据。address
2 仍然提供指向资源的链接,因此仍然可以导航到其自己的资源。Address
配置存储库会更改默认行为。如果您已经发布了版本,这可能会对服务的使用者造成重大更改。@RepositoryRestResource(excerptProjection=…​)