Conception d'une API REST par URI vs chaîne de requête


73

Disons que j'ai trois ressources qui sont liées comme suit:

Grandparent (collection) -> Parent (collection) -> and Child (collection)

Ce qui précède décrit la relation entre ces ressources de la manière suivante: Chaque grand-parent peut s’associer à un ou plusieurs parents. Chaque parent peut mapper à un ou plusieurs enfants. Je souhaite pouvoir prendre en charge la recherche sur la ressource enfant, mais avec les critères de filtre:

Si mes clients me transmettent une référence d'identité à un grand-parent, je souhaite uniquement rechercher les enfants qui sont les descendants directs de ce grand-parent.

Si mes clients me transmettent une référence d'identifiant à un parent, je souhaite effectuer une recherche uniquement sur les enfants qui sont les descendants directs de mon parent.

J'ai pensé à quelque chose comme ça:

GET /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}

et

GET /myservice/api/v1/parents/{parentID}/children?search={text}

pour les exigences ci-dessus, respectivement.

Mais je pourrais aussi faire quelque chose comme ça:

GET /myservice/api/v1/children?search={text}&grandparentID={id}&parentID=${id}

Dans cette conception, je pouvais permettre à mon client de me transmettre l'un ou l'autre de la chaîne de requête: grandparentID ou parentID, mais pas les deux.

Mes questions sont:

1) Quelle conception d'API est plus RESTful, et pourquoi? Sémantiquement, ils veulent dire et se comportent de la même manière. La dernière ressource de l'URI est "enfants", ce qui implique que le client opère sur la ressource enfants.

2) Quels sont les avantages et les inconvénients pour chacun en termes de compréhensibilité du point de vue du client et de maintenabilité du point de vue du concepteur.

3) À quoi servent réellement les chaînes de requête, mis à part le "filtrage" de votre ressource? Si vous optez pour la première approche, le paramètre de filtre est intégré à l'URI lui-même en tant que paramètre de chemin d'accès au lieu d'un paramètre de chaîne de requête.

Merci!


3
Le titre de votre question devrait être extrêmement déroutant pour quiconque la visionnant. Les segments valides d'un URI sont définis comme suit: <modèle>: // <utilisateur>: <mot de passe> @ <hôte>: <port> / <chemin>; <paramètres>? <Requête> / # <fragment> (bien que < mot de passe> est obsolète) Une "chaîne de requête" est un composant valide d'un URI, ainsi votre "vs" dans le titre est un langage complètement fou.
K. Alan Bates

Tu veux dire I want to only search against children who are INdirect descendants of that grandparent.? Selon votre structure, le grand-parent n'a pas d'enfants directs.
null

Quelle est la différence entre un enfant et un parent? Un parent est-il un parent s’il n’a pas d’enfants?
Ça sent le

re: potential design flawet si vous avez des informations sur une personne, mais aucune information sur ses parents, sont-ils qualifiés de child? (par exemple, Adam et Eve) :)
Jesse Chisholm

Réponses:


64

Première

Selon RFC 3986 §3.4 (Uniform Resource Identifiers § (Syntaxe Composants) | Recherche

3.4 Requête

Le composant de requête contient des données non hiérarchiques qui, avec les données du composant de chemin d'accès (Section 3.3), servent à identifier une ressource dans la portée du schéma et de l'autorité de dénomination de l'URI (le cas échéant).

Les composants de requête permettent de récupérer des données non hiérarchiques. il y a peu de choses de nature plus hiérarchique qu'un arbre généalogique! Ainsi , que vous pensiez que c'est "REST-y" ou non , pour vous conformer aux formats, protocoles et framework de et pour le développement de systèmes sur Internet, vous ne devez pas utiliser la chaîne de requête pour identifier cette information.

REST n'a rien à voir avec cette définition.

Avant de répondre à vos questions spécifiques, votre paramètre de requête "recherche" est mal nommé. Il serait préférable de traiter votre segment de requête comme un dictionnaire de paires clé-valeur.

Votre chaîne de requête pourrait être définie de manière plus appropriée comme

?first_name={firstName}&last_name={lastName}&birth_date={birthDate} etc.

Pour répondre à vos questions spécifiques

1) Quelle conception d'API est plus RESTful, et pourquoi? Sémantiquement, ils veulent dire et se comportent de la même manière. La dernière ressource de l'URI est "enfants", ce qui implique que le client opère sur la ressource enfants.

Je ne pense pas que cela soit aussi clair que vous semblez le croire.

Aucune de ces interfaces de ressources n'est RESTful. La principale condition préalable au style architectural RESTful est que les transitions d'état de l'application doivent être communiquées à partir du serveur en tant qu'hypermédia. Les gens ont travaillé sur la structure des URI pour les transformer en quelque sorte "d'URI RESTful", mais la littérature formelle concernant REST a en réalité très peu à dire à ce sujet. Mon opinion personnelle est qu'une grande partie de la méta-désinformation sur REST a été publiée dans le but de briser les vieilles et mauvaises habitudes. (Construire un système véritablement "RESTful" est en fait un peu de travail. L'industrie s'est foutue de "REST" et a répondu à certaines préoccupations orthogonales avec des qualifications et des restrictions absurdes.)

Selon la documentation REST, si vous souhaitez utiliser HTTP comme protocole d'application, vous devez respecter les exigences formelles des spécifications du protocole et vous ne pouvez pas "configurer http en même temps et déclarer que vous utilisez http". ; Si vous allez utiliser des URI pour identifier vos ressources, vous devez vous conformer aux exigences formelles des spécifications relatives aux URI / URL.

Votre question est directement adressée par RFC3986 §3.4, que j'ai liée ci-dessus. En fin de compte, même si un URI conforme est insuffisant pour considérer une API comme "RESTful", si vous voulez que votre système soit réellement "RESTful" et que vous utilisez HTTP et des URI, vous ne pouvez pas identifier les données hiérarchiques par le biais du serveur. chaîne de requête parce que:

3.4 Requête

Le composant de requête contient des données non hiérarchiques.

...c'est aussi simple que ça.

2) Quels sont les avantages et les inconvénients pour chacun en termes de compréhensibilité du point de vue du client et de maintenabilité du point de vue du concepteur.

Les "avantages" des deux premiers sont qu’ils sont sur la bonne voie . Le "contre" du troisième est qu'il semble être complètement faux.

En ce qui concerne vos problèmes de compréhensibilité et de maintenabilité, ceux-ci sont définitivement subjectifs et dépendent du niveau de compréhension du développeur client et des avantages du concepteur. La spécification d'URI est la réponse définitive quant à la manière dont les URI sont censés être formatés. Les données hiérarchiques sont supposées être représentées sur le chemin et avec des paramètres de chemin. Les données non hiérarchiques sont supposées être représentées dans la requête. Le fragment est plus compliqué, car sa sémantique dépend spécifiquement du type de média de la représentation demandée. Donc, pour aborder le composant "compréhensibilité" de votre question, je vais essayer de traduire exactement ce que vos deux premiers URI disent réellement. Ensuite, je tenterai de représenter ce que vous dites que vous essayez d'accomplir avec des adresses URI valides.

Traduction de vos adresses URI textuelles en leur sens sémantique /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text} Ceci dit pour les parents des grands-parents, trouver leur enfant ayant search={text} ce que vous avez dit avec votre adresse URI n'est cohérent que si vous recherchez les frères et sœurs d'un grand-parent. Avec vos «grands-parents, parents, enfants», vous avez trouvé un «grand-parent», remontant d’une génération à l’autre de ses parents, puis redescendant dans la génération des «grands-parents» en examinant les enfants des parents.

/myservice/api/v1/parents/{parentID}/children?search={text} Cela indique que pour le parent identifié par {parentID}, trouvez son enfant. ?search={text}Ceci est plus proche de ce que vous voulez et correspond à une relation parent-> enfant qui peut probablement être utilisée pour modéliser l'intégralité de votre API. Pour le modéliser de cette façon, il incombe au client de reconnaître que s’ils ont un "grandparentId", il existe une couche indirectionnelle entre l’ID qu’ils ont et la partie du graphique de la famille qu’ils souhaitent voir. Pour rechercher un "enfant" par "grandparentId", vous pouvez appeler votre /parents/{parentID}/childrenservice, puis pour chaque enfant renvoyé, recherchez son identifiant de personne auprès de ses enfants.

Implémentation de vos exigences en tant qu'URI Si vous souhaitez modéliser un identificateur de ressource plus extensible qui puisse parcourir l'arborescence, je peux penser à différentes façons de le faire.

1) Le premier, auquel j'ai déjà fait allusion. Représente le graphique de "People" sous forme de structure composite. Chaque personne a une référence à la génération située au-dessus d'elle via son chemin de parent et à une génération inférieure à son chemin d'enfants.

/Persons/Joe/Parents/Mother/Parents serait un moyen d'attraper les grands-parents maternels de Joe.

/Persons/Joe/Parents/Parents serait un moyen d'attraper tous les grands-parents de Joe.

/Persons/Joe/Parents/Parents?id={Joe.GrandparentID} prendrait le grand-parent de Joe ayant l'identifiant que vous avez en main.

et tout cela aurait du sens (notez qu'il pourrait y avoir une baisse de performance ici en fonction de la tâche en forçant un DFS sur le serveur en raison d'un manque d'identification de branche dans le modèle "Parents / Parents / Parents".) Vous bénéficiez également d'avoir la capacité de supporter un nombre quelconque de générations. Si, pour une raison quelconque, vous souhaitez rechercher 8 générations, vous pouvez le représenter par

/Persons/Joe/Parents/Parents/Parents/Parents/Parents/Parents/Parents/Parents?id={Joe.NotableAncestor}

mais ceci mène à la deuxième option dominante pour représenter ces données: via un paramètre de chemin.


2) Utiliser les paramètres de chemin pour "interroger la hiérarchie" Vous pouvez développer la structure suivante pour alléger le fardeau des utilisateurs tout en conservant une API cohérente.

Pour regarder en arrière 147 générations, représentant cet identifiant de ressource avec des paramètres de chemin vous permet de faire

/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor}

Pour localiser Joe à partir de son arrière-grand-parent, vous pouvez consulter le graphique d'un nombre connu de générations pour Joe's Id. /Persons/JoesGreatGrandparent/Children;generations=3?id={Joe.Id}

Le point important à noter avec ces approches est que sans informations supplémentaires dans l'identifiant et la demande, vous devez vous attendre à ce que le premier URI récupère une personne 147 générations à partir de Joe avec l'identifiant de Joe.NotableAncestor. Vous devez vous attendre à ce que le second récupère Joe. Supposons que vous souhaitiez réellement que votre client appelant puisse récupérer l'ensemble des nœuds et leurs relations entre la personne racine et le contexte final de votre URI. Vous pouvez le faire avec le même URI (avec quelques décorations supplémentaires) et en définissant une acceptation de text/vnd.graphvizsur votre demande, qui est le type de média enregistré par l'IANA pour la .dotreprésentation graphique. Avec cela, changez l’URI en

/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor.Id}#directed

avec un en-tête de demande HTTP Accept: text/vnd.graphviz , vous pouvez faire en sorte que les clients indiquent assez clairement qu'ils veulent le graphe dirigé de la hiérarchie générationnelle entre Joe et 147 générations antérieures, où cette 147e génération ancestrale contient une personne identifiée comme étant "l'Ancêtre notable" de Joe.

Je ne sais pas si text / vnd.graphviz a une sémantique prédéfinie pour son fragment, je ne pourrais pas en trouver dans une recherche d'instruction. Si ce type de média a effectivement des informations de fragment prédéfinies, il convient de suivre sa sémantique pour créer un URI conforme. Toutefois, si ces sémantiques ne sont pas prédéfinies, la spécification d'URI indique que la sémantique de l'identificateur de fragment n'est pas contrainte, mais définie par le serveur, ce qui rend l'utilisation valide.


3) À quoi servent réellement les chaînes de requête, mis à part le "filtrage" de votre ressource? Si vous optez pour la première approche, le paramètre de filtre est intégré à l'URI lui-même en tant que paramètre de chemin d'accès au lieu d'un paramètre de chaîne de requête.

Je pense avoir déjà battu cet homme à mort, mais les chaînes de requête ne servent pas à "filtrer" les ressources. Ils servent à identifier votre ressource à partir de données non hiérarchiques. Si vous avez détaillé votre hiérarchie avec votre chemin en allant /person/{id}/children/et que vous souhaitez identifier un enfant spécifique ou un ensemble spécifique d'enfants, utilisez un attribut qui s'applique à l'ensemble que vous identifiez et incluez-le dans la requête.


1
La RFC ne s'intéresse à la hiérarchie que dans la mesure où elle définit une syntaxe et un algorithme permettant de résoudre les références d'URI relatives. Pourriez-vous élaborer ou citer des sources expliquant pourquoi les exemples de l'article original ne sont pas conformes?
user2313838

2
Un arbre familial n’est-il pas vraiment un graphe, pas un arbre, et pas du tout hiérarchique. Envisager plusieurs parents, divorce et re-mariage, etc.
Myster

1
@RobertoAloi Il me semble contre-intuitif de communiquer votre propre interface "Aucun élément trouvé" via un ensemble vide lorsque HTTP en a déjà défini la définition. Le principe de base est que vous demandez au serveur de renvoyer "chose (s)" et s'il n'y a pas de "chose (s)" à renvoyer, le serveur le communique avec "404 - Introuvable". Qu'est-ce qui est contre-intuitif à ce sujet?
K. Alan Bates

1
J'ai toujours cru 404 indiquer que l'URL racine de la ressource était introuvable, c'est-à-dire la collection dans son ensemble. Donc, si vous avez interrogé / books? Author = Jim et qu'il n'y avait pas de livres de Jim, vous recevrez un jeu vide []. Mais si vous avez demandé / articles? Author = Jim mais la ressource articles n'existait même pas, car une collection 404 aiderait à indiquer qu'il n'y a aucune utilité à rechercher des articles.
adjenks

1
@adjenks Vous n'avez pas appris cela des spécifications formelles. Structurellement, les composants d'une URL peuvent être considérés comme contenant une "racine" si cela vous permet de raisonner sur les objectifs des composants, mais la chaîne de requête n'est finalement pas un filtre d'affichage sur une ressource identifiée via le chemin. La chaîne de requête est un citoyen de première classe d'une URL. Lorsque vous ne trouvez aucune ressource sur le serveur qui corresponde à votre URL (y compris la chaîne de requête), le moyen défini dans http pour communiquer cela est 404. Le modèle d'interaction que vous posez introduit une distinction sans différence.
K. Alan Bates

14

C'est là que vous vous trompez:

Si mes clients me transmettent une référence d'identifiant

Dans un système REST, le client ne doit jamais être dérangé par les ID. Les seuls identifiants de ressources que le client devrait connaître devraient être les URI. C'est le principe de "l'interface uniforme".

Pensez à la manière dont les clients interagiraient avec votre système. Supposons que l'utilisateur commence à parcourir une liste de grands-parents, il a choisi l'un des enfants de ses grands-parents, ce qui l'amène à /grandparent/123. Si le client doit pouvoir rechercher les enfants de /grandparent/123, alors, selon "HATEOAS", tout ce qui sera retourné lorsque vous ferez une requête sur /grandparent/123devrait renvoyer une URL à l'interface de recherche. Cette URL devrait déjà contenir toutes les données nécessaires pour être filtrées par le grand-parent actuel intégré.

Que le lien ressemble à /grandparent/123?search={term}ou /parent?grandparent=123&search={term}ou /parent?grandparentTerm=someterm&someothergplocator=blah&search={term}est sans importance selon REST. Notez que toutes ces URL ont le même nombre de paramètres {term}, même si elles utilisent des critères différents. Vous pouvez basculer entre l’une ou l’autre de ces URL ou les mélanger en fonction des grands-parents spécifiques, sans que le client ne s’éclate, car la relation logique entre les ressources est la même, même si l’implémentation sous-jacente peut être très différente.

Si vous aviez créé le service de telle sorte qu'il soit nécessaire /grandparent/{grandparentID}?search={term}d'aller dans un sens mais plutôt dans l' /children?parent={parentID}&search={term}autre sens, le couplage est trop important car le client doit savoir interpoler différentes choses sur différentes relations conceptuellement similaires.

Que vous y alliez /grandparent/123?search={term}ou que ce /parent?grandparent=123&search={term}soit une question de goût et que la mise en œuvre soit plus facile pour vous en ce moment. L'important est de ne pas exiger que le client soit modifié si vous modifiez votre stratégie d'URL ou si vous utilisez différentes stratégies sur différentes relations parents-enfants.


10

Je ne sais pas pourquoi les gens pensent que l'insertion des valeurs d'ID dans l'URL signifie en quelque sorte que c'est une API REST. REST concerne la gestion des verbes, le transfert de ressources.

Donc, si vous voulez PUT un nouvel utilisateur, vous devez envoyer une bonne quantité de données et une requête HTTP POST est idéale, donc bien que vous puissiez envoyer la clé (par exemple, l'ID utilisateur), vous enverrez les données utilisateur. (par exemple nom, adresse) en tant que données POST.

Il est maintenant courant de placer l'identifiant de la ressource dans l'URI, mais il s'agit d'une convention qui dépasse toutes les formes de forme canonique "ce n'est pas REST si ce n'est pas dans l'URI". Rappelez-vous que la thèse originale de REST ne mentionne pas du tout http, il s'agit d'un idiome de traitement de données dans un client-serveur, pas quelque chose qui est une extension de http (bien que, évidemment, http soit notre principale forme d'implémentation de REST) .

Par exemple, Fielding utilise la demande d'un article académique à titre d'exemple. Vous souhaitez récupérer la ressource "Document du Dr John sur la consommation de bière", mais vous pouvez également souhaiter la version initiale, ou la version la plus récente, de sorte que l'identifiant de la ressource ne soit peut-être pas quelque chose de facilement référencé comme identifiant unique pouvant être placé dans l'URI. REST permet cela et une intention déclarée est:

REST s’appuie plutôt sur le choix de l’identificateur de ressource par l’auteur qui correspond le mieux à la nature du concept identifié

Donc, rien ne vous empêche d'utiliser un URI statique pour récupérer vos parents, en leur transmettant un terme de recherche dans la chaîne de requête pour identifier l'utilisateur exact que vous recherchez. Dans ce cas, la "ressource" que vous identifiez est l'ensemble des grands-parents (et l'URI contient donc des "grands-parents" dans le cadre de l'URI. REST inclut le concept de "données de contrôle" conçu pour déterminer quelle représentation de votre la ressource doit être récupérée - l'exemple donné dans ceux-ci est le contrôle de cache, mais aussi le contrôle de version - afin que ma demande pour l'excellent article du Dr John puisse être affinée en transmettant la version en tant que données de contrôle, qui ne font pas partie de l'URI.

Je pense qu'un exemple d'interface REST qui n'est généralement pas mentionné est SMTP . Lors de la construction d'un message électronique, vous envoyez des verbes (FROM, TO, etc.) avec les données de ressource pour chaque partie du message. C'est RESTful même s'il n'utilise pas les verbes http, il utilise son propre ensemble.

Donc ... bien que vous ayez besoin d'une identification de ressource dans votre URI, il n'est pas nécessaire que ce soit votre référence d'identifiant. Cela peut heureusement être envoyé en tant que données de contrôle, dans la chaîne de requête ou même dans les données POST. Ce que vous identifiez réellement dans votre API REST, c'est que vous recherchez un enfant que vous avez déjà dans votre URI.

Donc, à mon sens, en lisant la définition de REST, vous demandez une ressource enfant en lui transmettant des données de contrôle (sous forme de chaîne de requête) pour déterminer celle que vous souhaitez restituer. Par conséquent, vous ne pouvez pas faire de demande d'URI pour un grand-parent ou un parent. Vous voulez que les enfants soient renvoyés, alors le terme parent / grand-parent ou identifiant ne devrait absolument pas figurer dans un tel URI. Les enfants devraient être.


En fait, l’URI en tant que concept est au cœur de REST. Ce qui n’est pas aussi important, c’est la syntaxe URI. Une interface REST appropriée doit identifier les ressources avec une syntaxe commune. Selon le principe de base REST, il n’est pas nécessairement mauvais d’identifier une ressource complexe avec un objet JSON au lieu de l’URI RFC 3986; toutefois, si vous le faites, vous devez utiliser JSON RI pour l’ensemble de votre API.
Lie Ryan

4

Beaucoup de gens ont déjà parlé de ce que REST signifie, etc. Mais aucun ne semble répondre au vrai problème: votre conception.

Pourquoi les grands-parents sont-ils différents d'un père? ils ont tous deux des enfants qui peuvent éventuellement avoir des enfants qui peuvent ...

Finalement, ils sont tous "humains". Vous l'avez probablement aussi dans votre code. Alors utilisez ça:

GET /<api-endpoint>/humans/<ID>

Reviendra quelques informations utiles sur l'homme. (Nom, et des trucs)

GET /<api-endpoint>/humans/<ID>/children

retournera évidemment un tableau d'enfants. Si aucun enfant n'existe, un tableau vide est probablement le chemin à parcourir.

Pour simplifier les choses, vous pouvez par exemple ajouter le drapeau "hasChildren". ou 'aGrandChildren'.

Pensez plus intelligemment, pas plus fort


1

Ce qui suit est plus RESTfull parce que chaque grandparentID obtient sa propre URL. De cette façon, la ressource est identifiée de manière unique.

GET /myservice/api/v1/grandparents/{grandparentID}
GET /myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}

La recherche de paramètre de requête est un bon moyen d'exécuter une recherche à partir du contexte de cette ressource.

Quand une famille devient très grande, vous pouvez utiliser start / limit comme options de requête, par exemple:

GET /myservice/api/v1/grandparents/{grandparentID}/children?start=1&limit=50

En tant que développeur, il est bon d’avoir différentes ressources avec une URL / URI unique. Je pense que vous devriez utiliser le paramètre de requête uniquement quand ils peuvent également être laissés de côté.

Peut-être que c'est une bonne lecture http://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling et sinon la thèse de doctorat originale de Roy T Fielding https://www.ics.uci.edu /~fielding/pubs/dissertation/fielding_dissertation.pdf qui explique très bien et complètement les concepts.


1

Je vais avec

/myservice/api/v1/people/{personID}/descendants/2?search={text}

Dans ce cas, chaque préfixe est une ressource valide:

  • /myservice/api/v1/people: tout le monde
  • /myservice/api/v1/people/{personID}: une personne avec des informations complètes (ancêtres, frères et sœurs, descendants)
  • /myservice/api/v1/people/{personID}/descendants: les descendants d'une personne
  • /myservice/api/v1/people/{personID}/descendants/2: petits-enfants d'une personne

Peut-être pas la meilleure réponse, mais au moins cela a du sens pour moi.


-1

de nombreux témoins avant moi ont déjà fait remarquer que le format d'URL est sans importance dans le contexte du service RESTful ... mes deux cents seraient ... un aspect important de REST tel que préconisé dans la rédaction initiale était le concept de "ressources" .. . Lorsque vous concevez votre URL, gardez à l'esprit qu'un aspect de la 'ressource' n'est pas une ligne dans un seul tableau (même s'il pourrait l'être). C'est plutôt une représentation ... qui est également cohérente .. .et peut être utilisé pour modifier l’état de cette représentation (et efficacement pour le support de stockage sous-jacent).

Dans votre exemple / myservice / api / v1 / grands-parents / {grandparentID} / parents / enfants? Search = {text}

Cela pourrait être une ressource plus utile si vous raccourcissez ceci / myservice / api / v1 / siblingsOfGrandParents? Search = ..

dans ce cas, vous pouvez déclarer 'siblingsOfGrandParents' en tant que ressource .. ceci simplifie l'URL

Comme d'autres l'ont fait remarquer, il existe une idée répandue et erronée selon laquelle il faut s'adapter à chaque type de relation hiérarchique entre les objets de domaine sous une forme plus explicite dans une URL. ressources et représentent toutes leurs relations ... la question devrait plutôt être je crois ... est-il nécessaire d'exposer une telle relation via des URL ... spécifiquement des segments de chemin ... peut-être pas.


lorsque vous passez votre temps précieux à rétrograder une réponse, il serait utile que vous puissiez prendre la parole et donner vos raisons.
Senthu Sivasambu

Je ne suis pas tout à fait sûr de savoir pourquoi votre vote a été basé sur votre réponse. Je suis d'accord avec toi. Un élément immédiat qui me saute aux yeux est que vous avez donné un exemple légèrement tangent en mentionnant "frères et sœurs" lorsque le PO posait des questions sur les relations hiérarchiques. Peut-être aussi que votre style d'écriture pourrait être amélioré. Vous utilisez beaucoup de "...", nous avons tendance à ne pas utiliser "..." beaucoup dans l'écriture formelle, professionnelle ou pédagogique. Il y a aussi un peu de redondance (par exemple, vous mentionnez par exemple, puis dites dans votre exemple). Peut-être que votre réponse pourrait être plus concise et aborder plus directement la question des PO.
KennyCason
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.