[SPRING] Spring 데이터 REST : 단일 리소스의 투영 표현
SPRINGSpring 데이터 REST : 단일 리소스의 투영 표현
스프링 데이터 REST를 사용하여 노출 된 간단한 UserRepository가 있습니다. User 엔티티 클래스는 다음과 같습니다.
@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 클래스를 만들었습니다.
@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);
}
또한 구성에서 사용자 투영을 사용하여 이것이 사용되는지 확인했습니다.
config.getProjectionConfiguration().addProjection(UserProjection.class, User.class);
그래서 GET / users 경로에서 다음 응답을 얻습니다 (프로젝션이 적용됨).
{
"_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, 다음과 같은 응답이 표시됩니다.
{
"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 주석으로 필드를 표시하는 것이 적절하지 않습니다.
@Oliver Gierke가 한 발췌문이 단일 리소스에 자동으로 적용되지 않는 이유에 대한 게시물을 여기에서 보았습니다. 그러나, 그것은 여전히 내 경우에 매우 불편하고 단일 리소스를 얻을 때 동일한 UserProjection을 반환하고자합니다. 어떻게 든 사용자 정의 컨트롤러를 만들거나 @JsonIgnore로 필드를 표시하지 않고 할 수 있습니까?
해결법
-
==============================
1.나는 DATAREST-428에서 제안 된대로 모든 자원에 대한 투영을 적용하는 ResourceProcessor 클래스를 만들 수있었습니다. 다음과 같은 방식으로 작동합니다 : 투영 매개 변수가 URL에 지정된 경우 - 지정된 투영법이 적용됩니다. 그렇지 않은 경우 - 이름이 기본값 인 투영법이 반환되고 처음 적용될 투영법이 적용됩니다. 또한 링크를 무시하는 사용자 지정 ProjectingResource를 추가해야합니다. 그렇지 않으면 반환하는 JSON에 두 개의 _links 키가 있습니다.
나는 DATAREST-428에서 제안 된대로 모든 자원에 대한 투영을 적용하는 ResourceProcessor 클래스를 만들 수있었습니다. 다음과 같은 방식으로 작동합니다 : 투영 매개 변수가 URL에 지정된 경우 - 지정된 투영법이 적용됩니다. 그렇지 않은 경우 - 이름이 기본값 인 투영법이 반환되고 처음 적용될 투영법이 적용됩니다. 또한 링크를 무시하는 사용자 지정 ProjectingResource를 추가해야합니다. 그렇지 않으면 반환하는 JSON에 두 개의 _links 키가 있습니다.
/** * 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(); } }
-
==============================
2.나는 비슷한 것을 최근에보고 있었고 Spring Data / Jackson 측에서 접근하려고 할 때 서클에서 돌아서 버렸습니다.
나는 비슷한 것을 최근에보고 있었고 Spring Data / Jackson 측에서 접근하려고 할 때 서클에서 돌아서 버렸습니다.
대안이며 매우 간단한 해결책은 다른 각도에서 접근하여 HTTP 요청의 Projection 매개 변수가 항상 존재하도록하는 것입니다. 이것은 서블릿 필터를 사용하여 들어오는 요청의 매개 변수를 수정하여 수행 할 수 있습니다.
이것은 다음과 같습니다.
public class ProjectionResolverFilter extends GenericFilterBean { private static final String REQUEST_PARAM_PROJECTION_KEY = "projection"; @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; if (shouldApply(request)) { chain.doFilter(new ResourceRequestWrapper(request), res); } else { chain.doFilter(req, res); } } /** * * @param request * @return True if this filter should be applied for this request, otherwise * false. */ protected boolean shouldApply(HttpServletRequest request) { return request.getServletPath().matches("some-path"); } /** * HttpServletRequestWrapper implementation which allows us to wrap and * modify the incoming request. * */ public class ResourceRequestWrapper extends HttpServletRequestWrapper { public ResourceRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getParameter(final String name) { if (name.equals(REQUEST_PARAM_PROJECTION_KEY)) { return "nameOfDefaultProjection"; } return super.getParameter(name); } } }
from https://stackoverflow.com/questions/40289665/spring-data-rest-projection-representation-of-single-resource by cc-by-sa and MIT license