java - 使用 Spring Data Repositories 对审计信息进行自定义 JSON 序列化

标签 java json jackson spring-data-jpa spring-data-rest

我正在使用 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;
}

这样,每个域模型都具有属性 createdBy , createDate , lastModifiedBylastModifiedDate暴露如本示例实体所示:

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
        }
    }
}

我想要什么

现在我想实现 createdBy 的更短表示形式和lastModfifiedBy这样这些条目就不包含 User目的。相反,只应显示名称(来自 User.getName() ):

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

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

我尝试过:

  • 使用 @JsonIdentityInfo在用户实体上——这根本没有任何影响
  • 通过 @Bean Jackson2ObjectMapperBuilderCustomizer customizer() {...} 为用户实体注册自定义(反)序列化器-- 渲染 { "createdBy": { "content": "testEM"}}
  • 注释重写的方法 public User getCreatedBy()在我的BaseEntity@JsonSerialize(using= UserJsonSerializer.class) 一起上课-- 这个抛出异常

    {
        "timestamp": 1497515751192,
        "status": 500,
        "error": "Internal Server Error",
        "exception": "org.springframework.http.converter.HttpMessageNotWritableException",
        "message": "Could not write content: Can not override serializer; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not override serializer",
        "path": "/testres/1"
    }
    
  • 我还阅读了有关使用 @JsonView 的内容但是,我并不清楚如何为给定的用例启用这些

更新

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

更新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.

所以你必须公开 UserRepository当审核员的类型为 User 时。 巧合的是,这正是我在创建 MWE 时经历的行为(最小工作示例,无法上传到 github,因为我位于代理后面:( )。 所以,用@RepositoryRestResource UserRepository extends JpaRepository<User, Long>公开暴露,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"
        }
    }
}

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

非常感谢您的帮助!

最佳答案

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

快速解决方案: 当您暴露RestRepository<User>时您的 API 和审核员的类型相同 User ,Spring 将生成指向 createdBy 的 HAL 链接和lastModifiedBy 。两个审核日期仍将内联,因为它们是简单字符串(由于 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"
        }
    }
}

关于java - 使用 Spring Data Repositories 对审计信息进行自定义 JSON 序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44562940/

相关文章:

java - 为什么我的 SQLiteOpenHelper 构造函数中出现 "incompatible types: Bla cannot be converted to Context"?

ajax - jQuery.Ajax "Access to restricted URI denied"解释?

c++ - 使用 rapidjson 检索 JSON 字符串中的嵌套对象

java - Spring Boot - 自定义 JSON 序列化

java - 获取 MessageBodyProviderNotFoundException : MessageBodyReader not found for media type=text/plain When output is JSON string

java - 如何使用java在html中附加标签

java - Objectify List<Ref<T>> 未由 Google App Engine 端点序列化

java - 如何在android中使用java检查变量是否包含双值或空值

JavaScript JSON 组合和数据 Anylish

java - jackson 未能反序列化简单的 JSON