PUT and POST fail on unknown properties Spring different behavior

匿名 (未验证) 提交于 2019-12-03 02:23:02

问题:

I am writing Spring Boot application using Spring Data Rest repositories and I want to deny access to resource if request body contains JSON that has unknown properties. Definition of simplified entity and repository:

@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> {} 

I use Jackson's deserialization feature to disallow unknown properties in JSONs.

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

When I send POST requests everything works as expected. When I use valid fields I get correct response:

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

And when I send JSON with unknown fields application throws expected error:

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") 

PUT method when using valid JSON returns correct response as well. However when I send PUT request with unknown field I expect Spring to throw error but instead of that, Spring updates object in database and returns it:

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": {...} } 

The error is thrown only when there is no object in database with given id:

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") 

Is it expected behavior or a bug in Spring Data Rest? How can I throw an error when JSON with unknown properties is passed to application no matter what request method is?

I've reproduced this behavior by modifying http://spring.io/guides/gs/accessing-data-rest/ , the only change I've made is Jackson2ObjectMapperBuilder , no other controllers or repositories are in this project.

回答1:

I think the behaviour you are observing is by design. When a POST is issued you are creating the resource so the JSON is deserialised into your entity type and Jackson is performing this task.

A PUT is working differently in spring data rest. The interesting part is handled in PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate.

The json is read into a JsonNode, the entity is read from the data store and then in DomainObjectReader.doMerge the implementation iterates over the json fields. It applies the json to the entity and saves it later in the controller implementation. It also discards all the fields that do not exist in the persistent entity:

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

This is my understanding from reading the code. I think you could argue that this is a bug. You could try to report it at spring data rest`s jira - https://jira.spring.io/browse/DATAREST. As far as I know there is no way to customise this behaviour.



回答2:

When it creates new entity then it converts json directly to java entity object through deserialization process where required validation is involved. But when it updates existing entity then it converts json to JsonNode and then merge with existing entity and as expected no validation happens because it is feature for json deserialization to java object.

As workaround you can additionally convert JsonNode to entity object and it will work as you expect.

I did quick example how to gain required validation.

go to https://github.com/valery-barysok/gs-accessing-data-rest

It is not clear solution but you can improve it :)

This example override existing spring class on classpath org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver

Note You must put this class on classpath before original version.

I did copy-past this class to project and modified readPutForUpdate method:

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);     } } 

and i used application.properties file to configure DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES



回答3:

You're using Jackson2ObjectMapperBuilder, which has the property DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES set to the disabled value by default.



回答4:

you can annotate your model with :

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


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!