使用 Spring 数据存储库自定义 JSON 审计信息序列化

Custom JSON serialization for Audit Infos with Spring Data Repositories

我正在使用 spring boot 1.5.2 以及 Spring Data JPA 和 Data Rest 实现公司内部 REST 服务。

问题

我正在寻找一种在使用 Spring Data Rest-Repositories 公开某些域模型时将对象序列化为字符串的有效方法。

上下文

我的领域模型都是从 BaseEntity 扩展而来的,看起来像这样:

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity extends AbstractAuditable<User, Long> implements Serializable {
  @Version
  private Long version;
}

有了这个,每个域模型都有属性 createdBycreateDatelastModifiedBylastModifiedDate,如本示例实体所示:

public class TestEntity extends BaseEntity { private String name; } 相应的 JSON 输出如下所示:

{
    "createdBy":
    {
        "name": "testEM",
        "contactInfo":
        {
            "title": null,
            "givenName": "GivenName",
            "surName": "Surname",
            "mail": "test@test.mail.de"
        },
        "function": "EMPLOYEE",
        "department":
        {
            "name": "mydep"
        }
    },
    "createdDate": "2017-06-12T11:49:17.013Z",
    "lastModifiedBy":
    {
        <same representation as "createdBy">
    },
    "lastModifiedDate": "2017-06-14T11:27:32.370Z",
    "name": "Hello,Name!",
    "new": false,
    "_links":
    {
        "self":
        {
            "href": "http://localhost:8080/testres/1"
        },
        "testEntity":
        {
            "href": "http://localhost:8080/testres/1{?projection}",
            "templated": true
        }
    }
}

我想要的

现在我想实现 createdBylastModfifiedBy 的更短表示,以便这些条目不包含 User 对象。而应该只显示名称(来自 User.getName()):

{
    "createdBy": "testEM",
    "createdDate": "2017-06-12T11:49:17.013Z",
    "lastModifiedBy": "testEM",
    "lastModifiedDate": "2017-06-12T11:49:17.013Z",
    ... // other properties
}

实现此目标的最佳方法是什么?

我试过:

更新

我创建了一些投影,这是减少输出的开箱即用支持方式。有关我编写的代码,请参阅此 Gist。 有了这些,并将投影设置为摘录,条目列表就可以正常显示了。但是,当您请求 localhost:8080/testRepo/1 等特定资源时,您会得到未投影的输出。我知道 Spring 默认情况下不会将投影应用于特定实体。所以我们必须将请求参数 ?=projection=testProjection 应用于每个请求。 因为这是可行的(因为应用程序不会 public),所以可能没问题,但对其他人来说可能不行。所以问题仍然存在,我们如何才能以有效的方式为每个资源更改审计信息?

更新 2

我又读了一遍 Spring Data REST Documentation 并偶然发现了这一段:

There is another route. If the Address domain object does not have it’s own repository definition, Spring Data REST will inline the data fields right inside the Person resource.

因此当审核员的类型为 User 时,您必须公开一个 UserRepository。 巧合的是,这正是我在创建 MWE 时遇到的确切行为(最小的工作示例,无法上传到 github,因为我在代理 :( ) 后面。 因此,@RepositoryRestResource UserRepository extends JpaRepository<User, Long> publicly 暴露,Spring 生成此 JSON:

{
    "createdDate": "2017-06-12T11:49:17.013Z",
    "lastModifiedDate": "2017-06-14T11:27:32.370Z",
    "name": "Hello,EM!",
    "new": false,
    "_links":
    {
        "self":
        {
            "href": "http://localhost:8080/testRepo/1"
        },
        "testEntity":
        {
            "href": "http://localhost:8080/testRepo/1{?projection}",
            "templated": true
        },
        "lastModifiedBy":
        {
            "href": "http://localhost:8080/testRepo/1/lastModifiedBy"
        },
        "createdBy":
        {
            "href": "http://localhost:8080/testRepo/1/createdBy"
        }
    }
}

这种行为对我来说是可以接受的,所以认为这个问题已经解决了。 如果有人有其他意见,请随时 post!

非常感谢任何帮助!

这不是我提出的问题的解决方案,但它是我和公司可以接受的妥协。

快速解决方案: 当您在 API 中公开 RestRepository<User> 并且您的审计员属于同一类型 User 时,Spring 将生成指向 createdBy 和 [=18 的 HAL 链接=].两个审计日期仍将内联,因为它们是简单的字符串(由于 JodaTime 转换)。

示例代码:

// resolves auditor from SecurityContext
public class AuditorAwareImpl implements AuditorAware<User> {

  @Override
  public User getCurrentAuditor() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication != null && authentication.getPrincipal() instanceof WrappedUser) {
      WrappedUser principal = (WrappedUser)authentication.getPrincipal();
      return principal.getUser();
    }
    throw new IllegalStateException("No current auditor available!");
  }
}

公开 UserRepository:

//exported is true by default
@RepositoryRestResource(exported = true)
public interface UserRepository extends JpaRepository<User, Long> {
  Optional<User> findByName(String loginName);
}

创建所有其他域对象继承自的 AuditEntity:

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity extends AbstractAuditable<User, Long> implements Serializable {
  @javax.persistence.Version
  private Long version;
}

公开您的领域模型:

@Entity
public class Project extends BaseEntity {
  private String project_name;
  // other properties
}

@RepositoryRestResource
public interface ProjectRepo extends JpaRepository<User, Long> {}

这将为 /projects/{id} 生成以下 JSON:

{
    "createdDate": "2017-06-12T11:49:17.013Z",
    "lastModifiedDate": "2017-06-14T11:27:32.370Z",
    "project_name": "MyExampleProjectName",
    "new": false,
    "_links":
    {
        "self":
        {
            "href": "http://localhost:8080/projects/1"
        },
        "project":
        {
            "href": "http://localhost:8080/projects/1{?projection}",
            "templated": true
        },
        "lastModifiedBy":
        {
            "href": "http://localhost:8080/projects/1/lastModifiedBy"
        },
        "createdBy":
        {
            "href": "http://localhost:8080/projects/1/createdBy"
        }
    }
}