问题
Domain/Repository
Project {
User owner;
}
//Querydsl repositories
@RepositoryRestResource
public interface ProjectRepository extends PagingAndSortingRepository<Project, Long>, QueryDslPredicateExecutor<Project>, QuerydslBinderCustomizer<QProject> {
default void customize(QuerydslBindings bindings, QProject project) {
(...)
}
}
Requeriment: filter data according to the authenticated user context:
- If user is
ROLE_PUBLIC
show projects accordingpredicate
and where user is theowner
. - If user is
ROLE_ADMIN
show projects accordingpredicate
filter.
I tried solved throught several alternatives:
Option 1: Override @RepositoryRestController
as says Spring DATA REST doc:
@RepositoryRestController
public class ProjectController {
@RequestMapping(value = "/projects", method = RequestMethod.GET)
@ResponseBody
public PagedResources<?> search(
@QuerydslPredicate(root=Project.class ,bindings =ProjectRepository.class) Predicate predicate,
@PageableDefault Pageable pageable, //
@AuthenticationPrincipal Principal principal) {
(...) // Update predicate wit user context
projectRepository.findAll(predicate,pageagle);
(...)
}
}
This throw exception since the @QueryDslPredicat is not on the RepositoryRestHandlerAdapter list (DATAREST-838):
Failed to instantiate [com.querydsl.core.types.Predicate]: Specified class is an interface
The most similar argumentResolver into the list is QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver
but I dont know if it could be useful for this issue.
Option 2: This issue is a tipical cross-cutting concern so I tried apply AOP. Define Advice and update the args (Predicate):
@Around("this(com.xxx.yyy.repository.ProjectRepository)")
public void filterProjectsByUser(final ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
// .. Find args Predicate, update it adding user profile expression and procceed.
pjp.proceed(args);
}
The result is not the same as default Repository methods, instead of original JSON Object (with _embedded,page,_links), the response was:
{
"_links" : {
"profile" : {
"href" : "http://localhost:8087/api/profile/projects"
}
}
}
Option 3
Using @RestController
:
@RestController
public class ProjectController {
@RequestMapping(value = "/projects/search", method = RequestMethod.GET)
@ResponseBody
public PagedResources<?> search(
@QuerydslPredicate(root=Project.class ,bindings =ProjectRepository.class) Predicate predicate,
@PageableDefault Pageable pageable, //
@AuthenticationPrincipal Principal principal) {
(...) // Update predicate wit user context
projectRepository.findAll(predicate,pageagle);
(...)
}
}
Using the same path @RequestMapping("/projects",...)
also override other endpoints (PUT,POST,...) and this is not desired. This forced to define another endpoint ("projects/search")
.
I think that this workaround is not elegant and requires news endpoints. I am sure that exist a better alternative with Spring Data REST.
Questions:
- Any sugestions about how to solve this requeriment?
- How apply AOP to Spring Data REST to solve crosscutting requirements?
- Why @QuerydslPredicate at argument resolver?
Spring Data REST version 2.5.6
Although it has been resolved I would like to receive feedback and suggestions . I hope that QueryDSLPredicate annotation will be fixedn and the documentation will be improved with more samples about this issue.
回答1:
Finally I get run the Option 2 my error was not return proceed
invocation result:
@Aspect
@Transactional
@Component
public class FilterProjectsAspect {
@Pointcut("execution(* com.xxx.ProjectRepository.findAll(..))")
public void projectFindAll() {
}
@Around("projectFindAll()")
public Object filterProjectsByUser(final ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Predicate) {
Predicate predicate=(Predicate) args[i];
BooleanExpression isProjectOwner =buildExpressionForUser()
predicate = ExpressionUtils.allOf(isProjectOwner, predicate);
args[i]=predicate; //Update args
}
return pjp.proceed(args);
}
}
来源:https://stackoverflow.com/questions/45531195/spring-data-rest-override-repositories-controllers-vs-aop