Spring Data REST:覆盖控制器上的存储库方法

Nic*_*las 17 java rest spring spring-data-rest

我有以下REST存储库,其实现由Spring在运行时生成.

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

}
Run Code Online (Sandbox Code Playgroud)

这意味着我将通过REST提供save(),find(),exists()和其他可用方法.

现在,我想覆盖其中一个方法; 例如,save().为此,我将创建一个暴露该方法的控制器,如下所示:

@RepositoryRestController
@RequestMapping("/foo")
public class FooController {

    @Autowired
    FooService fooService;


    @RequestMapping(value = "/{fooId}", method = RequestMethod.PUT)
    public void updateFoo(@PathVariable Long fooId) {
        fooService.updateProperly(fooId);
    }

}
Run Code Online (Sandbox Code Playgroud)

问题: 如果我启用了这个控制器,那么Spring实现的所有其他方法都不再暴露.因此,例如,我不能再对/ foo/1执行GET请求

问题: 是否有一种覆盖REST方法的方法,同时仍保留其他自动生成的Spring方法?

额外信息:

  1. 这个问题看起来非常相似: Spring Data Rest:RestController中的覆盖方法具有相同的请求映射路径 ......但我不想将路径更改为/ foo/1/save之类的东西

  2. 我想过使用@RepositoryEventHandler,但我不是很喜欢这个想法,因为我想把它封装在一个服务之下.此外,您似乎失去了对事务上下文的控制.

  3. Spring Data文档的这一部分说明如下:

    有时您可能希望为特定资源编写自定义处理程序.要利用Spring Data REST的设置,消息转换器,异常处理等,请使用@RepositoryRestController注释而不是标准的Spring MVC @Controller或@RestController

所以它似乎应该开箱即用,但不幸的是没有.

Mar*_*rin 13

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

仔细查看文档中的示例:虽然没有明确禁止类级请求映射,但它使用方法级请求映射.我不知道这是否是想要的行为或错误,但据我所知,这是使其工作,如规定的唯一途径这里.

只需将您的控制器更改为:

@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
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这也不起作用。如果我这样做,那么Spring实现的GET方法将不起作用。 (3认同)
  • 似乎对我有用(spring-boot-starter-data-rest 1.4.1.RELEASE)“@RepositoryRestController”与“@RestController”也达到了目的。 (2认同)
  • 还必须将“@ResponseBody”添加到重写的控制器方法中。 (2认同)
  • 这样做会丢失HATEOAS格式...是否可以选择保持相同格式? (2认同)
  • @Rafael:没有选择。您必须使用(扩展)[Resource](http://docs.spring.io/spring-hateoas/docs/current/api/org/springframework/hateoas/Resource.html)和[ResourceAssemblerSupport](http:// docs.spring.io/spring-hateoas/docs/current/api/org/springframework/hateoas/mvc/ResourceAssemblerSupport.html)。官方[documentation]中有信息(http://docs.spring.io/autorepo/docs/spring-hateoas/0.20.x/reference/html/#fundamentals.resource-assembler)。您还可以阅读[this](http://stackoverflow.com/a/29924387/5873923)和[this](http://stackoverflow.com/a/26551904/5873923) (2认同)

Dan*_*edo 9

让我们假设我们有一个Account实体:

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

随着AccountRepository揭露它的CRUD端点/accounts:

@RepositoryRestResource(collectionResourceRel = "accounts", path = "accounts")
public interface AccountRepository extends CrudRepository<Account, Integer> {
} 
Run Code Online (Sandbox Code Playgroud)

并且AccountController它会覆盖默认GET端点表单AccountRepository.:

@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);
    }
Run Code Online (Sandbox Code Playgroud)

如果您调用,GET /accounts?size=5&page=0您将获得以下使用模拟实现的输出:

{
  "_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
  }
}
Run Code Online (Sandbox Code Playgroud)

只是为了完整性,可以使用以下父级和依赖项配置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>
Run Code Online (Sandbox Code Playgroud)


小智 5

只是我发现的一个更新救了我的命。正如 @mathias-dpunkt 在这个答案中精彩地说的那样 /sf/answers/2416271651/

最重要的是,RepositoryRestController 知道 Spring Data Rest 基本路径,并将在此基本路径下提供服务。

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

你必须从@RequestMapping中省略“/api”