Лучшие практики для передачи данных между страницами - PullRequest
64 голосов
/ 09 июля 2010

Проблема

В стеке, который мы повторно используем между проектами, мы помещаем в сеанс немного слишком много данных для передачи данных между страницами.Это было хорошо в теории, потому что он предотвращает взлом, повторные атаки и т. Д., Но создает столько проблем, сколько решает.

Потеря сеанса сама по себе является проблемой, хотя обрабатывается , в основном путем реализации Session State Server (или с помощью SQL Server).Что еще более важно, сложно заставить работать кнопку «Назад» правильно, и это также дополнительная работа, чтобы создать ситуацию, когда пользователь может, скажем, открыть один и тот же экран на трех вкладках для работы с разными записями.

И этотолько верхушка айсберга.

Для большинства из этих проблем есть обходные пути, но, как я понимаю, все эти трения дают мне ощущение, что передача данных между страницами с использованием сессии является неправильным направлением.

Что я действительно хочу сделать, так это придумать лучшую практику, которую мой магазин может использовать постоянно для передачи данных между страницами, а затем, для новых приложений, заменить ключевые части нашего стека, которые в настоящее время полагаются на Session.

Было бы также неплохо, если бы окончательное решение не привело к появлению множества кодексов трубопровода.

Предлагаемые решения

Сеанс

Как уже упоминалось выше, сильная опора на сессию выглядит какхорошая идея, но она ломает кнопку «назад» и вызывает некоторые другие проблемы.

Могут быть способы обойти все проблемы, но это кажется большой дополнительной работой.

Одна вещь, которая очень приятна в использовании сессии, это то, что вмешательство не является проблемой.По сравнению с передачей всего через незашифрованный QueryString, вы в конечном итоге пишете гораздо меньше защитного кода.

Кросс-страничка

По правде говоря, я почти не рассматривал этот вариант.У меня есть проблема с тем, насколько тесно он связывает страницы - если я начну делать PreviousPage.FindControl ("SomeTextBox"), это выглядит как проблема с обслуживанием, если я когда-нибудь захочу попасть на эту страницу с другой страницы, которая, возможно, не имеетэлемент управления с именем SomeTextBox.

Кажется, что он ограничен и другими способами.Может быть, я хочу перейти на страницу по ссылке, например.

QueryString

В настоящее время я склоняюсь к этой стратегии, как в старые времена.Но я, вероятно, хочу, чтобы моя QueryString была зашифрована, чтобы ее было сложнее подделать, и я хотел бы также решить проблему повторных атак.

Для 4 парней из Rolla, есть статья оэто .

Однако должна быть возможность создать HttpModule, который позаботится обо всем этом и удалит все шифровальные колбасы со страницы.Конечно же, У Мадса Кристенсена есть статья, в которой он ее выпустил. Однако комментарии заставляют его звучать так, как будто у него проблемы с чрезвычайно распространенными сценариями.

Другие параметры

Конечно, это не исчерпывающий взгляд на варианты, а основные варианты, которые я рассматриваю. Эта ссылка содержит более полный список.Те, которые я не упомянул, такие как Cookies и Cache, не подходят для передачи данных между страницами.

В заключении ...

Итак, как вы решаете проблему передачи данных между страницами?Какие скрытые ошибки вам приходилось обходить, и есть ли какие-то ранее существующие инструменты, которые безупречно решают их все? Делаете Вы чувствуете, что у вас есть решение, которым вы полностью довольны?

Заранее спасибо!

Обновление: Просто вв случае, если я не достаточно ясен, под «передачей данных между страницами», о которых я говорю, например, передачей ключа CustomerID со страницы CustomerSearch.aspx в Customers.aspx, где будет открыт Customer и возможно редактирование.

Ответы [ 6 ]

41 голосов
/ 12 июля 2010

Во-первых, проблемы, с которыми вы сталкиваетесь, связаны с обработкой состояния в среде без состояния. Проблемы, с которыми вы сталкиваетесь, не новы, и, вероятно, это одна из вещей, которая делает веб-разработку более сложной, чем разработка Windows или разработка исполняемого файла.

Что касается веб-разработки, у вас есть пять вариантов, насколько мне известно, для обработки пользовательского состояния, которое можно использовать в сочетании друг с другом. Вы обнаружите, что ни одно решение не работает для всего. Вместо этого вам нужно определить, когда использовать каждое решение:

  • Строка запроса - Строки запроса хороши для передачи указателей на данные (например, значения первичного ключа) или значения состояния. Строки запроса сами по себе не должны считаться безопасными, даже если они зашифрованы из-за воспроизведения. Кроме того, некоторые браузеры имеют ограничение по длине URL. Однако строки запроса имеют некоторые преимущества, такие как то, что они могут быть добавлены в закладки и отправлены по электронной почте людям, и по сути они не имеют состояния, если не используются ни с чем другим.

  • Cookies - Cookies хороши для хранения очень небольшого количества информации для конкретного пользователя. Проблема в том, что куки также имеют ограничение по размеру, после чего они просто усекают данные, поэтому вы должны быть осторожны с размещением пользовательских данных в куки. Кроме того, пользователи могут уничтожать файлы cookie или прекращать их использование (хотя это также может помешать использованию стандартного сеанса). Как и в случае строк запросов, файлы cookie лучше, IMO, для указателей на данные, чем для самих данных, если только данные не крошечные.

  • Данные формы - Данные формы могут занимать совсем немного информации, однако за счет времени отправки и в некоторых случаях времени перезагрузки. ViewState в ASP.NET использует скрытые переменные формы для хранения информации. Передача данных между страницами с использованием чего-то вроде ViewState имеет преимущество в том, что с кнопкой «назад» удобнее работать, но может легко создавать огромные страницы, которые замедляют работу пользователя. В общем, модель ASP.NET не работает при публикации на нескольких страницах (хотя это возможно), а вместо этого работает с публикациями, возвращающимися на ту же страницу, и оттуда переходя на следующую страницу.

  • Сессия - Сессия полезна для информации, относящейся к процессу, с которым пользователь прогрессирует, или для общих настроек. Вы можете хранить довольно мало информации в сеансе за счет памяти сервера или времени загрузки из баз данных. Концептуально, Session работает, загружая всю пачку данных для пользователя сразу из памяти или с сервера состояний. Это означает, что если у вас есть очень большой набор данных, вы, вероятно, не хотите помещать его в сессию. Сессия может создать некоторые проблемы с кнопками «назад», которые необходимо сопоставить с тем, что пользователь на самом деле пытается выполнить. В общем, вы обнаружите, что кнопка «Назад» может быть проклятием веб-разработчика.

  • База данных - последнее решение (которое снова можно использовать в сочетании с другими) заключается в том, что вы сохраняете информацию в базе данных в соответствующей схеме со столбцом, который указывает состояние элемента. Например, если вы обрабатывали создание заказа, вы можете сохранить заказ в таблице «Заказ» со столбцом «состояние», который определяет, был ли это реальный заказ или нет. Вы бы сохраняли идентификатор заказа в строке запроса или сеансе. Веб-сайт будет продолжать записывать данные в таблицу для обновления различных частей и дочерних элементов, пока в конечном итоге пользователь не сможет объявить, что они выполнены, и состояние заказа помечается как реальный заказ. Это может усложнить отчеты и запросы, поскольку все они должны отличать «реальные» элементы от тех, которые находятся в процессе обработки.

Одним из элементов, упомянутых в вашей более поздней ссылке, был кэш приложений. Я бы не подумал, что это зависит от пользователя, поскольку это приложение для всего приложения. (Очевидно, что он может быть ориентирован на пользователя, но я бы не рекомендовал это делать). Я никогда не играл с сохранением данных в HttpContext, не передавая их обработчику или модулю, но я бы скептически относился к тому, что они отличаются от вышеупомянутых решений.

В общем, нет единого решения, чтобы управлять ими всеми. Лучше всего на каждой странице предположить, что пользователь мог перейти на эту страницу откуда угодно (в отличие от предположения, что он попал туда по ссылке на другой странице). Если вы сделаете это, с кнопками «назад» станет легче справляться (хотя это по-прежнему проблема). В своей разработке я широко использую первые четыре и иногда прибегаю к последнему решению, когда в этом есть необходимость.

9 голосов
/ 18 июля 2010

Хорошо, поэтому я хочу предвосхитить свой ответ этим;У Томаса, безусловно, самый точный и исчерпывающий ответ для людей, начинающих с нуля.Этот ответ совсем не в том же духе.Мой ответ исходит с точки зрения «разработчика бизнеса».Как мы все знаем слишком хорошо;иногда просто невозможно тратить деньги на переписывание чего-то, что уже существует и «работает» ... по крайней мере, не все в одном кадре.Иногда лучше реализовать решение, которое позволит вам со временем перейти на лучшую альтернативу.

Единственное, что я бы сказал, что Томасу не хватает, это;клиентское состояние javascript.Там, где я работаю, мы обнаружили, что клиенты все больше ожидают приложений типа «Web 2.0».Мы также обнаружили, что такого рода приложения обычно приводят к гораздо большей удовлетворенности пользователей.Немного потренировавшись, и с помощью некоторых действительно замечательных библиотек javascript, таких как jQuery (мы даже начали использовать GWT и обнаружили, что это AWESOME), взаимодействие с REST-сервисами на основе JSON, реализованными в WCF, может быть тривиальным.Этот подход также предоставляет очень хороший способ начать переход к архитектуре на основе SOA и четкое разделение пользовательского интерфейса и бизнес-логики.

Но я отвлекся.

Мне кажется, что у вас уже есть приложение, и вы уже вышли за пределы встроенного в ASP.NET управления состоянием сеанса.Итак ... вот мое предложение (при условии, что вы уже попробовали внепроцессное управление сеансами ASP.NET, которое значительно лучше масштабируется, чем внутрипроцессное / встроенное управление сеансами, и похоже, что вы это сделали, потому что упомянулиЭто);NCache.

NCache предоставляет вам «замену» заменой параметров управления сеансами в ASP.NET.Его очень легко реализовать, и он может «помочь» вашему приложению более чем достаточно для того, чтобы помочь вам - без каких-либо значительных инвестиций в немедленный рефакторинг существующей кодовой базы.

Вы можете использовать дополнительное время и деньги, чтобы начать сокращать свой технический долг, сосредоточив новые разработки на предметах, имеющих непосредственную ценность для бизнеса, - используя новый подход (например, любую из альтернатив, предложенных в других ответах, илимой).

Только мои мысли.

8 голосов
/ 29 апреля 2011

Несколько месяцев спустя я подумал, что обновлю этот вопрос техникой, с которой мне довелось работать, поскольку она сработала так хорошо.

После игры с более сложной обработкой состояния сеанса (что приводило к большому количеству неработающих кнопок «назад» и т. Д.), Я в итоге развернул свой собственный код для обработки зашифрованных строк QueryStrings. Это был огромный выигрыш - все мои сценарии проблем (кнопка «Назад», одновременное открытие нескольких вкладок, потерянное состояние сеанса и т. Д.) Решены, а сложность минимальна, поскольку использование очень знакомо.

Это все еще не волшебная пуля для всего, но я думаю, что это хорошо для примерно 90% сценариев, с которыми вы сталкиваетесь.

Детали

Я создал класс с именем CorePage, который наследуется от Page. У него есть методы, называемые SecureRequest и SecureRedirect.

Так что вы можете позвонить:

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

CorePage анализирует QueryString и шифрует его в переменную QueryString с именем CoreSecure. Таким образом, фактический запрос выглядит так:

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

Если доступно, текущий идентификатор пользователя, добавленный в систему, добавляется к ключу шифрования, поэтому повторные атаки не являются большой проблемой.

Оттуда вы можете позвонить:

X = SecureRequest("ClientID")

Заключение

Все работает без проблем, используя знакомый синтаксис.

За последние несколько месяцев я также адаптировал этот код для работы с крайними случаями, такими как гиперссылки, которые инициируют загрузку - иногда вам нужно создать гиперссылку на клиенте, который имеет защищенную строку QueryString. Это работает очень хорошо.

Дайте мне знать, если вы хотите увидеть этот код, и я его где-нибудь выложу.

Одна последняя мысль: странно принимать мой собственный ответ на некоторые очень вдумчивые посты, которые другие люди ставят здесь, но это действительно, кажется, окончательный ответ на мою проблему. Спасибо всем, кто помог мне попасть туда.

5 голосов
/ 25 июля 2012

После прохождения всех вышеупомянутых сценариев и ответов и этой ссылки Методы вставки данных Мой последний совет будет:

COOKIES для:

  • ENCRYPT [userId's]
  • ENCRYPT [productId]
  • ENCRYPT [xyzIds ..]
  • ENCRYPT [и т.д ..]

БАЗА ДАННЫХ для:

  • наборов данных по идентификатору COOKIE
  • наборов данных по идентификатору COOKIE
  • все другие большие куски по идентификатору COOKIE

Мой совет также зависит от приведенной ниже статистики и подробностей этой ссылки Методы вставки данных :

enter image description here

3 голосов
/ 09 июля 2010

Я бы никогда этого не сделал. У меня никогда не возникало проблем с сохранением всех данных сеанса в базе данных и их загрузкой на основе файлов cookie пользователей. Это сессия, насколько это возможно, но я сохраняю контроль над ней. Не отказывайтесь от контроля над данными вашего сеанса на веб-сервере ...

Немного поработав, вы можете поддерживать подсессии и разрешать многозадачность в разных вкладках / окнах.

2 голосов
/ 12 июля 2010

В качестве отправной точки я нахожу использование критических элементов данных, таких как идентификатор клиента, которые лучше всего поместить в строку запроса для обработки. Вы можете легко отслеживать / фильтровать плохие данные, поступающие из этих элементов, и это также обеспечивает некоторую интеграцию с электронной почтой или другими соответствующими сайтами / приложениями.

В предыдущем приложении единственный способ просмотреть сотрудника или запись запроса с его участием состояла в том, чтобы войти в приложение, выполнить поиск сотрудника или выполнить поиск последних записей, чтобы найти соответствующую запись. Это стало проблематичным и занимало много времени, когда кому-то из смежного отдела требовалось просто просмотреть записи для целей аудита.

В процессе переписывания я сделал доступными и идентификатор сотрудника, и идентификаторы запроса через базовые URL-адреса «ViewEmployee.aspx? Id = XXX» и «ViewRequest.aspx? Id = XXX». Приложение было настроено на A) отфильтровывать плохие идентификаторы и B) проверять подлинность и авторизовывать пользователя перед тем, как разрешить ему доступ к этим страницам. Это позволило основным пользователям приложения отправлять аудитору простые электронные письма с URL-адресом в электронном письме. Когда они очень спешили, у них было время массовой обработки, они могли просто щелкнуть список URL-адресов и выполнить соответствующую обработку.

Другие данные, относящиеся к сеансу, такие как даты изменения и поддержание «состояния» взаимодействия пользователя с приложением, становятся немного более сложными, но, надеюсь, это обеспечит вам стартовую подсказку.

...