Java Spring数据REST:覆盖控制器上的存储库方法

Java Spring数据REST:覆盖控制器上的存储库方法,java,spring,rest,spring-data-rest,Java,Spring,Rest,Spring Data Rest,我有以下REST存储库,它的实现是由Spring在运行时生成的 @RepositoryRestResource public interface FooRepository extends CrudRepository<Foo, Long> { } 问题: 如果启用此控制器,那么Spring实现的所有其他方法将不再公开。例如,我不能再对/foo/1执行GET请求 问题: 有没有一种方法可以覆盖REST方法,同时保留其他自动生成的Spring方法 额外信息: 这个问题似乎很相似:

我有以下REST存储库,它的实现是由Spring在运行时生成的

@RepositoryRestResource
public interface FooRepository extends CrudRepository<Foo, Long> {

}
问题: 如果启用此控制器,那么Spring实现的所有其他方法将不再公开。例如,我不能再对/foo/1执行GET请求

问题: 有没有一种方法可以覆盖REST方法,同时保留其他自动生成的Spring方法

额外信息:

  • 这个问题似乎很相似: ... 但我不想将路径更改为/foo/1/save之类的内容

  • 我曾想过使用@RepositoryEventHandler,但我不太喜欢这个想法,因为我想将它封装在服务中。此外,您似乎失去了对事务上下文的控制

  • 他说:

    有时,您可能需要为特定的应用程序编写自定义处理程序 资源。要利用Spring Data REST的设置,请发送消息 转换器、异常处理等都使用 @RepositoryRestController注释,而不是标准的Spring MVC @控制器或@RestController

  • 因此,它似乎应该是开箱即用的,但不幸的是不是

    有没有一种方法可以覆盖REST方法,同时保留其他自动生成的Spring方法

    仔细查看文档中的示例:虽然没有明确禁止类级别的requestmapping,但它使用方法级别的requestmapping。 我不确定这是想要的行为还是一个bug,但据我所知,这是使它工作的唯一方法,如上所述

    只需将控制器更改为:

    @RepositoryRestController
    public class FooController {
    
        @Autowired
        FooService fooService;
    
        @RequestMapping(value = "/foo/{fooId}", method = RequestMethod.PUT)
        public void updateFoo(@PathVariable Long fooId) {
            fooService.updateProperly(fooId);
        }
    
        // edited after Sergey's comment
        @RequestMapping(value = "/foo/{fooId}", method = RequestMethod.PUT)
        public RequestEntity<Void> updateFoo(@PathVariable Long fooId) {
            fooService.updateProperly(fooId);
    
            return ResponseEntity.ok().build(); // simplest use of a ResponseEntity
        }
    }
    
    @RepositoryRestController
    公共类FooController{
    @自动连线
    食品服务;
    @RequestMapping(value=“/foo/{fooId}”,method=RequestMethod.PUT)
    public void updateFoo(@PathVariable Long fooId){
    fooService.updateProperty(fooId);
    }
    //根据谢尔盖的评论进行编辑
    @RequestMapping(value=“/foo/{fooId}”,method=RequestMethod.PUT)
    public RequestEntity updateFoo(@PathVariable Long fooId){
    fooService.updateProperty(fooId);
    return ResponseEntity.ok().build();//ResponseEntity的最简单用法
    }
    }
    
    如果您使用Java 8,我发现了一个简洁的解决方案-只需在接口中使用默认方法即可

    @RepositoryRestResource
    public interface FooRepository extends CrudRepository<Foo, Long> {
        default <S extends T> S save(S var1) {
            //do some work here
        }
    }
    
    @RepositoryRestResource
    公共接口FoodRepository扩展了Crudepository{
    默认S保存(S var1){
    //在这里做些工作
    }
    }
    
    让我们假设我们有一个
    账户
    实体:

    @Entity
    public class Account implements Identifiable<Integer>, Serializable {
    
        private static final long serialVersionUID = -3187480027431265380L;
    
        @Id
        private Integer id;
        private String name;
    
        public Account(Integer id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        @Override
        public Integer getId() {
            return id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    
    以及一个覆盖默认
    GET
    端点表单
    AccountRepository
    AccountController

    @RepositoryRestController
    public class AccountController {
        private PagedResourcesAssembler<Account> pagedAssembler;
    
        @Autowired
        public AccountController(PagedResourcesAssembler<Account> pagedAssembler) {
            this.pagedAssembler = pagedAssembler;
        }
    
        private Page<Account> getAccounts(Pageable pageRequest){
            int totalAccounts= 50;
            List<Account> accountList = IntStream.rangeClosed(1, totalAccounts)
                                                 .boxed()
                                                 .map( value -> new Account(value, value.toString()))
                                                 .skip(pageRequest.getOffset())
                                                 .limit(pageRequest.getPageSize())
                                                 .collect(Collectors.toList());
            return new PageImpl(accountList, pageRequest, totalAccounts);
        }
    
        @RequestMapping(method= RequestMethod.GET, path="/accounts", produces = "application/hal+json")
        public ResponseEntity<Page<Account>> getAccountsHal(Pageable pageRequest, PersistentEntityResourceAssembler assembler){
            return new ResponseEntity(pagedAssembler.toResource(getAccounts(pageRequest), (ResourceAssembler) assembler), HttpStatus.OK);
        }
    
    为了完整起见,POM可以配置为具有以下父级和依赖项:

    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.2.RELEASE</version>
        </parent>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-rest-webmvc</artifactId>
                <version>2.6.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
            </dependency>
        </dependencies>
    
    
    org.springframework.boot
    spring启动程序父级
    1.5.2.1发布
    org.springframework.boot
    SpringBootStarterWeb
    org.springframework.data
    spring数据rest webmvc
    2.6.1.1发布
    org.springframework.boot
    spring引导启动器数据jpa
    com.h2数据库
    氢
    
    这只是我发现的一个救了我一命的更新。 正如@mathias dpunkt在这个回答中精彩地说的那样

    最重要的是,RepositoryRestController知道spring数据rest 基本路径,并将在此基本路径下提供服务

    因此,如果您的基本路径是“/api”,并且您正在使用@RepositoryRestController


    你必须从@RequestMapping

    中ommit“/api”这可能对你有帮助吗?我意识到这个问题不是Grails的问题,但这个概念与这里描述的问题/答案类似:@Tarmo:虽然我认为这可能可行,但它会迫使我继续向存储库中添加逻辑,我更喜欢将其保留在服务中。不幸的是,这也不起作用。如果我这样做了,那么Spring实现的GET方法就不起作用了。我似乎也能起作用(SpringBootStarter数据REST1.4.1.RELEASE)另外,
    @RepositoryRestController
    vs
    @RestController
    也起到了作用。还必须将
    @ResponseBody
    添加到重写的控制器方法中。这样做会丢失HATEOAS格式。。。有没有办法保持相同的格式?@Rafael:没有办法。您必须使用(扩展)和。官方文件中有相关信息。您还可以读取,这将覆盖此存储库的整个应用程序的
    save
    方法。如果这不是期望的行为,就不应该使用它,否则它是一个有效的选项。这就是答案!ResourceAssembler来自hateoas 1.0,称为RepresentationModelAssembler。看见
    @RepositoryRestController
    public class AccountController {
        private PagedResourcesAssembler<Account> pagedAssembler;
    
        @Autowired
        public AccountController(PagedResourcesAssembler<Account> pagedAssembler) {
            this.pagedAssembler = pagedAssembler;
        }
    
        private Page<Account> getAccounts(Pageable pageRequest){
            int totalAccounts= 50;
            List<Account> accountList = IntStream.rangeClosed(1, totalAccounts)
                                                 .boxed()
                                                 .map( value -> new Account(value, value.toString()))
                                                 .skip(pageRequest.getOffset())
                                                 .limit(pageRequest.getPageSize())
                                                 .collect(Collectors.toList());
            return new PageImpl(accountList, pageRequest, totalAccounts);
        }
    
        @RequestMapping(method= RequestMethod.GET, path="/accounts", produces = "application/hal+json")
        public ResponseEntity<Page<Account>> getAccountsHal(Pageable pageRequest, PersistentEntityResourceAssembler assembler){
            return new ResponseEntity(pagedAssembler.toResource(getAccounts(pageRequest), (ResourceAssembler) assembler), HttpStatus.OK);
        }
    
    {
      "_embedded": {
        "accounts": [
          {
            "name": "1",
            "_links": {
              "self": {
                "href": "http://localhost:8080/accounts/1"
              },
              "account": {
                "href": "http://localhost:8080/accounts/1"
              }
            }
          },
          {
            "name": "2",
            "_links": {
              "self": {
                "href": "http://localhost:8080/accounts/2"
              },
              "account": {
                "href": "http://localhost:8080/accounts/2"
              }
            }
          },
          {
            "name": "3",
            "_links": {
              "self": {
                "href": "http://localhost:8080/accounts/3"
              },
              "account": {
                "href": "http://localhost:8080/accounts/3"
              }
            }
          },
          {
            "name": "4",
            "_links": {
              "self": {
                "href": "http://localhost:8080/accounts/4"
              },
              "account": {
                "href": "http://localhost:8080/accounts/4"
              }
            }
          },
          {
            "name": "5",
            "_links": {
              "self": {
                "href": "http://localhost:8080/accounts/5"
              },
              "account": {
                "href": "http://localhost:8080/accounts/5"
              }
            }
          }
        ]
      },
      "_links": {
        "first": {
          "href": "http://localhost:8080/accounts?page=0&size=5"
        },
        "self": {
          "href": "http://localhost:8080/accounts?page=0&size=5"
        },
        "next": {
          "href": "http://localhost:8080/accounts?page=1&size=5"
        },
        "last": {
          "href": "http://localhost:8080/accounts?page=9&size=5"
        }
      },
      "page": {
        "size": 5,
        "totalElements": 50,
        "totalPages": 10,
        "number": 0
      }
    }
    
    <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.2.RELEASE</version>
        </parent>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.data</groupId>
                <artifactId>spring-data-rest-webmvc</artifactId>
                <version>2.6.1.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-jpa</artifactId>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
            </dependency>
        </dependencies>