首页 > 解决方案 > 覆盖 Spring Data Rest 自动创建的存储库端点

问题描述

我有一个 Spring 项目spring-data-rest作为依赖项。我的项目中有很多存储库,它们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 injection into custom @RepositoryRestController in a @WebMvcTest unit test 的请求方法中所述

如果我替换@RestControllerRepositoryRestController但随后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)。这引发了映射冲突错误。

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

标签: springspring-data-rest

解决方案


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

RepositoryRestMvcConfiguration有一个DelegatingHandlerMappingbean,其中包含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是默认的,您可以在编写逻辑时检查它。这可能会有所帮助。


推荐阅读