域/存储库
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: 根据经过身份验证的用户上下文过滤数据:
ROLE_PUBLIC
根据predicate
显示项目,并且用户是owner
。ROLE_ADMIN
根据predicate
过滤器显示项目。我尝试通过几种方法解决:
选项1 :如Spring DATA REST所述覆盖@RepositoryRestController
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);
(...)
}
}
由于@QueryDslPredicat不在RepositoryRestHandlerAdapter列表(DATAREST-838)上,因此抛出异常:
Failed to instantiate [com.querydsl.core.types.Predicate]: Specified class is an interface
列表中最相似的argumentResolver是QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver
,但我不知道它是否对此问题有用。
选项2 :这个问题是一个典型的交叉问题,因此我尝试应用AOP。定义建议并更新args(谓词):
@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);
}
结果与默认的Repository方法不同,而不是原始的JSON对象(使用_embedded,page,_links),响应为:
{
"_links" : {
"profile" : {
"href" : "http://localhost:8087/api/profile/projects"
}
}
}
选项3
使用@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);
(...)
}
}
使用相同的路径@RequestMapping("/projects",...)
也会覆盖其他端点(PUT,POST,...),这是不可取的。这被迫定义另一个端点("projects/search")
。
我认为这种解决方法并不优雅,需要新闻端点。我确信使用Spring Data REST存在更好的替代方案。
问题:
Spring Data REST 2.5.6版
虽然已经解决,但我希望收到反馈和建议。我希望QueryDSLPredicate注释将被修复,文档将会有更多关于此问题的示例进行改进。
答案 0 :(得分:1)
最后我运行选项2 我的错误不是返回proceed
调用结果:
@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);
}
}