ASP.NET - Добавление search engine friendly адресов на сайт
Сейчас, наверное, очень сложно себе представить хороший сайт без «красивых» адресов страниц. Они одновременно и радуют глаз, и помогают поисковикам лучше ориентироваться на сайте.
Для asp.net существует несколько библиотек, позволяющих добавить поддержку таких адресов на сайт. Все они имеют свои плюсы и минусы, но я хотел бы поделиться своим методом, который мне кажется наиболее простым, поскольку не требует большого количества кода и позволяет автоматически обрабатывать все aspx-файлы на сайте без необходимости прописывать маршруты для каждого из них.
Суть метода
Суть метода достаточно проста. Если пользователь зашёл на сайт по какому-то адресу, то этот адрес разбивается на две части: обработчик и параметры. Обработчиком считается та часть адреса, которую можно сопоставить с каким-то файлом на сервере, параметрами - всё остальное.
Например, если пользователь зашёл на сайт по адресу ~/forum/asp.net/42/, то в качестве обработчика может быть выбран один из следующих файлов с соответствующими параметрами:
- ~/forum/asp.net/42/default.aspx (без параметров);
- ~/forum/asp.net/42.aspx (без параметров);
- ~/forum/asp.net/default.aspx (с параметром 42);
- ~/forum/asp.net.aspx (с параметром 42);
- ~/forum/default.aspx (c параметрами asp.net и 42);
- ~/forum.aspx (c параметрами asp.net и 42).
Эти виртуальные пути проверяются в указанном порядке, пока не будет найден тот, который соответствует существующему на сервере файлу. Он и считается обработчиком.
Так же по аналогии можно искать файлы других типов, например ashx-файлы.
Реализация
Как я уже отметил в самом начале, реализация не требует большого количества кода и может быть добавлена в проект всего за несколько минут. Все изменения необходимо вносить в файл Global.asax.
Прежде всего подпишемся на событие PostResolveRequestCache, переопределив метод Init.
В обработчике этого события получим текущий виртуальный путь и проверим, соответствует ли он существующему на сервере файлу. Если это не так, то при помощи метода MapSefPathToExistingPath попробуем сопоставить его с другим виртуальным путём, который соответствует существующему файлу.
В случае успеха при помощи метода RewritePath заменим текущий виртуальный путь на тот, который вернёт метод MapSefPathToExistingPath, а параметры сохраним в коллекции Items.
Хотел бы отметить, что коллекция Items имеет тип IDictionary, поэтому хорошей практикой считается в качестве ключей использовать не строки, а экземпляры класса Object. В данном случае я просто упростил код.
Наконец, добавим метод MapSefPathToExistingPath, который будет выполнять сопоставление виртуальных путей. В данном методе будем убирать на конце текущего виртуального пути по одному сегменту (сегменты разделяются символом слэш), пока не получим виртуальный путь, соответствующий существующему файлу.
Всё готово. Теперь для всех запросов к несуществующим файлам будет автоматически определяться подходящий обработчик.
Заключение
Я не буду подробно описывать способ получения параметров в коде. Думаю, очевидно, что на странице их можно получить через свойство Context.Items. Для большей простоты можно даже создать свой класс страницы, в который добавить методы для быстрого получения параметров.
В данном случае я хотел лишь описать общую идею метода, поэтому конечная его реализация может существенно отличаться, поскольку будет зависеть от требований проекта.
Скачать демо (ASP.NET 4.0, Web Site)
А зачем ещё велосипед?
ОтветитьУдалитьЕсть же всё это из коробки, называется ASP.NET Routing. Работает думаю надежнее.
Безусловно, в некоторых случаях Routing отлично справляется.
ОтветитьУдалитьЯ предлагаю альтернативное решение по двум причинам:
1. Не нужно при добавлении новой страницы прописывать новый маршрут.
2. При большом количестве страниц при каждом запросе будут проверяться все маршруты на предмет их соответствия запрашиваемому адресу, что может сказаться на производительности. (Не сомневаюсь, что там всё значительно оптимизировано, но всё же)
Ну вообще есть в ASP.NET Routing возможность и таких конструкций
ОтветитьУдалитьroutes.MapPageRoute("rule1", "{path}/{page}/{*queryvalues}", "~/default.aspx")
так что для КАЖДОЙ новой страницы делать правило нет смысла
Такая конструкция:
ОтветитьУдалитьroutes.MapPageRoute("rule1", "{path}/{page}/{*queryvalues}", "~/default.aspx")
Сделает так, что обработчиком всех запросов будет страница ~/default.aspx. Разве не так?
routes.MapPageRoute("rule1", "{path}/{page}/{*queryvalues}", "~/default.aspx")
ОтветитьУдалитьТут смысл немного другой, главное идея.
Можно и так
routes.MapPageRoute("rule1", "forum/{topic}/{*queryvalues}", "~/forum/default.aspx")
смысл будет в том что подпадает любой адрес типа /forum/*/**
например /forum/topic2/page2 или /forum/to2pic/reply/post2
только в последнем случае reply/post2 будет находиться в одной переменной и её нужно будет разбирать.
Это только если форум, а если на сайте ещё есть страницы login.aspx, registration.aspx, catalog.aspx и всякие другие *.aspx?
ОтветитьУдалитьВсё равно для каждой нужно будет добавить маршрут и все маршруты будут проверяться при каждом запросе.
Поэтому у меня и возникла мысль сопоставлять запрос с каким-то файлом на сервере основываясь на его расположении, а не на таблице маршрутов.
Кстати, справедливости ради стоит отметить, что похожий метод используется в Asp.net Web Pages. Если установить WebMatrix, то IIS начинает обрабатывать cshtml-файлы как обычные aspx-файлы (только с синтаксисом Razor).
Т.е если в корень сайта положить файл forum.cshtml, то он будет доступен по адресам ~/forum.cshtml, ~/forum/, ~/forum/bla-bla/, ~/forum/bla-bla/bla-bla/ итд.