restresource - Con Spring Data REST, ¿por qué la propiedad @Version se está convirtiendo en ETag y no está incluida en la representación?
spring data rest example (1)
En Spring Data REST (a través de Spring Boot 1.3.3), cuando GET
una colección de recursos de, por ejemplo, people
, la propiedad @Version
no se incluye con los recursos:
$curl -v http://localhost:8080/api/people/1
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /api/people/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.42.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< ETag: "0"
< Last-Modified: Tue, 26 Apr 2016 00:08:12 GMT
< Content-Type: application/hal+json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Tue, 26 Apr 2016 00:12:56 GMT
<
{
"id" : 1,
"createdDate" : {
"nano" : 351000000,
"epochSecond" : 1461629292
},
"lastModifiedDate" : {
"nano" : 351000000,
"epochSecond" : 1461629292
},
"firstName" : "Bilbo",
"lastName" : "Baggins",
"_links" : {
"self" : {
"href" : "http://localhost:8080/api/people/1"
},
"person" : {
"href" : "http://localhost:8080/api/people/1"
}
}
* Connection #0 to host localhost left intact
de forma predeterminada, o cuando configuro mi repositorio de Spring Data:
@Configuration
public class ApplicationRepositoryConfiguration
extends RepositoryRestMvcConfiguration
{
@Override
protected void configureRepositoryRestConfiguration(
RepositoryRestConfiguration config
)
{
config.exposeIdsFor(Person.class);
config.setBasePath("/api/");
}
}
La versión @Version
es la versión de la fila de datos que se incrementa en las actualizaciones y se incluye en los datos del encabezado HTTP ETag
cuando consulto un recurso específico. En lugar de tener que invocar un GET
en cada recurso de la colección, preferiría obtener @Version
en la colección GET
para que pueda escribir mi aplicación para verificar el valor de @Version
en cada actualización de recursos sin realizar la suma adicional GET
round- excursiones.
¿Hay alguna manera de incluir el campo @Version
en cada uno de los recursos de una colección GET
?
La definición de entidad se ve así:
@Data @Entity @EntityListeners(AuditingEntityListener.class)
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@CreatedDate
@Column(nullable=false)
private Instant createdDate;
@LastModifiedDate
@Column(nullable=false)
private Instant lastModifiedDate;
@Version
@JsonProperty
private Long version;
…
}
No no hay. El ETag es el HTTP equivalente a lo que se expresa como propiedad @Value
en el back-end. Spring Data REST convierte todas las propiedades relacionadas con back-end que tienen un mecanismo correspondiente en el protocolo HTTP exactamente en esas: los identificadores se convierten en URI (y no deben formar parte de la carga útil), @LastModifiedDate
propiedades @LastModifiedDate
convierten en encabezados, @Version
propiedades @Version
convierten en ETags.
La razón es bastante simple: si usa HTTP, use los medios de protocolo que están disponibles para lograr cosas que se implementan en el nivel de acceso a datos. Ese es un aspecto en el que Spring Data REST no está simplemente exponiendo una base de datos a la web, sino que realmente inspecciona su modelo y traduce las características del modelo en medios específicos del protocolo.
Para resumir: con Spring Data REST tiene dos opciones para las actualizaciones:
- Simplemente
PUT
sin un encabezadoIf-Match
: impone la anulación de todo lo que está presente en el servidor a medida que se carga el agregado, los datos entrantes se asignan y se escriben de nuevo. Aún se aplica el bloqueo optimista si otro cliente cambió el agregado mientras tanto (aunque es una ventana muy corta). Si ese es el caso, verás un409 Conflict
. -
PUT
con un encabezadoIf-Match
- Spring Data REST comprueba el ETag presentado contra el valor actual de la propiedad de versión del agregado y devuelve un412 Precondition Failed
en caso de que exista una discrepancia en ese punto. En ese caso, los clientes pueden buscar el estado actual del recurso y decidir cómo proceder. Podrían decidir anular lo que está en el servidor usandoPUT
sin un encabezadoIf-Match
.
Se pueden realizar optimizaciones similares para las solicitudes GET:
-
GET
conIf-None-Match
(ETag) /If-Modified-Since
(con el valor de encabezado Last-Modified) - Verá un304 Not Modified
en caso de que el recurso aún se encuentre en el mismo estado que antes y así evitará gastar ancho de banda para la respuesta. - Plain
GET
siempre devolverá la representación.