복붙노트

[SPRING] 알 수없는 속성에서 PUT 및 POST가 실패 함 Spring 다른 동작

SPRING

알 수없는 속성에서 PUT 및 POST가 실패 함 Spring 다른 동작

Spring Data Rest 저장소를 사용하여 Spring Boot 응용 프로그램을 작성 중이며 요청 본문에 알 수없는 속성이있는 JSON이 있으면 자원에 대한 액세스를 거부하고 싶습니다. 단순화 된 엔티티 및 저장소의 정의 :

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

잭슨의 직렬화 해제 기능을 사용하여 JSON에서 알 수없는 속성을 허용하지 않습니다.

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

POST 요청을 보내면 모든 것이 예상대로 작동합니다. 올바른 필드를 사용할 때 올바른 응답을 얻습니다.

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

그리고 알 수없는 필드가있는 JSON을 보낼 때 응용 프로그램에서 예상되는 오류가 발생합니다.

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

유효한 JSON을 사용할 때 PUT 메서드가 올바른 응답을 반환합니다. 그러나 알 수없는 필드와 PUT 요청을 보낼 때 나는 Spring이 에러를 던지기를 기대하지만 대신 Spring은 데이터베이스에있는 객체를 업데이트하고 그것을 반환합니다 :

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

주어진 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")

예상되는 동작입니까? 아니면 Spring 데이터 나머지의 버그입니까? 알 수없는 속성을 가진 JSON이 요청 메소드에 상관없이 애플리케이션에 전달되면 오류를 던질 수 있습니까?

http://spring.io/guides/gs/accessing-data-rest/를 수정하여이 동작을 재현했습니다. Jackson2ObjectMapperBuilder 만 변경 했으므로이 프로젝트에는 다른 컨트롤러 나 저장소가 없습니다.

해결법

  1. ==============================

    1.나는 당신이 관찰 한 행동이 의도적으로 생각합니다. POST가 실행되면 리소스를 생성하므로 JSON이 엔티티 유형으로 deserialised되고 Jackson이이 작업을 수행합니다.

    나는 당신이 관찰 한 행동이 의도적으로 생각합니다. POST가 실행되면 리소스를 생성하므로 JSON이 엔티티 유형으로 deserialised되고 Jackson이이 작업을 수행합니다.

    PUT은 봄 데이터 휴식에서 다르게 작동합니다. 흥미로운 부분은 PersistentEntityResourceHandlerMethodArgumentResolver.readPutForUpdate에서 처리됩니다.

    json은 JsonNode로 읽혀지고 엔티티는 데이터 저장소에서 읽히고 DomainObjectReader.doMerge에서 json 필드를 통해 구현이 반복됩니다. 엔티티에 json을 적용하고 나중에 제어기 구현에 저장합니다. 또한 영속 엔티티에 존재하지 않는 모든 필드를 버립니다.

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

    이것은 코드를 읽는 것으로부터의 나의 이해입니다. 나는 이것이 당신이 벌레라고 주장 할 수 있다고 생각합니다. spring data rest`s jira (https://jira.spring.io/browse/DATAREST)에서이를보고하려고 할 수 있습니다. 내가 아는 한이 동작을 사용자 정의 할 수있는 방법이 없습니다.

  2. ==============================

    2.새로운 엔티티를 생성 할 때 json을 자바 엔티티 객체로 변환하는 데 필요한 검증이 필요한 역 직렬화 프로세스가 수행됩니다. 하지만 기존 엔티티를 업데이트하면 json을 JsonNode로 변환 한 다음 기존 엔티티와 병합하고 예상대로 Java 객체에 대한 json 비 직렬화의 기능이기 때문에 유효성 검사가 수행되지 않습니다.

    새로운 엔티티를 생성 할 때 json을 자바 엔티티 객체로 변환하는 데 필요한 검증이 필요한 역 직렬화 프로세스가 수행됩니다. 하지만 기존 엔티티를 업데이트하면 json을 JsonNode로 변환 한 다음 기존 엔티티와 병합하고 예상대로 Java 객체에 대한 json 비 직렬화의 기능이기 때문에 유효성 검사가 수행되지 않습니다.

    대안으로 JsonNode를 엔티티 객체로 추가 변환 할 수 있으며 예상대로 작동합니다.

    필요한 검증을 얻는 방법에 대한 빠른 예제를 작성했습니다.

    https://github.com/valery-barysok/gs-accessing-data-rest로 이동하십시오.

    그것은 명확한 해결책이 아니지만 당신은 그것을 향상시킬 수 있습니다 :)

    이 예제는 org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver 클래스 패스의 기존 스프링 클래스를 오버라이드한다.

    참고이 클래스는 원본 버전보다 먼저 클래스 경로에 배치해야합니다.

    이 클래스를 프로젝트에 복사하여 붙여넣고 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);
        }
    }
    

    나는 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES를 구성하기 위해 application.properties 파일을 사용했습니다.

  3. ==============================

    3.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 속성이 기본적으로 비활성화 된 값으로 설정된 Jackson2ObjectMapperBuilder를 사용하고 있습니다.

    DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES 속성이 기본적으로 비활성화 된 값으로 설정된 Jackson2ObjectMapperBuilder를 사용하고 있습니다.

  4. ==============================

    4.다음과 같이 모델에 주석을 달 수 있습니다.

    다음과 같이 모델에 주석을 달 수 있습니다.

    @Entity
    @JsonIgnoreProperties(ignoreUnknown=false)
    public class Person{
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private long id;
    
        private String firstName;
        private String lastName;
    
        /* getters and setters */
    }
    
  5. from https://stackoverflow.com/questions/34545997/put-and-post-fail-on-unknown-properties-spring-different-behavior by cc-by-sa and MIT license