Spring Data REST:单一资源的投影表示 [英] Spring Data REST: projection representation of single resource

查看:32
本文介绍了Spring Data REST:单一资源的投影表示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的 UserRepository,它使用 Spring Data REST 公开.下面是 User 实体类:

I have a simple UserRepository which exposed using Spring Data REST. Here is the User entity class:

@Document(collection = User.COLLECTION_NAME)
@Setter
@Getter
public class User extends Entity {

    public static final String COLLECTION_NAME = "users";

    private String name;
    private String email;
    private String password;
    private Set<UserRole> roles = new HashSet<>(0);
}

我创建了一个 UserProjection 类,其外观如下:

I've created a UserProjection class which looks the following way:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Projection(types = User.class)
public interface UserProjection {

    String getId();

    String getName();

    String getEmail();

    Set<UserRole> getRoles();
}

这是存储库类:

@RepositoryRestResource(collectionResourceRel = User.COLLECTION_NAME, path = RestPath.Users.ROOT,
        excerptProjection = UserProjection.class)
public interface RestUserRepository extends MongoRepository<User, String> {

    // Not exported operations

    @RestResource(exported = false)
    @Override
    <S extends User> S insert(S entity);

    @RestResource(exported = false)
    @Override
    <S extends User> S save(S entity);

    @RestResource(exported = false)
    @Override
    <S extends User> List<S> save(Iterable<S> entites);
}

我还在配置中指定了用户投影以确保它会被使用.

I've also specified user projection in configuration to make sure it will be used.

config.getProjectionConfiguration().addProjection(UserProjection.class, User.class);

所以,当我在 /users 路径上执行 GET 时,我得到以下响应(应用了投影):

So, when I do GET on /users path, I get the following response (projection is applied):

{
  "_embedded" : {
    "users" : [ {
      "name" : "Yuriy Yunikov",
      "id" : "5812193156aee116256a33d4",
      "roles" : [ "USER", "ADMIN" ],
      "email" : "yyunikov@gmail.com",
      "points" : 0,
      "_links" : {
        "self" : {
          "href" : "http://127.0.0.1:8080/users/5812193156aee116256a33d4"
        },
        "user" : {
          "href" : "http://127.0.0.1:8080/users/5812193156aee116256a33d4{?projection}",
          "templated" : true
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://127.0.0.1:8080/users"
    },
    "profile" : {
      "href" : "http://127.0.0.1:8080/profile/users"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

但是,当我尝试对单个资源进行 GET 调用时,例如/users/5812193156aee116256a33d4,我收到以下回复:

However, when I try to make a GET call for single resource, e.g. /users/5812193156aee116256a33d4, I get the following response:

{
  "name" : "Yuriy Yunikov",
  "email" : "yyunikov@gmail.com",
  "password" : "123456",
  "roles" : [ "USER", "ADMIN" ],
  "_links" : {
    "self" : {
      "href" : "http://127.0.0.1:8080/users/5812193156aee116256a33d4"
    },
    "user" : {
      "href" : "http://127.0.0.1:8080/users/5812193156aee116256a33d4{?projection}",
      "templated" : true
    }
  }
}

如您所见,密码字段正在返回并且未应用投影.我知道有 @JsonIgnore 注释可用于隐藏资源的敏感数据.但是,我的 User 对象位于不了解 API 或 JSON 表示的不同应用程序模块中,因此在那里用 @JsonIgnore 注释标记字段没有意义.

As you may see, the password field is getting returned and projection is not applied. I know there is @JsonIgnore annotation which can be used to hide sensitive data of resource. However, my User object is located in different application module which does not know about API or JSON representation, so it does not make sense to mark fields with @JsonIgnore annotation there.

我在此处看到了@Oliver Gierke 的一篇关于为什么不自动将摘录投影应用于单个资源的帖子.但是,在我的情况下仍然非常不方便,我想在获得单个资源时返回相同的 UserProjection .是否有可能在不创建自定义控制器或使用 @JsonIgnore 标记字段的情况下做到这一点?

I've seen a post by @Oliver Gierke here about why excerpt projections are not applied to single resource automatically. However, it's still very inconvenient in my case and I would like to return the same UserProjection when I get a single resource. Is it somehow possible to do it without creating a custom controller or marking fields with @JsonIgnore?

推荐答案

我能够创建一个 ResourceProcessor 类,该类按照 DATAREST-428.它的工作方式如下:如果在 URL 中指定了投影参数 - 将应用指定的投影,如果没有 - 将返回名称为 default 的投影,将应用应用的第一个找到的投影.另外,我必须添加忽略链接的自定义 ProjectingResource,否则返回的 JSON 中有两个 _links 键.

I was able to create a ResourceProcessor class which applies projections on any resource as suggested in DATAREST-428. It works the following way: if projection parameter is specified in URL - the specified projection will be applied, if not - projection with name default will be returned, applied first found projection will be applied. Also, I had to add custom ProjectingResource which ignores the links, otherwise there are two _links keys in the returning JSON.

/**
 * Projecting resource used for {@link ProjectingProcessor}. Does not include empty links in JSON, otherwise two 
 * _links keys are present in returning JSON.
 *
 * @param <T>
 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
class ProjectingResource<T> extends Resource<T> {

    ProjectingResource(final T content) {
        super(content);
    }
}

/**
 * Resource processor for all resources which applies projection for single resource. By default, projections
 * are not
 * applied when working with single resource, e.g. http://127.0.0.1:8080/users/580793f642d54436e921f6ca. See
 * related issue <a href="https://jira.spring.io/browse/DATAREST-428">DATAREST-428</a>
 */
@Component
public class ProjectingProcessor implements ResourceProcessor<Resource<Object>> {

    private static final String PROJECTION_PARAMETER = "projection";

    private final ProjectionFactory projectionFactory;

    private final RepositoryRestConfiguration repositoryRestConfiguration;

    private final HttpServletRequest request;

    public ProjectingProcessor(@Autowired final RepositoryRestConfiguration repositoryRestConfiguration,
                               @Autowired final ProjectionFactory projectionFactory,
                               @Autowired final HttpServletRequest request) {
        this.repositoryRestConfiguration = repositoryRestConfiguration;
        this.projectionFactory = projectionFactory;
        this.request = request;
    }

    @Override
    public Resource<Object> process(final Resource<Object> resource) {
        if (AopUtils.isAopProxy(resource.getContent())) {
            return resource;
        }

        final Optional<Class<?>> projectionType = findProjectionType(resource.getContent());
        if (projectionType.isPresent()) {
            final Object projection = projectionFactory.createProjection(projectionType.get(), resource
                    .getContent());
            return new ProjectingResource<>(projection);
        }

        return resource;
    }

    private Optional<Class<?>> findProjectionType(final Object content) {
        final String projectionParameter = request.getParameter(PROJECTION_PARAMETER);
        final Map<String, Class<?>> projectionsForType = repositoryRestConfiguration.getProjectionConfiguration()
                .getProjectionsFor(content.getClass());

        if (!projectionsForType.isEmpty()) {
            if (!StringUtils.isEmpty(projectionParameter)) {
                // projection parameter specified
                final Class<?> projectionClass = projectionsForType.get(projectionParameter);
                if (projectionClass != null) {
                    return Optional.of(projectionClass);
                }
            } else if (projectionsForType.containsKey(ProjectionName.DEFAULT)) {
                // default projection exists
                return Optional.of(projectionsForType.get(ProjectionName.DEFAULT));
            }

            // no projection parameter specified
            return Optional.of(projectionsForType.values().iterator().next());
        }

        return Optional.empty();
    }
}

这篇关于Spring Data REST:单一资源的投影表示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆