services example and java spring spring-data-rest

java - example - PUT y POST fallan en propiedades desconocidas Spring comportamiento diferente



rest services put example (4)

Creo que el comportamiento que estás observando es por diseño. Cuando se emite un POST, está creando el recurso, por lo que JSON se deserializa en su tipo de entidad y Jackson está realizando esta tarea.

Un PUT está funcionando de manera diferente en el resto de datos de primavera. La parte interesante se maneja en PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate .

El json se lee en un JsonNode , la entidad se lee desde el almacén de datos y luego en DomainObjectReader.doMerge la implementación itera sobre los campos de json. Aplica el json a la entidad y lo guarda más adelante en la implementación del controlador. También descarta todos los campos que no existen en la entidad persistente:

if (!mappedProperties.hasPersistentPropertyForField(fieldName)) { i.remove(); continue; }

Este es mi entendimiento de leer el código. Creo que podrías argumentar que esto es un error. Puede intentar reportarlo en Spring Data Rest`s Jira - https://jira.spring.io/browse/DATAREST . Por lo que sé, no hay forma de personalizar este comportamiento.

Estoy escribiendo la aplicación Spring Boot usando los repositorios de Spring Data Rest y quiero denegar el acceso al recurso si el cuerpo de la solicitud contiene JSON que tiene propiedades desconocidas. Definición de entidad simplificada y repositorio:

@Entity public class Person{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String firstName; private String lastName; /* getters and setters */ } @RepositoryRestResource(collectionResourceRel = "people", path = "people") public interface PersonRepository extends CrudRepository<Person, Long> {}

Utilizo la función de deserialización de Jackson para no permitir propiedades desconocidas en JSON.

@Bean public Jackson2ObjectMapperBuilder objectMapperBuilder(){ Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); builder.failOnUnknownProperties(true); return builder; }

Cuando envío solicitudes POST todo funciona como se espera. Cuando uso campos válidos obtengo la respuesta correcta:

curl -i -x POST -H "Content-Type:application/json" -d ''{"firstName": "Frodo", "lastName": "Baggins"}'' http://localhost:8080/people { "firstName": "Frodo", "lastName": "Baggins", "_links": {...} }

Y cuando envío JSON con campos desconocidos, la aplicación arroja el error esperado:

curl -i -x POST -H "Content-Type:application/json" -d ''{"unknown": "POST value", "firstName": "Frodo", "lastName": "Baggins"}'' http://localhost:8080/people com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "unknown" (class Person), not marked as ignorable (2 known properties: "lastName", "firstName")

El método PUT cuando se usa JSON válido también devuelve la respuesta correcta. Sin embargo, cuando envío una solicitud PUT con un campo desconocido, espero que Spring arroje un error, pero en lugar de eso, Spring actualiza el objeto en la base de datos y lo devuelve:

curl -i -x PUT -H "Content-Type:application/json" -d ''{"unknown": "PUT value", "firstName": "Bilbo", "lastName": "Baggins"}'' http://localhost:8080/people/1 { "firstName": "Bilbo", "lastName": "Baggins", "_links": {...} }

El error se produce solo cuando no hay ningún objeto en la base de datos con el ID dado:

curl -i -x PUT -H "Content-Type:application/json" -d ''{"unknown": "PUT value", "firstName": "Gandalf", "lastName": "Baggins"}'' http://localhost:8080/people/100 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "unknown" (class Person), not marked as ignorable (2 known properties: "lastName", "firstName")

¿Es el comportamiento esperado o un error en Spring Data Rest? ¿Cómo puedo lanzar un error cuando JSON con propiedades desconocidas se pasa a la aplicación sin importar qué método de solicitud sea?

He reproducido este comportamiento modificando http://spring.io/guides/gs/accessing-data-rest/ , el único cambio que he hecho es Jackson2ObjectMapperBuilder , no hay otros controladores o repositorios en este proyecto.


Cuando crea una nueva entidad, convierte json directamente en un objeto de entidad java a través del proceso de deserialización en el que está involucrada la validación requerida. Pero cuando actualiza la entidad existente, convierte json a JsonNode y luego se fusiona con la entidad existente y, como se esperaba, no se realiza ninguna validación porque es una función para la deserialización de json al objeto java.

Como solución alternativa, también puede convertir JsonNode en objeto de entidad y funcionará como espera.

Hice un rápido ejemplo de cómo obtener la validación requerida.

vaya a https://github.com/valery-barysok/gs-accessing-data-rest

No es una solución clara pero puedes mejorarla :)

Este ejemplo anula la clase spring existente en classpath org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver

Nota Debe poner esta clase en classpath antes de la versión original.

readPutForUpdate y readPutForUpdate esta clase al proyecto y modifiqué el método readPutForUpdate :

private Object readPutForUpdate(IncomingRequest request, ObjectMapper mapper, Object existingObject, RootResourceInformation information) { try { JsonPatchHandler handler = new JsonPatchHandler(mapper, reader); JsonNode jsonNode = mapper.readTree(request.getBody()); // Here we have required validation mapper.treeToValue(jsonNode, information.getDomainType()); return handler.applyPut((ObjectNode) jsonNode, existingObject); } catch (Exception o_O) { throw new HttpMessageNotReadableException(String.format(ERROR_MESSAGE, existingObject.getClass()), o_O); } }

y utilicé el archivo application.properties para configurar DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES


Está utilizando Jackson2ObjectMapperBuilder , que tiene la propiedad DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES establecida en el valor disabled de forma predeterminada.


Puedes anotar tu modelo con:

@Entity @JsonIgnoreProperties(ignoreUnknown=false) public class Person{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; private String firstName; private String lastName; /* getters and setters */ }