Java 我应该将实体的ID作为隐藏字段放在URL中还是放在表单中?

Java 我应该将实体的ID作为隐藏字段放在URL中还是放在表单中?,java,spring,rest,spring-mvc,Java,Spring,Rest,Spring Mvc,我认为就REST而言,ID应该放在URL中,比如: 然后我在那个URL上调用GET,PUT,DELETE。我想这有点清楚。在SpringMVC控制器中,我将使用@PathVariable获取ID。工作 现在,SpringMVC的实际问题是,如果我这样做,我就不必将ID作为表单的一部分,Spring会发出类型为的警告 Skipping URI variable 'id' since the request contains a bind value with the same name. 否则

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

然后我在那个URL上调用GET,PUT,DELETE。我想这有点清楚。在SpringMVC控制器中,我将使用@PathVariable获取ID。工作

现在,SpringMVC的实际问题是,如果我这样做,我就不必将ID作为表单的一部分,Spring会发出类型为的警告

Skipping URI variable 'id' since the request contains a bind value with the same name.
否则。而且只发送一次也是有意义的,对吗?如果他们不匹配,你会怎么做

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

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

你将如何解决这个困境

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


我认为,解决这个问题的最干净的方法是让数据库处理重复项:向数据库列添加唯一约束。(或JPA通过添加
@UniqueConstraint
) 但是您仍然必须捕获数据库异常并将其转换为用户友好的消息


通过这种方式,您可以使spring MVC验证器保持简单:只验证字段,而不需要查询数据库。

您所说的都是正确的设计rest api的正确方法是在path变量中提到资源id。如果您现在将swagger中的一些示例作为开放api查看,您可以在那里找到类似的示例

对于您来说,正确的解决方案是使用这样的自定义验证器

import javax.validation.Validator;`
import org.apache.commons.lang3.StringUtils;`
import org.springframework.validation.Errors;`
importorg.springframework.validation.beanvalidation.CustomValidatorBean;`

public class MyValidator extends CustomValidatorBean {`
    public void myvalidate(Object target,Errors errors,String flag,Profile profile){
        super.validate(target,errors);
        if(StringUtils.isEmpty(profile.name())){
            errors.rejectValue("name", "NotBlank.profilereg.name", new Object[] { "name" }, "Missing Required Fields");
        }       

    }           

}

这将确保所有字段都经过验证,并且您不需要在表单中传递id。

您是否可以使用不同的URI模板变量名称来消除2(URI模板变量与参数)之间的歧义

@RequestMapping(value = "/{chanId}/admin", method = RequestMethod.PUT)
public String saveChannel(@PathVariable Long chanId, @ModelAttribute("channelForm") @Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
[...]

我想我终于找到了解决办法

事实证明,Spring也将路径变量绑定到Bean中!我在任何地方都没有发现这个文档,也没有预料到它,但是当尝试重命名path变量时,就像@DavidW建议的那样(我希望它在我的控制器方法中只会产生局部效果),我意识到一些东西被破坏了,因为前面提到的


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

谢谢,但我的问题实际上更多地指向@PathVariable vs.form hidden field问题,而不是实际的用例。您很可能是对的,让DB处理副本在这种情况下可能是最好的。但在其他情况下,验证器(或处理表单对象的另一个助手)可能需要知道ID。可能是为了区分现有ID>0或新ID=0条目。PUT表示“用请求正文中的版本替换当前版本”。如果主体包含一个ID,那么看起来该ID可以更改。。。我只会在pathVariable@TomVanRossom我认为您应该将Ressource与域实体分离。URL中id的问题之一是,用户可能会尝试猜测id,并尝试查看/修改他应该访问的数据。这将阻止警告,是的。但这并不能防止这两个值之间可能存在的歧义。如果他们不一样,你应该依靠哪一个?我知道你已经想出了一个解决办法,但我想我还是会回答的。我总是相信API(url参数)而不是数据(表单字段),除非数据是以某种方式委托的,比如加密签名。API是动作试图做什么的具体说明;我不相信任何忽略非可选API参数的实现。如果数据与API值不同,您可能需要标记(日志/错误),但也可能有这样做的正当理由,例如复制操作。CustomValidatorBean如何帮助我不需要表单中的ID?我有一个自定义验证器,但它只是实现了
验证器
。但在这里,我需要ID来执行一些验证逻辑。您可以在ID到达java层后将其传递给验证器,但验证器在调用我的代码之前执行。它与@Valid一起使用,我喜欢它。这就是当您使用自定义验证器时,您可以控制何时调用它,而不需要使用验证器注释来注释POJO
@RequestMapping(value = "/{chanId}/admin", method = RequestMethod.PUT)
public String saveChannel(@PathVariable Long chanId, @ModelAttribute("channelForm") @Valid ChannelEditForm channelEditForm, BindingResult result, Model model, RedirectAttributes redirectAttributes)
{
[...]