首页 > 解决方案 > 我应该将我的实体 ID 放在 URL 中还是作为隐藏字段放入表单中?

问题描述

我认为就 REST 而言,ID 应该放在 URL 中,例如:

https://example.com/module/[ID]

然后我在那个 URL 上调用 GET、PUT、DELETE。我认为这很清楚。在 Spring MVC 控制器中,我将使用 @PathVariable 获取 ID。作品。

现在,我对 Spring MVC 的实际问题是,如果我这样做,我必须不将 ID 作为表单的一部分包含(以及),Spring 会发出类型的警告

Skipping URI variable 'id' since the request contains a bind value with the same name.

否则。而且只发送一次也很有意义,对吧?如果他们不匹配你会怎么做?

这很好,但我确实有一个用于我的表单支持 bean 的自定义验证器,它需要知道 ID!(它需要检查某个唯一名称是否已被用于不同的实体实例,但不能在不知道提交表单的 ID 的情况下进行)。

我还没有找到一种好方法来告诉验证器来自@PathVariable 的ID,因为验证甚至在我的控制器方法中的代码执行之前就已经发生了。

你将如何解决这个困境?

这是我的控制器(修改):

@Controller
@RequestMapping("/channels")
@RoleRestricted(resource = RoleResource.CHANNEL_ADMIN)
public class ChannelAdminController
{
    protected ChannelService channelService;
    protected ChannelEditFormValidator formValidator;

    @Autowired
    public ChannelAdminController(ChannelService channelService, ChannelEditFormValidator formValidator)
    {
        this.channelService = channelService;
        this.formValidator = formValidator;
    }

    @RequestMapping(value = "/{channelId}/admin", method = RequestMethod.GET)
    public String editChannel(@PathVariable Long channelId, @ModelAttribute("channelForm") ChannelEditForm channelEditForm, Model model)
    {
        if (channelId > 0)
        {
            // Populate from persistent entity
        }
        else
        {
            // Prepare form with default values
        }
        return "channel/admin/channel-edit";
    }

    @RequestMapping(value = "/{channelId}/admin", method = RequestMethod.PUT)
    public String saveChannel(@PathVariable Long channelId, @ModelAttribute("channelForm") @Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
    {
        try
        {
            // Has to validate in controller if the name is already used by another channel, since in the validator, we don't know the channelId
            Long nameChannelId = channelService.getChannelIdByName(channelEditForm.getName());
            if (nameChannelId != null && !nameChannelId.equals(channelId))
                result.rejectValue("name", "channel:admin.f1.error.name");
        }
        catch (EmptyResultDataAccessException e)
        {
            // That's fine, new valid unique name (not so fine using an exception for this, but you know...)
        }

        if (result.hasErrors())
        {
            return "channel/admin/channel-edit";
        }

        // Copy properties from form to ChannelEditRequest DTO
        // ...

        // Save
        // ...

        redirectAttributes.addFlashAttribute("successMessage", new SuccessMessage.Builder("channel:admin.f1.success", "Success!").build());
        // POST-REDIRECT-GET
        return "redirect:/channels/" + channelId + "/admin";
    }


    @InitBinder("channelForm")
    protected void initBinder(WebDataBinder binder)
    {
        binder.setValidator(formValidator);
    }
}

标签: javaspringrestspring-mvc

解决方案


我想我终于找到了解决方案。

事实证明,Spring 也将路径变量绑定到形成 bean!我没有在任何地方找到这个文档,也没有预料到它,但是当尝试重命名路径变量时,就像@DavidW 建议的那样(我本来希望在我的控制器方法中只有局部效果),我意识到由于前面提到的,有些东西坏了。

所以,基本上,解决方案是在表单支持对象上也有 ID 属性,但不包括 HTML 表单中的隐藏输入字段。这样,Spring 将使用路径变量并将其填充到表单上。@PathVariable甚至可以跳过控制器方法中的本地参数。


推荐阅读