web-dev-qa-db-fra.com

Meilleures pratiques pour le transfert de données entre les pages

Le problème

Dans la pile que nous réutilisons entre les projets, nous mettons un peu trop de données dans la session pour passer des données entre les pages. C'était bon en théorie car cela empêche toute falsification, rejouer les attaques, etc., mais cela crée autant de problèmes qu'il en résout.

La perte de session elle-même est un problème, bien que ce soit surtout géré par l'implémentation de Session State Server (ou en utilisant SQL Server). Plus important encore, il est difficile de faire fonctionner correctement le bouton de retour, et c'est également un travail supplémentaire pour créer une situation où un utilisateur peut, par exemple, ouvrir le même écran dans trois onglets pour travailler sur différents enregistrements.

Et ce n'est que la pointe de l'iceberg.

Il existe des solutions de contournement pour la plupart de ces problèmes, mais au fur et à mesure que je m'éloigne, tout ce frottement me donne le sentiment que le passage de données entre les pages en utilisant la session est la mauvaise direction.

Ce que je veux vraiment faire ici, c'est trouver une meilleure pratique que ma boutique peut utiliser tout le temps pour passer des données entre les pages, puis, pour les nouvelles applications, remplacer les éléments clés de notre pile qui reposent actuellement sur Session.

Il serait également agréable que la solution finale n'aboutisse pas à des montagnes de code de plomberie passe-partout.

Solutions proposées

Session

Comme mentionné ci-dessus, s'appuyer fortement sur Session semble comme une bonne idée, mais cela casse le bouton de retour et provoque d'autres problèmes.

Il peut y avoir des moyens de contourner tous les problèmes, mais cela semble être un travail supplémentaire.

Une chose très agréable à propos de l'utilisation de session est le fait que la falsification n'est tout simplement pas un problème. Comparé à tout passer via QueryString non crypté, vous finissez par écrire beaucoup moins de code de garde.

Publication sur plusieurs pages

En vérité, j'ai à peine envisagé cette option. J'ai un problème avec la façon dont il est étroitement couplé rend les pages - si je commence à faire PreviousPage.FindControl ("SomeTextBox"), cela semble être un problème de maintenance si jamais je veux accéder à cette page à partir d'une autre page qui n'a peut-être pas un contrôle appelé SomeTextBox.

Il semble également limité à d'autres égards. Peut-être que je veux accéder à la page via un lien, par exemple.

QueryString

Je penche actuellement vers cette stratégie, comme autrefois. Mais je veux probablement que ma QueryString soit cryptée pour la rendre plus difficile à falsifier, et je voudrais également gérer le problème des attaques par rejeu.

Sur 4 gars de Rolla, il y a un article à ce sujet .

Cependant, il devrait être possible de créer un HttpModule qui s'occupe de tout cela et supprime toute la fabrication de saucisses de chiffrement de la page. Effectivement, Mads Kristensen a un article où il en a publié un. Cependant, les commentaires donnent l'impression qu'il a des problèmes avec des scénarios extrêmement courants.

Autres options

Bien sûr, ce n'est pas un regard exaustif sur les options, mais plutôt les principales options que j'envisage. Ce lien contient une liste plus complète. Ceux que je n'ai pas mentionnés, tels que les cookies et le cache, ne sont pas appropriés pour transmettre des données entre les pages.

En clôture ...

Alors, comment gérez-vous le problème de la transmission de données entre les pages? Quels pièges cachés avez-vous dû contourner, et y a-t-il des outils préexistants qui les résolvent tous parfaitement? Do vous vous sentez comme vous avez une solution qui vous satisfait complètement?

Merci d'avance!

Mise à jour: Juste au cas où je ne serais pas assez clair, en "passant des données entre les pages" dont je parle, par exemple, en passant un CustomerID à partir d'une page CustomerSearch.aspx vers Customers.aspx, où le client sera ouvert et la modification peut se produire.

64
Brian MacKay

Plusieurs mois plus tard, j'ai pensé que je mettrais à jour cette question avec la technique avec laquelle j'ai fini par travailler, car cela a si bien fonctionné.

Après avoir joué avec une gestion des états de session plus complexe (ce qui a entraîné de nombreux boutons arrière cassés, etc.), j'ai fini par rouler mon propre code pour gérer les chaînes de requête chiffrées. Cela a été une énorme victoire - tous mes scénarios de problèmes (bouton de retour, plusieurs onglets ouverts en même temps, état de session perdu, etc.) sont résolus et la complexité est minime car l'utilisation est très familière.

Ce n'est toujours pas une solution miracle pour tout, mais je pense que c'est bon pour environ 90% des scénarios que vous rencontrez.

Détails

J'ai construit une classe appelée CorePage qui hérite de Page. Il a des méthodes appelées SecureRequest et SecureRedirect.

Vous pourriez donc appeler:

 SecureRedirect(String.Format("Orders.aspx?ClientID={0}&OrderID={1}, ClientID, OrderID)

CorePage analyse la requête QueryString et la chiffre dans une variable QueryString appelée CoreSecure. Ainsi, la demande réelle ressemble à ceci:

Orders.aspx? CoreSecure = 1IHXaPzUCYrdmWPkkkuThEes% 2fIs4l6grKaznFGAeDDI% 3d

S'il est disponible, l'ID utilisateur actuellement connecté est ajouté à la clé de chiffrement, donc les attaques par relecture ne sont pas autant un problème.

De là, vous pouvez appeler:

X = SecureRequest("ClientID")

Conclusion

Tout fonctionne de manière transparente, en utilisant une syntaxe familière.

Au cours des derniers mois, j'ai également adapté ce code pour qu'il fonctionne avec les cas Edge, tels que les liens hypertexte qui déclenchent un téléchargement - parfois, vous devez générer un lien hypertexte sur le client qui a un QueryString sécurisé. Ça marche vraiment bien.

Faites-moi savoir si vous souhaitez voir ce code et je le mettrai quelque part.

Une dernière pensée: c'est bizarre d'accepter ma propre réponse sur certains des messages très réfléchis que d'autres personnes ont publiés ici, mais cela semble vraiment être la réponse ultime à mon problème. Merci à tous ceux qui m'ont aidé à m'y rendre.

8
Brian MacKay

Tout d'abord, les problèmes que vous rencontrez concernent la gestion de l'état dans un environnement sans état. Les difficultés que vous rencontrez ne sont pas nouvelles et c'est probablement l'une des choses qui rend le développement Web plus difficile que le développement de Windows ou le développement d'un exécutable.

En ce qui concerne le développement Web, vous avez cinq choix, à ma connaissance, pour gérer l'état spécifique à l'utilisateur qui peuvent tous être utilisés en combinaison les uns avec les autres. Vous constaterez qu'aucune solution ne fonctionne pour tout. Au lieu de cela, vous devez déterminer quand utiliser chaque solution:

  • Chaîne de requête - Les chaînes de requête sont utiles pour passer des pointeurs vers des données (par exemple, des valeurs de clé primaire) ou des valeurs d'état. Les chaînes de requête en elles-mêmes ne doivent pas être considérées comme sécurisées même si elles sont cryptées en raison de la relecture. De plus, certains navigateurs ont une limite sur la longueur de l'url. Cependant, les chaînes de requête présentent certains avantages, telles qu'elles peuvent être mises en signet et envoyées par courrier électronique à des personnes et sont intrinsèquement apatrides si elles ne sont utilisées avec rien d'autre.

  • Cookies - Les cookies sont bons pour stocker de très petites quantités d'informations pour un utilisateur particulier. Le problème est que les cookies ont également une limitation de taille, après quoi ils tronquent simplement les données, vous devez donc être prudent lorsque vous placez des données personnalisées dans un cookie. De plus, les utilisateurs peuvent tuer les cookies ou arrêter leur utilisation (bien que cela empêcherait également l'utilisation de la session standard). À l'instar des chaînes de requête, les cookies sont meilleurs, OMI, pour les pointeurs vers les données que pour les données elles-mêmes, sauf si les données sont minuscules.

  • Données de formulaire - Les données de formulaire peuvent cependant prendre beaucoup d'informations au détriment des heures de publication et, dans certains cas, des temps de rechargement. ViewState d'ASP.NET utilise des variables de formulaire cachées pour conserver les informations. Passer des données entre des pages en utilisant quelque chose comme ViewState a l'avantage de fonctionner plus facilement avec le bouton de retour, mais peut facilement créer des pages gigantesques qui ralentissent l'expérience de l'utilisateur. En général, le modèle ASP.NET ne fonctionne pas sur la publication croisée (bien que cela soit possible), mais fonctionne à la place sur les publications de retour à la même page et à partir de là, en naviguant vers la page suivante.

  • Session - La session est utile pour les informations relatives à un processus avec lequel l'utilisateur progresse ou pour les paramètres généraux. Vous pouvez stocker un certain nombre d'informations dans la session au détriment de la mémoire du serveur ou des temps de chargement à partir des bases de données. Sur le plan conceptuel, la session fonctionne en chargeant la liasse de données entière pour l'utilisateur en une seule fois à partir de la mémoire ou d'un serveur d'état. Cela signifie que si vous avez un très grand ensemble de données, vous ne voudrez probablement pas le mettre en session. La session peut créer des problèmes de bouton de retour qui doivent être mis en balance avec ce que l'utilisateur essaie réellement d'accomplir. En général, vous constaterez que le bouton de retour peut être le fléau du développeur Web.

  • Base de données - La dernière solution (qui peut à nouveau être utilisée en combinaison avec d'autres) consiste à stocker les informations dans la base de données dans son schéma approprié avec une colonne qui indique l'état de l'élément. Par exemple, si vous gérez la création d'une commande, vous pouvez stocker la commande dans la table Order avec une colonne "état" qui détermine s'il s'agit d'une commande réelle ou non. Vous devez stocker l'identifiant de la commande dans la chaîne ou la session de requête. Le site Web continuerait d'écrire des données dans le tableau pour mettre à jour les différentes pièces et éléments enfants jusqu'à ce que l'utilisateur soit en mesure de déclarer que cela est fait et que l'état de la commande est marqué comme étant une vraie commande. Cela peut compliquer les rapports et les requêtes dans la mesure où ils doivent tous différencier les "vrais" éléments de ceux en cours.

L'un des éléments mentionnés dans votre lien ultérieur était Cache d'application. Je ne considérerais pas cela comme spécifique à l'utilisateur car il est à l'échelle de l'application. (Cela peut évidemment être adapté à l'utilisateur, mais je ne le recommanderais pas non plus). Je n'ai jamais joué avec le stockage de données dans HttpContext en dehors de leur transmission à un gestionnaire ou à un module, mais je serais sceptique quant au fait que cela soit différent des solutions mentionnées ci-dessus.

En général, il n'y a pas de solution unique pour les gouverner tous. La meilleure approche consiste à supposer sur chaque page que l'utilisateur aurait pu accéder à cette page depuis n'importe où (au lieu de supposer qu'il y est arrivé en utilisant un lien sur une autre page). Si vous faites cela, les problèmes de bouton de retour deviennent plus faciles à gérer (bien que cela soit toujours une douleur). Dans mon développement, j'utilise largement les quatre premiers et recourt parfois à la dernière solution lorsque le besoin l'exige.

41
Thomas

Très bien, donc je veux faire précéder ma réponse de ceci; Thomas a clairement la réponse la plus précise et la plus complète à ce jour pour les personnes commençant à zéro. Cette réponse n'est pas du tout dans la même veine. Ma réponse vient du point de vue d'un "développeur d'entreprise". Comme nous le savons tous trop bien; Parfois, il n'est tout simplement pas possible de dépenser de l'argent pour réécrire quelque chose qui existe déjà et qui "fonctionne" ... du moins pas tout d'un coup. Parfois, il est préférable de mettre en œuvre une solution qui vous permettra de migrer vers une meilleure alternative au fil du temps.

La seule chose que je dirais que Thomas manque, c'est; état javascript côté client. Là où je travaille, nous avons constaté que les clients s'attendent de plus en plus à des applications de type "Web 2.0". Nous avons également constaté que ces types d'applications entraînent généralement une satisfaction des utilisateurs beaucoup plus élevée. Avec un peu de pratique et l'aide de très bonnes bibliothèques javascript comme jQuery (nous avons même commencé à utiliser GWT et nous l'avons trouvé IMPRESSIONNANT) communiquant avec les services basés sur JSON REST services implémentés dans WCF Cette approche fournit également un très bon moyen de commencer à évoluer vers une architecture basée sur SOA et une séparation nette de l'interface utilisateur et de la logique métier.

Mais je m'égare.

Il me semble que vous disposez déjà d'une application et que vous avez déjà repoussé les limites de la gestion d'état de session intégrée d'ASP.NET. Alors ... voici ma suggestion (en supposant que vous avez déjà essayé la gestion de session hors processus d'ASP.NET, qui évolue considérablement mieux que la gestion de session in-process/on-box, et cela semble être le cas parce que vous avez mentionné il); NCache.

NCache vous fournit un remplacement "drop-in" pour les options de gestion de session d'ASP.NET. Il est très facile à mettre en œuvre et pourrait "panser" votre application plus que suffisamment bien pour vous permettre de passer au travers - sans aucun investissement important dans la refonte immédiate de votre base de code existante.

Vous pouvez utiliser le temps et l'argent supplémentaires pour commencer à réduire votre dette technique en concentrant le nouveau développement sur des choses ayant une valeur commerciale immédiate - en utilisant une nouvelle approche (telle que l'une des alternatives proposées dans les autres réponses, ou la mienne).

Juste mes pensées.

9
Steve

Après avoir parcouru tous les scénarios et réponses ci-dessus et ce lien Méthodes de passage de données Mon dernier conseil serait:

[~ # ~] cookies [~ # ~] pour:

  • ENCRYPT [userId's]
  • ENCRYPT [productId]
  • CHIFFREMENT [xyzIds ..]
  • CHIFFREMENT [etc.]

[~ # ~] base de données [~ # ~] pour:

  • jeux de données BY COOKIE ID
  • tables de données PAR ID COOKIE
  • tous les autres gros morceaux PAR COOKIE ID

Mon conseil dépend également des statistiques ci-dessous et des détails de ce lien Méthodes de passage de données :

enter image description here

5
sajanyamaha

Je ne ferais jamais ça. Je n'ai jamais eu de problème à stocker toutes les données de session dans la base de données, à les charger en fonction du cookie des utilisateurs. C'est une session en ce qui concerne tout, mais j'en garde le contrôle. N'abandonnez pas le contrôle de vos données de session à votre serveur Web ...

Avec un peu de travail, vous pouvez prendre en charge les sous-sessions et autoriser le multitâche dans différents onglets/fenêtres.

3
Fosco

Comme point de départ, je trouve qu'il est préférable d'utiliser les éléments de données critiques, tels qu'un identifiant client, dans la chaîne de requête pour le traitement. Vous pouvez facilement suivre/filtrer les mauvaises données provenant de ces éléments, et cela permet également une certaine intégration avec le courrier électronique ou d'autres sites/applications connexes.

Dans une application précédente, la seule façon de visualiser un employé ou un enregistrement de demande le concernant était de se connecter à l'application, de rechercher l'employé ou de rechercher des enregistrements récents pour trouver l'enregistrement en question. Cela est devenu problématique et a fait couler beaucoup de temps lorsque quelqu'un d'un service connexe devait faire une simple vue sur les enregistrements à des fins d'audit.

Dans la réécriture, j'ai rendu à la fois l'ID de l'employé et demandé des ID disponibles via une URL de base de "ViewEmployee.aspx? Id = XXX" et "ViewRequest.aspx? Id = XXX". L'application a été configurée pour A) filtrer les mauvais ID et B) authentifier et autoriser l'utilisateur avant de lui permettre d'accéder à ces pages. Cela a permis aux utilisateurs principalement de l'application d'envoyer des e-mails simples aux auditeurs avec une URL dans l'e-mail. Lorsqu'ils étaient pressés, ils étaient dans leur temps de traitement en masse, ils pouvaient simplement cliquer sur une liste d'URL et effectuer le traitement approprié.

D'autres données liées à la session, telles que les dates de modification et le maintien de "l'état" de l'interaction de l'utilisateur avec l'application deviennent un peu plus complexes, mais nous espérons que cela vous fournira un point de départ.

2
Dillie-O