Переход со старого ASP.NET
Старое ASP.NET-приложение нельзя обновить простым изменением TargetFramework, если оно зависит от System.Web, WebForms и другого конвейера. При этом пользователи и поисковые системы уже знают его URL. Миграция должна разделить внутреннее устройство и публичный договор: новая реализация может меняться, а важные маршруты и смысл ответов требуют осознанного сохранения.
Этот урок завершает серию планом совместимого перехода. В lesson-32 архива продолжения находятся полный учебный реестр маршрутов, правила владельцев и сценарии сравнения. Reverse proxy, перенос настоящего ASP.NET, cookies sharing и деплой не выполняются. Данные ProfessorWeb используются как опыт сохранения адресов, но существующие PHP-страницы сайта не изображаются приложением System.Web. Текущая работа остаётся локальным проектированием.
Сначала фиксируется внешний контракт
У старого маршрута важны не только path и заголовок. Нужно записать методы, query, status, content type, форму данных, redirects и условия доступа. Для учебного старого /Catalog.aspx?topic=frontend ожидается HTML-страница с тремя курсами. Для /api/courses/javascript — JSON указанного курса. Эти ресурсы нельзя автоматически объявить одинаковыми лишь из-за общей темы каталога.
Реестр хранит один владеющий backend на каждый маршрут. До переключения это legacy, после подтверждённой совместимости — core. Порядок правил задаётся явно, чтобы широкий fallback не перехватил выбранный новый API. Неизвестный адрес остаётся 404, а не получает главную с кодом 200.
method,path,query,owner,expected_status,representation
GET,/Catalog.aspx,topic=frontend,legacy,200,text/html
GET,/api/courses/javascript,,core,200,application/json
GET,/missing-route,,none,404,error
Это полный маленький пример реестра, а не настройка уже работающего proxy. Он показывает, что переключается конкретное сочетание метода и ресурса. POST по тому же path требует собственного правила; перенос только GET не означает готовность изменяющих операций.
Два приложения могут сосуществовать
Постепенная миграция позволяет направлять часть запросов в новое приложение, сохраняя остальные в старом. Такой подход требует routing-слоя и границы доверия между приложениями. В архиве описано решение владельцев, но executable proxy-конфигурация не поставляется: выбор IIS, YARP или другой инфраструктуры зависит от настоящего окружения.
Не следует выдавать проксирование за переписанный код. Если маршрут по-прежнему обслуживает старый backend, он остаётся старым, даже когда пользователь видит общий header. Это полезное промежуточное состояние, которое должно отражаться в реестре и наблюдении, чтобы команда знала, какая реализация отвечает за сбой.
Внутренний адрес backend не должен становиться canonical публичной страницы. Redirect также не должен утекать на loopback или закрытый host. Приложению нужно корректно понимать внешний scheme, host и path base, но forwarded headers принимаются только от доверенного proxy. Разрешение произвольному клиенту задавать эти поля может подменить генерируемые ссылки и решения безопасности.
Совместимость представления проверяется отдельно
Сохранённый URL ещё не гарантирует прежний ответ. Если HTML-страница стала JSON, старые посетители не получают прежний смысл. Если 404 превратился в 200 с красивой заглушкой, это меняет поведение поисковых систем и клиентов. Реестр должен отмечать такие различия и принимать их как отдельные решения, а не считать техническим успехом отсутствие сетевой ошибки.
Для ProfessorWeb мы сохраняли конкретные пути, включая .php, хотя содержимое новой версии стало статическим. Этот опыт применим к принципу маршрутов, но не означает, что PHP-исполнение было перенесено в ASP.NET Core. Сервер должен выдать сохранённый ресурс по историческому адресу; динамические формы требуют иной реализации, если они действительно нужны.
При изменении адреса выбирается один постоянный redirect на конечный ресурс. Цепочка из нескольких промежуточных адресов затрудняет наблюдение и увеличивает число переходов. Однако нельзя делать redirect всех неизвестных URL на главную, чтобы скрыть пропуски инвентаризации. Отсутствующий ресурс должен оставаться различимым.
Сессии нельзя объединить названием cookie
У старого ASP.NET и нового ASP.NET Core могут быть разные форматы билета, key storage и правила аккаунтов. Одинаковое имя cookie не делает их совместимыми. Нельзя просто заставить оба приложения читать одну строку и считать, что identity перенесена. При необходимости совместного входа требуется отдельно поддерживаемая интеграция и проверка свойств отзыва, срока жизни и claims.
Проект миграции должен определить источник учётной записи. Если старый backend выдаёт ID, новое приложение не принимает его из незаверенного браузерного header. Передача через proxy тоже требует защищённого доверенного канала и исключения прямого обхода. Реестр урока описывает эту границу, но не поставляет псевдорешение вида X-User: admin.
CSRF, политики ресурса и файловые пути из уроков 22–24 проверяются на каждой новой изменяющей операции. Общая форма входа сама по себе не доказывает, что старый POST и новый POST защищены одинаково. Пока маршрут остаётся legacy, его безопасность оценивается в старой реализации; после переключения — в новой, без пропуска между ними.
Данные и писатели переходят согласованно
Самая опасная часть часто скрыта за маршрутом: два приложения могут одновременно писать в одну таблицу по разным правилам. Версия из урока 19 не защищает, если старый UPDATE не увеличивает её. Новая проверка owner не помогает, если legacy позволяет произвольное изменение. Поэтому для каждого изменяющего маршрута фиксируется один действующий писатель либо полностью согласованный общий контракт.
Схему можно расширять совместимыми полями, но удаление старой колонки до перевода читателей разрушит действующие запросы. План содержит последовательность: добавить совместимое представление, перевести чтение, согласовать запись, подтвердить отсутствие старых обращений и только затем удалить лишнее. Эти действия пока не исполняются и не считаются выполненной миграцией.
Откат тоже требует предметного ответа. Возврат routing к legacy прост лишь тогда, когда новые данные остаются понятны старому приложению. Если Core уже записал несовместимое состояние, одна кнопка переключения не восстановит корректность. До выпуска нужно определить допустимую границу отката, резервную копию и правила восстановления.
Для будущего сравнения возьмите каждый маршрут реестра и сопоставьте status, заголовки, ссылки, данные и отказ доступа. Отдельно пройдите неизвестные пути и POST. После этого конкретный маршрут может перейти к Core с наблюдением и возможностью возврата, а не весь сайт за один непроверенный шаг. Подход постепенной миграции описан в официальном руководстве ASP.NET.