2017-08-06 109 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); 
    } 

}