HATEOAS RepresentationModelAssembler,POST with JSON body returns 500: IllegalArgumentException: 没有足够的变量值可用于扩展 'id'
HATEOAS RepresentationModelAssembler, POST with JSON body returns 500: IllegalArgumentException: Not enough variable values available to expand 'id'
我遵循了 Spring 关于使用 HATEOAS 构建 REST 的教程:https://spring.io/guides/tutorials/rest/ 并将其与 JPA 和 MySQL DB (Maven) 混合使用。当我 运行 应用程序时,我可以看到 MySQL workbench 中的前 2 个表很好(尽管有第 3 个表突然出现?)。如果我执行 GET /players,它工作正常。当我在 Postman 中执行 POST 请求 (http://localhost:8080/players) 时正文为 JSON: { "playerName":"Pedro" }
,我得到一个 500 状态并且我从 Spring
Not enough variable values available to expand 'id'] with root cause...
我想实现完整的CRUD操作。这里出现了很多疑问,因为 playerId 是自增的,参数 registrariondate 是一个 TIMESTAMP。这是次要的,因为我猜问题出在我的应用程序中使用 RepresentationModelAssembler,但不太确定如何处理响应和请愿。
这里是项目结构:
错误日志:
Hibernate:
insert
into
player
(player_name, registration_date)
values
(?, ?)
2021-04-02 11:20:03.781 ERROR 2312 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: java.lang.IllegalArgumentException: Not enough variable values available to expand 'id'] with root cause
java.lang.IllegalArgumentException: Not enough variable values available to expand 'id'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:370) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:263) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents$FullPathComponent.expand(HierarchicalUriComponents.java:917) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:434) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:52) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.UriComponents.expand(UriComponents.java:172) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.build(DefaultUriBuilderFactory.java:403) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.hateoas.UriTemplate.expand(UriTemplate.java:272) ~[spring-hateoas-1.2.5.jar:1.2.5]
at org.springframework.hateoas.Link.expand(Link.java:361) ~[spring-hateoas-1.2.5.jar:1.2.5]
at org.springframework.hateoas.Link.toUri(Link.java:434) ~[spring-hateoas-1.2.5.jar:1.2.5]
at RESTApiJWTAuthMySQL.controllers.PlayerController.createNewPlayer(PlayerController.java:66) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
控制器class:
@RestController
//@RequestMapping("/")
public class PlayerController {
@Autowired
private final PlayerRepository repository;
@Autowired
private final PlayerModelAssembler assembler;
PlayerController(PlayerRepository repository, PlayerModelAssembler assembler) {
this.repository = repository;
this.assembler = assembler;
}
@GetMapping("/players/")
public CollectionModel<EntityModel<Player>> all() {
List<EntityModel<Player>> players = repository.findAll().stream().map
(assembler::toModel).collect(Collectors.toList());
return CollectionModel.of(players, linkTo(methodOn(PlayerController.class).all()).withSelfRel());
}
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable Long playerId) {
Player player = repository.findById(playerId).orElseThrow(() -> new PlayerNotFoundException(playerId));
return assembler.toModel(player);
}
@PostMapping(path="/players", consumes="application/json")
public ResponseEntity<?> createNewPlayer(@RequestBody Player newPlayer) {
EntityModel<Player> entityModel = assembler.toModel(repository.save(newPlayer));
return ResponseEntity.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()).body(entityModel);
}
@PutMapping("/players/{id}")
public ResponseEntity<?> replacePlayer(@RequestBody Player newPlayer, @PathVariable Long playerId) {
Player updatedPlayer = repository.findById(playerId) //
.map(player -> {
player.setPlayerName(newPlayer.getPlayerName());
return repository.save(player);
}) //
.orElseGet(() -> {
newPlayer.setPlayerId(playerId);
return repository.save(newPlayer);
});
EntityModel<Player> entityModel = assembler.toModel(updatedPlayer);
return ResponseEntity //
.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) //
.body(entityModel);
}
@DeleteMapping("/players/{id}")
public ResponseEntity<?> deleteEmployee(@PathVariable Long playerId) {
repository.deleteById(playerId);
return ResponseEntity.noContent().build();
}
}
PlayerModelAssembler
class:
@Component
public class PlayerModelAssembler implements RepresentationModelAssembler<Player, EntityModel<Player>> {
@Override
public EntityModel<Player> toModel(Player player) {
return EntityModel.of(player, //
linkTo(methodOn(PlayerController.class).one(player.getPlayerId())).withSelfRel(),
linkTo(methodOn(PlayerController.class).all()).withRel("players"));
}
}
玩家实体class:
@Entity
@Table(name = "Player")
public class Player {
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
private Long playerId;
@Column (name = "player_name")
private String playerName;
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column (name = "registration_date", updatable = false)
private Date registrationDate;
@OneToMany (mappedBy="player", cascade = CascadeType.ALL, orphanRemoval=true)
private List<DiceRoll> diceRolls = new ArrayList<>();
public Player() {
}
public Player(Long playerId, String playerName, Date registrationDate) {
this.playerId=playerId;
this.playerName = playerName;
this.registrationDate = registrationDate;
}
//getter&setters//
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Player))
return false;
Player player = (Player) o;
return Objects.equals(this.playerId, player.playerId) && Objects.equals(this.playerName, player.playerName)
&& Objects.equals(this.registrationDate, player.registrationDate);
}
@Override
public int hashCode() {
return Objects.hash(this.playerId, this.playerName, this.registrationDate);
}
@Override
public String toString() {
return "Player{" + "id=" + this.playerId + ", name='" + this.playerName + '\'' + ", date of registration='" + this.registrationDate + '\'' + '}';
}
}
POM 文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.10.Final</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Schema.sql:
USE `dicegame`;
DROP TABLE IF EXISTS `Player`;
CREATE TABLE IF NOT EXISTS `Player`
(
`player_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`player_name` VARCHAR(45) NOT NULL,
`registration_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
DROP TABLE IF EXISTS `DiceRoll`;
CREATE TABLE IF NOT EXISTS `DiceRoll`
(
`diceRoll_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`d1` INT(55) NOT NULL,
`d2` INT(55) NOT NULL,
`result` VARCHAR(45) NOT NULL,
`diceRoll_registration` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`player_id` BIGINT NOT NULL
);
ALTER TABLE DiceRoll
ADD constraint FK_PLAYER_ID FOREIGN KEY (player_id)
REFERENCES Player (player_id);
如有任何帮助或指导,我们将不胜感激。我在教程中看到他们将 curl petiton 用作:
$ curl -v -X POST localhost:8080/players -H 'Content-Type:application/json' -d '{"playerName": "Pedro"}'
它应该可以工作。非常感谢。
方法PlayerModelAssembler.toModel(Player player)
使用PlayerController.one(@PathVariable Long playerId)
生成自link。
如果注解@PathVariable
的name
属性没有提供,Spring期望参数名与[=18=17=]包围的名字相同=].
在您的原始代码中,参数名称 playerId
与 id
不同。所以要修复它,
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable("id") Long playerId) {
}
或
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable Long id) {
}
或
@GetMapping("/players/{playerId}")
public EntityModel<Player> one(@PathVariable Long playerId) {
}
我遵循了 Spring 关于使用 HATEOAS 构建 REST 的教程:https://spring.io/guides/tutorials/rest/ 并将其与 JPA 和 MySQL DB (Maven) 混合使用。当我 运行 应用程序时,我可以看到 MySQL workbench 中的前 2 个表很好(尽管有第 3 个表突然出现?)。如果我执行 GET /players,它工作正常。当我在 Postman 中执行 POST 请求 (http://localhost:8080/players) 时正文为 JSON: { "playerName":"Pedro" }
,我得到一个 500 状态并且我从 Spring
Not enough variable values available to expand 'id'] with root cause...
我想实现完整的CRUD操作。这里出现了很多疑问,因为 playerId 是自增的,参数 registrariondate 是一个 TIMESTAMP。这是次要的,因为我猜问题出在我的应用程序中使用 RepresentationModelAssembler,但不太确定如何处理响应和请愿。
这里是项目结构:
错误日志:
Hibernate:
insert
into
player
(player_name, registration_date)
values
(?, ?)
2021-04-02 11:20:03.781 ERROR 2312 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: java.lang.IllegalArgumentException: Not enough variable values available to expand 'id'] with root cause
java.lang.IllegalArgumentException: Not enough variable values available to expand 'id'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:370) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:263) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents$FullPathComponent.expand(HierarchicalUriComponents.java:917) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:434) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:52) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.UriComponents.expand(UriComponents.java:172) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.build(DefaultUriBuilderFactory.java:403) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.hateoas.UriTemplate.expand(UriTemplate.java:272) ~[spring-hateoas-1.2.5.jar:1.2.5]
at org.springframework.hateoas.Link.expand(Link.java:361) ~[spring-hateoas-1.2.5.jar:1.2.5]
at org.springframework.hateoas.Link.toUri(Link.java:434) ~[spring-hateoas-1.2.5.jar:1.2.5]
at RESTApiJWTAuthMySQL.controllers.PlayerController.createNewPlayer(PlayerController.java:66) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
控制器class:
@RestController
//@RequestMapping("/")
public class PlayerController {
@Autowired
private final PlayerRepository repository;
@Autowired
private final PlayerModelAssembler assembler;
PlayerController(PlayerRepository repository, PlayerModelAssembler assembler) {
this.repository = repository;
this.assembler = assembler;
}
@GetMapping("/players/")
public CollectionModel<EntityModel<Player>> all() {
List<EntityModel<Player>> players = repository.findAll().stream().map
(assembler::toModel).collect(Collectors.toList());
return CollectionModel.of(players, linkTo(methodOn(PlayerController.class).all()).withSelfRel());
}
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable Long playerId) {
Player player = repository.findById(playerId).orElseThrow(() -> new PlayerNotFoundException(playerId));
return assembler.toModel(player);
}
@PostMapping(path="/players", consumes="application/json")
public ResponseEntity<?> createNewPlayer(@RequestBody Player newPlayer) {
EntityModel<Player> entityModel = assembler.toModel(repository.save(newPlayer));
return ResponseEntity.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()).body(entityModel);
}
@PutMapping("/players/{id}")
public ResponseEntity<?> replacePlayer(@RequestBody Player newPlayer, @PathVariable Long playerId) {
Player updatedPlayer = repository.findById(playerId) //
.map(player -> {
player.setPlayerName(newPlayer.getPlayerName());
return repository.save(player);
}) //
.orElseGet(() -> {
newPlayer.setPlayerId(playerId);
return repository.save(newPlayer);
});
EntityModel<Player> entityModel = assembler.toModel(updatedPlayer);
return ResponseEntity //
.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) //
.body(entityModel);
}
@DeleteMapping("/players/{id}")
public ResponseEntity<?> deleteEmployee(@PathVariable Long playerId) {
repository.deleteById(playerId);
return ResponseEntity.noContent().build();
}
}
PlayerModelAssembler
class:
@Component
public class PlayerModelAssembler implements RepresentationModelAssembler<Player, EntityModel<Player>> {
@Override
public EntityModel<Player> toModel(Player player) {
return EntityModel.of(player, //
linkTo(methodOn(PlayerController.class).one(player.getPlayerId())).withSelfRel(),
linkTo(methodOn(PlayerController.class).all()).withRel("players"));
}
}
玩家实体class:
@Entity
@Table(name = "Player")
public class Player {
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
private Long playerId;
@Column (name = "player_name")
private String playerName;
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column (name = "registration_date", updatable = false)
private Date registrationDate;
@OneToMany (mappedBy="player", cascade = CascadeType.ALL, orphanRemoval=true)
private List<DiceRoll> diceRolls = new ArrayList<>();
public Player() {
}
public Player(Long playerId, String playerName, Date registrationDate) {
this.playerId=playerId;
this.playerName = playerName;
this.registrationDate = registrationDate;
}
//getter&setters//
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Player))
return false;
Player player = (Player) o;
return Objects.equals(this.playerId, player.playerId) && Objects.equals(this.playerName, player.playerName)
&& Objects.equals(this.registrationDate, player.registrationDate);
}
@Override
public int hashCode() {
return Objects.hash(this.playerId, this.playerName, this.registrationDate);
}
@Override
public String toString() {
return "Player{" + "id=" + this.playerId + ", name='" + this.playerName + '\'' + ", date of registration='" + this.registrationDate + '\'' + '}';
}
}
POM 文件:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.10.Final</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Schema.sql:
USE `dicegame`;
DROP TABLE IF EXISTS `Player`;
CREATE TABLE IF NOT EXISTS `Player`
(
`player_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`player_name` VARCHAR(45) NOT NULL,
`registration_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
DROP TABLE IF EXISTS `DiceRoll`;
CREATE TABLE IF NOT EXISTS `DiceRoll`
(
`diceRoll_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`d1` INT(55) NOT NULL,
`d2` INT(55) NOT NULL,
`result` VARCHAR(45) NOT NULL,
`diceRoll_registration` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`player_id` BIGINT NOT NULL
);
ALTER TABLE DiceRoll
ADD constraint FK_PLAYER_ID FOREIGN KEY (player_id)
REFERENCES Player (player_id);
如有任何帮助或指导,我们将不胜感激。我在教程中看到他们将 curl petiton 用作:
$ curl -v -X POST localhost:8080/players -H 'Content-Type:application/json' -d '{"playerName": "Pedro"}'
它应该可以工作。非常感谢。
方法PlayerModelAssembler.toModel(Player player)
使用PlayerController.one(@PathVariable Long playerId)
生成自link。
如果注解@PathVariable
的name
属性没有提供,Spring期望参数名与[=18=17=]包围的名字相同=].
在您的原始代码中,参数名称 playerId
与 id
不同。所以要修复它,
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable("id") Long playerId) {
}
或
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable Long id) {
}
或
@GetMapping("/players/{playerId}")
public EntityModel<Player> one(@PathVariable Long playerId) {
}