java - PUT方法(RESTful)不能用作更新资源的方法

标签 java rest jax-rs put http-method

根据这篇文章(http://restcookbook.com/HTTP%20Methods/put-vs-post/),应该将PUT作为一种更新资源的方法。

但是,使用JAX_RS 2.0和Jersey 2.0练习RESTful时,我认为它不会更新特定的资源。
(即,我正在使用JAX_RS 2.0和Jersey 2.0研究RESTful)

这是这样的资源。

<customer>
    <name>Before</name>
    <postcode>111</postcode>
</customer>

我正在尝试做的是更新(也许我应该说“替换”)此资源。
    ClientConfig config = new ClientConfig();
    Client client = ClientBuilder.newClient(config);
    WebTarget target =  client.target("http://xxx/yyy/zzz/end.cust");

    Customer cust = new Customer();
    cust.setName("After");
    cust.setPostcode(222);

    target.path("Before").request().put(Entity.xml(cust));

@Id注释在“Customer”类中设置为“Name”,因此路径“Before”应用作ID,并且第一个资源(名为“Before”)应替换为第二个资源(名为“After” ”)。

但是,执行完上面的编码后,“Before”资源仍然保留,并且有新的“After”资源重新分配。
似乎PUT方法可以创建新资源,而不是更新某些内容。
(即,既有“之前”资源又有“之后”资源,并且没有任何更新)

我测试了POST方法以创建新资源,并且它按预期创建了新资源。

如果您发现我做错了什么或需要做什么,请您提供一些建议吗?

编辑

我将添加服务器端代码。用@PUT注释的方法是这样的。
 @PUT
 @Path("{id}")
 @Consumes({"application/xml", "application/json"})
 public void edit(@PathParam("id") String id, Customer entity) {
     super.edit(entity);
 }

这是一个名为CustomerFacadeREST.java的类,在我创建“来自数据库的RESTful服务”后自动创建。

根据NetBeans的文档,super.edit()方法最初就是这样的。
 public void edit(T entity) {
 getEntityManager().merge(entity);
 }

在“客户”类中,以这种方式将@Id设置为“名称”值。
 public class Customer implements Serializable {
     private static final long serialVersionUID = 1L;
     @Id
     @Basic(optional = false)
     @NotNull
     @Size(min = 1, max = 80)
     @Column(name = "Name")
     private String name;
     // Other fields, such as Postcode...

     public Customer() {
     }

     // Other constructors and methods...
     }

最佳答案

“HTTP动词”(例如PUT,GET,POST,DELETE)背后的思想只是协议语义的问题。仅执行HTTP PUT操作并不会带来任何神奇效果。这是我们开发人员在开发时应该理解的适当语义,因为这些语义是所有人都知道的(这就是存在协议的原因)。如果没有人遵循这些语义,那么世界将处于大萧条与世界末日之间。

就是说,这些动词(语义)是对使用某种动词执行请求的客户端的一种保证(或保证是一个更好的词),它将具有一些已知的语义。一个主要因素是等幂的想法。幂等的想法是,无论我发出多少次请求,结果都将相同(或产生相同的效果)。

某些HTTP动词被认为是幂等的,例如PUT,DELETE和GET。不管发出多少次完全相同的请求,总的想法是结果/效果应该相同。另一方面,据说POST不是幂等的,因为完全相同的POST请求可能会产生不同的结果,例如错误地再次提交订单或两次创建新客户。

如果我们想让世界变得更美好,并为彻底崩溃而拯救世界做出自己的贡献,我们应该学习这些语义,并通过遵循这些语义成为好公民。关于动词语义,不仅要了解幂等,还有很多要学习的知识,但是要了解很多,是一个好的开始。我建议也许拿起一本有关REST的好书来学习一些好的做法。或者,如果您想成为一个很酷的孩子,请take time to read the bible (actually the Fielding Dissertation)

综上所述,开发人员创建代码以遵循这些语义是我们的职责。您的方法创建新资源的原因可能是因为您正在使用代码创建新资源。也许这样的事情似乎更合适:

@PUT
@Path("/customers/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response updateCustomer(@PathParam("id") long id, 
                               Customer updateCustomer) {

    Customer customer = customerService.getCustomerById(id);
    if (customer == null) {
        throw new WebApplicationException("Can't find it", 404);
    }

    customer.setFirstName(updateCustomer.getFirstName());
    customer.setLastName(updateCustomer.getLastName());
    ...

    return Response.noContent().build();
}

因此,我们只是更新数据库中已经存在的客户。通常,通过更新的PUT请求,应该知道特定的客户资源URI。所以说客户请求http://blah.com/api/customers/1234,我们的服务将使用id 1234查找客户。如果找不到该资源,我们将返回404状态代码,因为该资源不存在。如果确实存在,那么我们将使用请求中提供的客户数据来更新客户。如果要创建一个不知道URI的新客户,则POST将是正确的,然后将客户表示形式发送到http://blah.com/api/customers

还要仅保留一个FYI:在许多情况下,发生这种情况是,客户请求(获取)资源(例如客户),并更新该客户表示,然后将其作为PUT请求与更新后的客户一起发回。从服务器上可以看出,它应该使用该信息来更新特定客户的数据。

更新

根据您的编辑。您完全不知道应该如何工作。
Customer cust = new Customer();
cust.setName("After");
cust.setPostcode(222);

target.path("Before").request().put(Entity.xml(cust));

这样做的问题在于,使用新的Customer时,您将标识符设置为"After",这与请求路径中的标识符不同,而是使用了"Before"。因此,路径变量{id}为“之前”。使用此请求URI,您表示要访问ID为“之前”的客户。如我的代码所示,检查数据库中是否存在ID为“Before”的客户是您的职责。如果不是,则应返回404 Not Found。您为新的name设置的Customer(id)应该是数据库中预期的ID。因此,如果要在数据库“之后”中使用ID更新客户。那么您应该在路径中输入“After”,而不是“Before”。我们不应该尝试更改标识符。

就像我说的那样,当我们想要更新资源时,通常,获取资源,更新某些字段(而不是标识符),然后将其发送回去。一个序列可能看起来像
final String PATH = "http://hello.com/api/customers"
WebTarget target = client.target(PATH);
Customer customer = target.path("1234").request().get(Customer.class);
// where 1234 is the id (or in your case `name` of the customer.
// I would avoid using the name as the DB id, that's why my example uses numbers
customer.setPostalCode(...);
target = client.target(PATH).path(customer.getName()); // getName should be 1234
Response response = target.request().put(Entity.xml(customer));

我们在路径中使用与提供的ID相同的ID,因为这是服务器中标识资源的方式。

关于java - PUT方法(RESTful)不能用作更新资源的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27314049/

相关文章:

file-upload - 如何在 WildFly 中设置 Undertow MAX_ENTITY_SIZE

java - 带有子资源定位器的 Jersey 版本控制

java - POJ 2136 垂直直方图

java - StreamCorruptedException,当使用 ObjectInputStream 时

java - Hibernate更新对象的非引用字段

java - Spring REST - 创建 ZIP 文件并将其发送到客户端

java - 如何使用 jersey 和 java 在客户端和 web 服务 rest 之间传输对象

java - 使用 ArgumentCaptor 验证模拟服务调用失败

Java:在 map 中查找空键:

rest - 返回隐藏了大多数属性的实体列表的 RESTful 资源的命名约定?