【问题标题】:Spring Data Rest Override Repositories (Controllers vs AOP)Spring Data Rest Override Repositories (Controllers vs AOP)
【发布时间】:2017-08-06 10:45:36
【问题描述】:

域/存储库

Project {
   User owner;
}

//Querydsl repositories
@RepositoryRestResource
public interface ProjectRepository extends PagingAndSortingRepository<Project, Long>, QueryDslPredicateExecutor<Project>, QuerydslBinderCustomizer<QProject> {  
   default void customize(QuerydslBindings bindings, QProject project) {
      (...)
   }
}

要求: 根据经过身份验证的用户上下文过滤数据:

  • 如果用户是ROLE_PUBLIC,则根据predicate 显示项目,并且用户是owner
  • 如果用户是ROLE_ADMIN,则根据predicate 过滤器显示项目。

我尝试通过几种方法解决:

选项 1:覆盖 @RepositoryRestController,如 Spring 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。定义 Advice 并更新 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 存在更好的替代方案。

问题:

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

Spring Data REST 版本 2.5.6

虽然已解决,但我希望收到反馈和建议。我希望 QueryDSLPredicate 注释将得到修复,文档将通过更多关于此问题的示例得到改进。

【问题讨论】:

    标签: java spring spring-aop spring-data-rest querydsl


    【解决方案1】:

    最后我运行 Option 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);
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 2015-02-07
      • 2019-01-30
      • 2021-12-01
      • 2015-12-25
      • 2020-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      相关资源
      最近更新 更多