覆盖Spring Data Rest自动创建的存储库端点

时间:2019-04-17 12:26:47

标签: spring spring-data-rest

我有一个带有spring-data-rest作为依赖项的Spring项目。我的项目中有很多存储库,spring-data-rest会自动为其创建REST API端点。到目前为止,这非常适合我的需求。现在,我需要更改所有存储库(特别是/BASE_PATH/REPOSITORY)的一个端点的默认功能。此路径以数据库的所有记录的分页列表作为响应。

现在,我想为我所有的存储库重新实现此端点。这是我遇到障碍的地方。我尝试过

@RestController
public class MyTableResource {
    private MyTableService myTableService;

    @Autowired
    public MyTableResource(MyTableService myTableService) {
        this.myTableService = myTableService;
    }

    @GetMapping(value = "/api/v1/myTables", produces = MediaTypes.HAL_JSON_VALUE)
    public ResponseEntity getMyTables(@QuerydslPredicate(root = MyTable.class) Predicate predicate) throws NoSuchMethodException {
        // My custom implementation
    }
}

现在这有点奏效了,但问题是我需要为我的所有存储库编写几乎相同的代码。我尝试过@GetMapping(value = "/api/v1/{repository}", produces = MediaTypes.HAL_JSON_VALUE),但这也与我单独实现的/api/v1/notarepository相匹配。

此外,即使我做了@GetMapping(value = "/api/v1/{repository}", produces = MediaTypes.HAL_JSON_VALUE),我也想使用MyTable路径变量(在其中为{repository})来获取存储库对象(myTables)的句柄这种情况。

简而言之,我想为我所有的存储库编写一个自定义控制器,因为每个存储库的逻辑都是相同的,同时确保根据调用的路径调用正确的存储库,并确保任何路径我介绍的变量不会隐藏我编写的其他控制器类。

我尝试过的更多事情

我试图从实体列表中自动获取分页的HATEOAS资源对象。为此,我发现我可以使用PagedResourceAssembler

@RestController
public class MyTableResource {
    private MyTableService myTableService;

    @Autowired
    public MyTableResource(MyTableService myTableService) {
        this.myTableService = myTableService;
    }

    @GetMapping(value = "/api/v1/myTables", produces = MediaTypes.HAL_JSON_VALUE)
    public ResponseEntity getMyTables(@QuerydslPredicate(root = MyTable.class) Predicate predicate, PagedResourcesAssembler<Object> pagedResourcesAssembler) throws NoSuchMethodException {
        // My custom implementation
        return ResponseEntity.ok(pagedResourcesAssembler.toResource(myTableList);
    }
}

这为我提供了所需的页面链接很好的响应,但没有提供每个实体的链接。然后我发现我可以挂上PersistentEntityResourceAssembler并将其传递到上面的toResource

@RestController
public class MyTableResource {
    private MyTableService myTableService;

    @Autowired
    public MyTableResource(MyTableService myTableService) {
        this.myTableService = myTableService;
    }

    @GetMapping(value = "/api/v1/myTables", produces = MediaTypes.HAL_JSON_VALUE)
    public ResponseEntity getMyTables(@QuerydslPredicate(root = MyTable.class) Predicate predicate, PagedResourcesAssembler<Object> pagedResourcesAssembler, PersistentEntityResourceAssembler assembler) throws NoSuchMethodException {
        // My custom implementation
        return ResponseEntity.ok(pagedResourcesAssembler.toResource(myTableList, assembler);
    }
}

这与How to have PersistentEntityResourceAssembler injected into request methods of custom @RepositoryRestController in a @WebMvcTest unit test中的报告不起作用。

如果我将@RestController替换为RepositoryRestController,但是Predicate停止工作,如https://jira.spring.io/browse/DATAREST-838所述,这是可行的。

因此,我尝试使用@QuerydslPredicate RootResourceInformation resourceInformation而不是@QuerydslPredicate(root = MyTable.class) Predicate predicate。这也没有用,因为我的控制器端点中没有/{repository}

然后我尝试设置@GetMapping(value = "/{repository}" produces = MediaTypes.HAL_JSON_VALUE)。这引发了映射冲突错误。

所以我完全不知道下一步该怎么做。

1 个答案:

答案 0 :(得分:0)

您可以通过扩展RepositoryRestMvcConfiguration来扩展Spring Data Rest提供的默认行为。

RepositoryRestMvcConfiguration有一个DelegatingHandlerMapping bean,其中包含HandlerMapping的列表。 Spring遍历此列表,并尝试查找该请求的处理程序。此列表的顺序很重要。第一个首先被拾取执行。因此,如果我们在已有的处理程序之前添加一个新的处理程序,则将调用HandlerMapping

您可以使用任何想要查找请求处理程序的逻辑。就您而言,这就是路径变量是存储库名称。

以下代码添加了一个新的处理程序:

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

@Configuration
public class CustomRestMvcConfiguration extends RepositoryRestMvcConfiguration {
    public CustomRestMvcConfiguration(ApplicationContext context,
                                      ObjectFactory<ConversionService> conversionService) {
        super(context, conversionService);
    }

    @Override public DelegatingHandlerMapping restHandlerMapping() {
        DelegatingHandlerMapping delegatingHandlerMapping = super.restHandlerMapping();

        List<HandlerMapping> delegates = delegatingHandlerMapping.getDelegates();
        delegates.add(0, new HandlerMapping() {
            @Override public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
                //Your custom logic to decide if you should handle the request
                //If you don't want to handle the request return null
                return null;
            }
        });

        return new DelegatingHandlerMapping(delegates);
    }
}

希望这会有所帮助!

注意:RepositoryRestHandlerMapping是默认的,您可以在编写逻辑时检查它。可能会有帮助。