I'm designing JAX-RS APIs.
POST /myentities
PUT /myentities/{myentityId}
I did like this.
@PUT
@Path("/{myentityId: \\d+}")
@Consumes(...)
@Transactional
public Response updateMyentity(
@PathParam("myentityId") final long myentityId,
final Myentity myentity) {
if (!Objects.equal(myentitiyId, myentity.getId())) {
// throw bad request
}
entityManager.merge(myentity);
return Response.noContent().build();
}
I suddenly get curious and questioned to my self.
When a client invokes following request
PUT /myentities/101 HTTP/101
Host: ..
<myentity id=101>
</myentity>
Is it possible that the request is processed even if there is no resource identified by 101
?
I ran a test.
acceptEntityManager(entityManager -> {
// persist
final Device device1 = mergeDevice(entityManager, null);
entityManager.flush();
logger.debug("device1: {}", device1);
assertNotNull(device1.getId());
// remove
entityManager.remove(device1);
entityManager.flush();
assertNull(entityManager.find(Device.class, device1.getId()));
// merge again, with non existing id
final Device device2 = entityManager.merge(device1);
entityManager.flush();
logger.debug("device2: {}", device2);
});
I found that second merge works and a new id assigned.
Is this normal? Shouldn't EntityManager#merge
deny the operation?
Is it mean that any client can attack the API by calling
PUT /myentities/<any number>
?
If I understand this right, then yes, this is how merge should work.
merge
creates a new instance of your entity class, copies the current state of the entity that you provide (in your casedevice1
, and adds that NEW instance to the transaction. So the NEW instance is managed, and any changes you make to the old instance are not updated in the transaction and will not be propagated to the database viaflush
- operation.So while you removed
device1
from the database withentityManager.remove(device1);
the callfinal Device device2 = entityManager.merge(device1);
adds a new copy of it (with new ID) to the database. Changes you make todevice1
are not tracked in the transaction, so they won't be reflected in the database tables once youflush
. Changes todevice2
on the other hand WILL be tracked in the transaction.