2017-08-06 108 views
1

域/存储库弹簧数据休息覆盖库(控制器VS AOP)

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过滤显示项目。

我试图解决throught几种选择:

选项1:覆盖@RepositoryRestControllerSpring DATA REST医生说:

@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); 
} 

结果是不一样的默认存储库的方法,而不是原来的JSON对象(_embedded,页面,_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存在更好的选择。

问题:

  • 有关如何解决这个requeriment任何建议吗?
  • 如何将AOP应用于Spring Data REST以解决横切需求?
  • 为什么@QuerydslPredicate在参数解析器?

Spring Data REST version 2.5。6

虽然已经解决,但我想获得反馈和建议。我希望QueryDSLPredicate注解将被修复,文档将会被更多关于这个问题的示例所改进。

回答

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

}