我正在尝试创建一个Spring MVC控制器,它允许使用HTML表单创建和编辑一个简单的域对象(Player
)。 And preferably in a RESTful manner。当提交的表单中出现错误时,我遇到了编辑问题。我希望客户端(浏览器)显示错误消息和原始表单,以便用户更正并重新提交。
但我无法让它发挥作用。如果出现错误,我可以让Web应用程序重新显示原始表单,但不会显示错误消息。我认为因为我的代码在这种情况下重定向到表单页面。我尝试删除重定向,但随后Web服务器抱怨资源不允许PUT
。我需要做什么?
以下是我的控制器的相关代码:
@Controller
@RequestMapping({
"/player"
})
public class PlayerController {
@RequestMapping(value = "/{id}/edit", method = RequestMethod.PUT)
public String editPlayer(@PathVariable("id")
final long id, @Valid
@ModelAttribute(Model.PLAYER)
final PlayerModel player, final BindingResult result) {
if (!result.hasErrors()) {
final Player playerEntity = playerService.find(id);
playerEntity.setName(player.getName());
playerService.update(playerEntity);
return "redirect:/player/" + id;
} else {
return "redirect:/player/" + id + "/edit";
}
}
@RequestMapping(value = "/{id}/edit", method = RequestMethod.GET)
public String showEditPlayerPage(@PathVariable("id")
final long id, final org.springframework.ui.Model model) {
createModel(id, model);
return View.EDIT_PLAYER;// the player editing page
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String showPlayerPage(@PathVariable("id")
final long id, final org.springframework.ui.Model model) {
// ...
return View.PLAYER;// the read-only view of a player
}
// Other code
}
我在PersonModel
上有JSR-303验证注释,如果name
太短或太长,会触发验证失败。编辑表单的HTML是:
<form:form commandName="player" method="PUT">
<fieldset>
<table>
<tr>
<th><label for="player_name">Player Name:</label></th>
<td><form:input path="name" size="64" maxlength="64"
id="player_name" /> <br /> <small
id="player_name_msg">Not empty, but no more
than 64 characters.</small> <form:errors path="name"
class="error" /></td>
</tr>
</table>
<input type="submit" value="Submit"></input>
</fieldset>
</form:form>
修改
为了清楚起见,对于表单有效的情况,一切正常。我有用于将PUT转换为POST的servlet过滤器,它似乎工作正常。
编辑2:
实验和修补显示我的控制器正在执行; >执行我的控制器后,发生了对PUT的拒绝。看起来Spring不喜欢对PUT的响应有一个视图名称。
答案 0 :(得分:0)
您需要在HiddenHttpMethodFilter
中添加web.xml
才能使PUT/DELETE
正常工作:
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<servlet-name>your-servlet</servlet-name>
</filter-mapping>
来自Spring Documentation:
...虽然HTTP定义了这四种方法,但HTML只支持两种:GET 和POST。幸运的是,有两种可能的解决方法:你可以 要么使用JavaScript来执行PUT或DELETE,要么只是执行POST 使用'真实'方法作为附加参数(建模为隐藏 HTML表单中的输入字段)。后一个技巧就是Spring的 HiddenHttpMethodFilter可以。这个过滤器是一个普通的Servlet过滤器和 因此它可以与任何Web框架结合使用(不是 只是Spring MVC)。只需将此过滤器添加到您的web.xml和POST 用隐藏的_method参数将转换为 相应的HTTP方法请求。
完整参考here。
修改强>
尝试使用<url-mapping/>
代码而不是<servlet-name/>
:
<filter>
<filter-name>httpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>httpMethodFilter</filter-name>
<url-mapping>/*</url-mapping>
</filter-mapping>
答案 1 :(得分:0)
因此,我的问题的原因是坚持使用HTTP PUT来改变玩家实体(资源)。我坚持这样做是因为我认为这是纯粹的RESTful做事方式:POST用于创建,PUT用于更改。但似乎我和其他人都错了:it's OK to use POST for alteration。
我改变了我的控制器:
@Controller
@RequestMapping({
"/player"
})
public class PlayerController {
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseStatus(HttpStatus.NO_CONTENT)
public void editPlayer(
@PathVariable("id") final long id,
@Valid @ModelAttribute(Model.PLAYER) final PlayerModel player,
final BindingResult result) {
// ...
}
@RequestMapping(value = "/{id}", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public String editPlayerWithView(
@PathVariable("id") final long id,
@Valid @ModelAttribute(Model.PLAYER) final PlayerModel player,
final BindingResult result) {
// ...
if (result.hasErrors()) {
return View.EDIT_PLAYER;
} else {
// ...
return View.PLAYER;
}
}
@RequestMapping(value = "/{id}/edit", method = RequestMethod.GET)
public String showEditPlayerPage(
@PathVariable("id") final long id,
final org.springframework.ui.Model model) {
// ...
return View.EDIT_PLAYER;
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String showPlayerPage(
@PathVariable("id") final long id,
final org.springframework.ui.Model model) {
// ...
return View.PLAYER;
}
我对我的表单做了一个简单的改动:
<form:form commandName="player" action="/player/${player.id}">
<!-- ... -->
</form:form>
现在,我可以使用HTML表单创建和更改来查看,创建和更改播放器实体(资源),并尝试无效创建或更改,从而导致在表单中向用户显示错误消息。
我可以给我的回答标题如何我学会停止担心并热爱POST。