Api REST : comment s’assurer que les enums reçus sont bien ceux que l’on voulait nous transmettre
Sous ce titre un peu long se trouve une problématique que j’ai rencontré dernièrement en construisant une API REST. Je devais transmettre des objets avec un statut contenu dans une liste prédéfinie de valeurs mais l’utilisation d’une énumération me posait problème…
Pourquoi se poser cette question ?
Ma première réaction dans ce genre de situation est de créer une énumération contenant les différentes valeurs.
public enum StatusObject { Sleeping, Walking, Eating, Running, Coding }
Il est alors possible de l’utiliser comme type de n’importe quelle propriété d’un objet ou même directement. C’est ce que je fais dans en déclarant cette méthode d’un controller :
[HttpPost] [Route("Create")] public async Task<HttpResponseMessage> CreateNewRabbit(Rabbit toCreate) { var status = toCreate.StatusObject; /* .... */ }
Comme vous le savez peut-être, en C# les énumérations sont par défaut traduites en integer. L’état Walking a par exemple la valeur 1 dans notre exemple. La valeur numérique est directement déduite de l’ordre dans lequel sont définies les “valeurs”.
Aussi, lorsqu’un client de mon API souhaite me transmettre une valeur, il le fait via des chaines de caractères non typées. Il peut donc par exemple me transmettre un objet à créer sous cette forme :
{ StatusObject : 2 }
Cela va marcher parfaitement avec le code que l’on a écrit car l’énumération sera parsée automatiquement en la valeur “Eating”. Ce comportement que je ne remets pas en cause, me pose un problème. En effet, il suffit que du côté serveur pour une raison X ou Y, les valeurs numériques changent et le client communiquera les mauvaises valeurs sans le vouloir à l’API. Pour moi, ce n’est pas un cas rare car il existe beaucoup trop de façon de créer ce problème :
- Suppression d’une des valeurs : celles placées après auront toutes la valeur numérique inférieure,
- Modification de l’ordre des valeurs : les valeurs numériques seront mélangées,
- Ajout d’une valeur ailleurs qu’à la fin : rebelote, les valeurs numériques seront mélangées …
Comment éviter que cela se produise ?
Une solution à laquelle nous sommes parvenus avec Alexandre-Pierre et Adrien est de vérifier les valeurs que l’on reçoit. Il me semble même que c’est une bonne pratique du développement en général .
Dans notre cas, on va mettre en place 2 choses complémentaires :
- Ne pas utiliser le type de l’énumération directement dans l’objet que l’on reçoit mais une string. Cela aura son utilité dans le code mais cela force, au moins psychologiquement l’utilisateur de notre API à écrire la valeur de façon textuelle dans l’objet qu’il nous transmet. Par exemple “Eating” dans notre cas.
- Empêcher de nous transmettre un int : comme nous allons parser la valeur reçue pour la transformer en l’une enum, il est toujours possible (mais moins tentant ) de nous transmettre la valeur sous la forme d’un integer. Nous allons empêcher cela avec la ligne de code ci-dessous.
private static bool IsProvidedEnumStringValueValid<T>(string enumStringValue) where T : struct { T parsedPlatform; bool isPlatformParsed = Enum.TryParse(enumStringValue, out parsedPlatform); //This is not the enum you are looking for ? bool enumProvidedInvalid = !isPlatformParsed || enumStringValue != parsedPlatform.ToString(); return enumProvidedInvalid; }
Dans cette méthode, je vérifie que le string donné est bien transformable en une valeur de mon enum. Je vérifie que la valeur parsée transformée en string correspond à la valeur reçue pour empêcher les valeurs fournies sous forme d’int.
Il serait aussi bien sûr possible d’utiliser une liste de chaines de caractères acceptées mais je trouve cela plus propre et maintenable de passer par cette méthode.
Et voilà pour ma méthode mais je suis sûr que les gourous du Web trouveront une manière plus élégante de faire cela
Commentaires