2
votes

Configurez FAIL_ON_UNKNOWN_PROPERTIES pour chaque RequestMapping différemment dans le contrôleur

Je veux gérer la conversion json en objet différemment sur différents @RequestMapping dans mon contrôleur.

Je crois que si nous ajoutons la dépendance Jackson dans notre projet spring-boot, il gère la conversion json en objet et la propriété # spring.jackson.deserialization.fail-on-unknown-properties = true s'assurera cette conversion échouera s'il y a une propriété inconnue présente dans le json (veuillez me corriger si je me trompe).

Pouvons-nous dire à jackson localement quand échouer sur des propriétés inconnues et quand les ignorer propriété.

Voici un extrait de code pour utiliser un indicateur.

        @PostMapping(value = "fail/fast")
        public @ResponseBody UserDTO test(@FAIL_ON_UNKNOWN @RequestBody UserDTO userDTO, @RequestParam boolean failFast) {
            ..///processing...
            return userDTO;
        }

        @PostMapping(value = "fail/safe")
        public @ResponseBody UserDTO test( @RequestBody UserDTO userDTO, @RequestParam boolean failFast) {
                ..///processing...
                return userDTO;
        }

Je n'ai pas besoin qu'il soit géré à l'exécution comme i je fais en utilisant @RequestParam. Existe-t-il une propriété que je peux utiliser pour marquer les mappages où vérifier les propriétés inconnues et où les ignorer.

Modifier : Ce que je recherche, c'est de modifier une application existante pour gérer la propriété inconnue par mappage. Par exemple:

    @GetMapping(value = "sample")
    public @ResponseBody UserDTO test(@RequestParam String str, @RequestParam boolean failFast) {
        ObjectMapper map = new ObjectMapper();
        if( failFast) {
            map.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, true);
        } else {
            map.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        }
        UserDTO userDTO = null;
        try {
            userDTO = map.readValue(str, UserDTO.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return userDTO;
    }

Si un roi de la validation peut être ajouté par mappage, je n'ai pas besoin de changer tous les mappages existants pour personnaliser la propriété inconnue et le changement de code sera minimal.


3 commentaires

Vous pouvez différencier par DTO en spécifiant au niveau de la classe ce qu'il faut faire avec les propriétés.


@Deinum mais alors ce sera corrigé pour cette classe. Supposons que dans certains cas, il est correct d'avoir une propriété inconnue dans le DTO, mais dans d'autres cas, ce n'est pas correct. comment nous traiterons ces cas si nous spécifions au niveau de la classe.


Utilisez différents DTO. Donc, à moins que vous ne souhaitiez créer manuellement toutes les instances ObjectMapper vous-même et faire le marshaling vous-même, votre option est d'utiliser différents DTO.


3 Réponses :


1
votes

L ' ObjectMapper de

Jackson vous permet de créer un nouvel ObjectReader avec une configuration personnalisée. Vous pouvez créer une instance ObjectMapper commune dans votre application et, pour certains contrôleurs, l'utiliser comme objet de base pour créer des lecteurs personnalisés. Il vous permettra d'utiliser toutes les fonctionnalités communes et les modules enregistrés et d'en changer quelques-uns si nécessaire. Voir contrôleur ci-dessous: La classe

curl -i -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{"id":"some-value"}' http://localhost:8080/options/fail

Payload n'a qu'une seule propriété - id . Dans un contrôleur, nous utilisons ObjectReader avec DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES activé. Dans d'autres, nous utilisons ObjectReader avec la configuration par défaut avec DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES désactivé.

Pour une demande de test:

curl -i -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -d '{"id":"some-value","id1":1}' http://localhost:8080/options/fail


2 commentaires

Merci d'avoir répondu. Je veux faire quelque chose de similaire mais pour mon application existante. Ici, nous travaillons sur HttpRequest lui-même et convertissons nous-mêmes. Mais tous mes mappages de contrôleurs reçoivent DTO après avoir été convertis de json par jackson. J'ai besoin d'indiquer jackson avant de convertir json en DTO, quand vérifier la propriété inconnue. Est-il possible du tout de configurer une défaillance de propriété inconnue par mappage au moment de la conversion json vers DTO.


@MohdWaseem, cette nouvelle exigence change de perspective. Vous devez jeter un œil à la classe MappingJackson2HttpMessageConverter . Peut-être pourriez-vous l'étendre et configurer ObjectReader ici.



1
votes

J'ai pu obtenir le résultat souhaité en implémentant mon propre HttpMessageConverter . Merci à @MichalZiober de l'avoir suggéré.

J'ai créé un HttpMessageConvertor personnalisé et l'ai enregistré avec mon MediaType personnalisé: {"application", "json-failFast "} .

Cela fonctionne chaque fois que Header: Content-Type: application / json-failFast est présent puis des propriétés inconnues dans @ RequestBody / @ ResponseBody ne sera pas accepté lors de la conversion de json en Object et UnrecognizedPropertyException sera lancé.

Et chaque fois que Header: Content-Type: application / json est présent, les propriétés non reconnues dans @RequestBody/ResponseBody seront ignoré.

Voici mon HttpMessageConverter retour> personnalisé :

@Component
public class CustomJsonMessageConverter extends AbstractJackson2HttpMessageConverter {

    @Nullable
    private String jsonPrefix;

    public CustomJsonMessageConverter() {
        this(Jackson2ObjectMapperBuilder.json().build().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,true));
    }
    public CustomJsonMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper, new MediaType[]{ new MediaType("application", "json-failFast")});
    }

    public void setJsonPrefix(String jsonPrefix) {
        this.jsonPrefix = jsonPrefix;
    }

    public void setPrefixJson(boolean prefixJson) {
        this.jsonPrefix = prefixJson ? ")]}', " : null;
    }

    protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
            if (this.jsonPrefix != null) {
            generator.writeRaw(this.jsonPrefix);
        }
    }
}


1 commentaires

Belle solution. Je n'améliorerais qu'un peu le nom du type de média. application / json-fastFail serait beaucoup plus descriptif.



1
votes
@Autowired
private RequestMappingHandlerAdapter converter;

@Override
public void afterPropertiesSet() throws Exception {
    configureJacksonToFailOnUnknownProperties();
}

private void configureJacksonToFailOnUnknownProperties() {
    MappingJackson2HttpMessageConverter httpMessageConverter = converter.getMessageConverters().stream()
            .filter(mc -> mc.getClass().equals(MappingJackson2HttpMessageConverter.class))
            .map(mc -> (MappingJackson2HttpMessageConverter)mc)
            .findFirst()
            .get();

    httpMessageConverter.getObjectMapper().enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}

0 commentaires