根据这篇文章(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/