Spring Data Rest Override Repositories (Controllers vs AOP)

。_饼干妹妹 提交于 2019-12-08 07:53:06

问题


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 according predicate and where user is the owner.
  • If user is ROLE_ADMIN show projects according predicate 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!