| 此版本仍在开发中,尚不被认为是稳定的。对于最新的稳定版本,请使用 Spring Data REST 4.5.3! | 
投影和摘录
Spring Data REST 提供了导出的域模型的默认视图。但是,有时,由于各种原因,您可能需要更改该模型的视图。本节介绍如何定义投影和摘录,以提供简化和简化的资源视图。
预测
考虑以下域模型:
@Entity
public class Person {
  @Id @GeneratedValue
  private Long id;
  private String firstName, lastName;
  @OneToOne
  private Address address;
  …
}这Person前面示例中的对象有几个属性:
- 
id是主键。
- 
firstName和lastName是数据属性。
- 
address是指向另一个域对象的链接。
现在假设我们创建一个相应的存储库,如下所示:
interface PersonRepository extends CrudRepository<Person, Long> {}默认情况下,Spring Data REST 导出此域对象,包括其所有属性。firstName和lastName导出为普通数据对象。关于address属性。一种选择是还为Address对象,如下所示:
interface AddressRepository extends CrudRepository<Address, Long> {}在这种情况下,一个Person资源渲染address属性作为 URI 发送给其相应的Address资源。如果我们在系统中查找“Frodo”,我们可能会看到这样的 HAL 文档:
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons/1"
    },
    "address" : {
      "href" : "http://localhost:8080/persons/1/address"
    }
  }
}还有另一种方法。如果Addressdomain 对象没有自己的存储库定义,Spring Data REST 包含Person资源,如以下示例所示:
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "address" : {
    "street": "Bag End",
    "state": "The Shire",
    "country": "Middle Earth"
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons/1"
    }
  }
}但是,如果您不想要address细节?同样,默认情况下,Spring Data REST 导出其所有属性(除了id).您可以通过定义一个或多个投影来为 REST 服务的使用者提供替代方案。以下示例显示了不包含地址的投影:
@Projection(name = "noAddresses", types = { Person.class }) (1)
interface NoAddresses { (2)
  String getFirstName(); (3)
  String getLastName(); (4)
}| 1 | 这 @Projection注释将其标记为投影。这name属性提供
投影的名称,我们稍后会更详细地介绍。这types属性将此投影定位为仅应用于Person对象。 | 
| 2 | 它是一个 Java 接口,使其成为声明式的。 | 
| 3 | 它导出 firstName. | 
| 4 | 它导出 lastName. | 
这NoAddresses投影只有firstName和lastName,这意味着它不提供任何地址信息。假设你有一个单独的存储库Addressresources,Spring Data REST 的默认视图与前面的表示略有不同,如以下示例所示:
{
  "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 | 这 selfURI 是一个 URI 模板。 | 
若要查看对资源的预测,请查找localhost:8080/persons/1?projection=noAddresses.
| 提供给 projectionquery 参数与@Projection(name = "noAddress").它与投影接口的名称无关。 | 
您可以有多个投影。
| 请参阅投影以查看示例项目。我们鼓励您尝试一下。 | 
Spring Data REST 查找投影定义,如下所示:
- 
任何 @Projection接口,与实体定义(或其子包之一)注册在同一包中。
- 
您可以使用以下命令手动注册投影 RepositoryRestConfiguration.getProjectionConfiguration().addProjection(…).
无论哪种情况,投影接口都必须具有@Projection注解。
查找现有投影
Spring Data REST 公开了应用程序级配置文件语义 (ALPS) 文档,这是一种微元数据格式。要查看 ALPS 元数据,请按照profile根资源公开的链接。如果向下导航到 ALPS 文档,则Person资源(这将是/alps/persons),您可以找到有关Person资源。将列出投影,以及有关GETREST 转换,以类似于以下示例的块形式:
{ …
  "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 文档的这一部分显示了有关 GET和Person资源。 | 
| 2 | 这部分是 projection选项。 | 
| 3 | 此部分包含 noAddresses投影。 | 
| 4 | 此投影提供的实际属性包括 firstName和lastName. | 
| 如果出现以下情况,则会选择投影定义并将其提供给客户: 
 | 
引入隐藏数据
到目前为止,在本节中,我们已经介绍了如何使用投影来减少呈现给用户的信息。预测还可以带来通常看不见的数据。例如,Spring Data REST 忽略用@JsonIgnore附注。考虑以下域对象:
@Entity
public class User {
	@Id @GeneratedValue
	private Long id;
	private String name;
	@JsonIgnore private String password; (1)
	private String[] roles;
  …| 1 | Jackson的 @JsonIgnore用于防止password字段不再序列化为 JSON。 | 
这Userclass 可用于存储用户信息以及与 Spring Security 集成。如果您创建UserRepository这password字段通常会被导出,这并不好。在前面的示例中,我们通过应用 Jackson 的@JsonIgnore在password田。
| 如果出现以下情况,Jackson 也不会将字段序列化为 JSON @JsonIgnore位于字段的相应 getter 函数上。 | 
然而,预测引入了仍然为该领域服务的能力。可以创建以下投影:
@Projection(name = "passwords", types = { User.class })
interface PasswordProjection {
  String getPassword();
}如果创建并使用此类投影,它会避开@JsonIgnore指令放在User.password.
| 这个例子可能看起来有点做作,但通过更丰富的领域模型和许多预测,可能会意外泄露此类细节。由于 Spring Data REST 无法辨别此类数据的敏感性,因此您需要避免此类情况。 | 
投影还可以生成虚拟数据。假设您有以下实体定义:
@Entity
public class Person {
  ...
  private String firstName;
  private String lastName;
  ...
}您可以创建一个投影,将前面示例中的两个数据字段组合在一起,如下所示:
@Projection(name = "virtual", types = { Person.class })
public interface VirtualProjection {
  @Value("#{target.firstName} #{target.lastName}") (1)
  String getFullName();
}| 1 | Spring的 @Value注释允许您插入一个 SpEL 表达式,该表达式采用目标对象并将其拼接在一起firstName和lastName属性来呈现只读fullName. | 
摘录
摘录是自动应用于资源集合的投影。例如,您可以更改PersonRepository如下:
@RepositoryRestResource(excerptProjection = NoAddresses.class)
interface PersonRepository extends CrudRepository<Person, Long> {}前面的示例指示 Spring Data REST 使用NoAddresses嵌入时的投影Person资源到集合或相关资源中。
| 摘录投影不会自动应用于单个资源。它们必须被刻意应用。摘录投影旨在提供集合数据的默认预览,但在获取单个资源时则不然。有关该主题的讨论,请参阅为什么不会自动将摘录投影应用于 Spring Data REST 项资源? | 
除了更改默认渲染之外,摘录还具有其他渲染选项,如下一节所示。
摘录常用数据
当您组合域对象时,会出现 REST 服务的常见情况。例如,一个Person存储在一个表中,其相关Address存储在另一个中。默认情况下,Spring Data REST 提供该人的address作为客户端必须导航的 URI。但是,如果消费者总是获取这些额外的数据是很常见的,那么摘录投影可以将这些额外的数据段内联,从而为您节省额外的费用GET.为此,您可以定义另一个摘录投影,如下所示:
@Projection(name = "inlineAddress", types = { Person.class }) (1)
interface InlineAddress {
  String getFirstName();
  String getLastName();
  Address getAddress(); (2)
}| 1 | 该投影已命名为 inlineAddress. | 
| 2 | 此投影增加了 getAddress,它返回Address田。在投影中使用时,它会导致信息内联包含在内。 | 
您可以将其插入PersonRepository定义,如下所示:
@RepositoryRestResource(excerptProjection = InlineAddress.class)
interface PersonRepository extends CrudRepository<Person, Long> {}这样做会导致 HAL 文档显示如下:
{
  "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 | 链接到 Addressresource 仍然提供,因此仍然可以导航到自己的资源。 | 
请注意,前面的示例是本章前面显示的示例的混合。您可能需要通读它们以跟踪最终示例的进展。
| 配置 @RepositoryRestResource(excerptProjection=…)对于存储库,则更改了默认行为。如果您已经发布了版本,这可能会导致服务使用者发生重大更改。 |