Главная страница   /   9.5. Маршрутизация с ASP.NET Web Forms (ASP.NET MVC 4 в действии

ASP.NET MVC 4 в действии

ASP.NET MVC 4 в действии

Джеффри Палермо

9.5. Маршрутизация с ASP.NET Web Forms

До сих пор мы рассматривали маршрутизацию в рамках ASP.NET MVC. Хотя система маршрутизации действительно была впервые введена в MVC, впоследствии она была перемещена в ядро .NET Framework в .NET 3.5 SP1, и в .NET 4 она также полностью поддерживается в приложениях ASP.NET Web Forms. Это значит, что страницы Web Forms могут существовать бок о бок с контроллерами и представлениями MVC в рамках одного проекта, используя одни и те же схемы URL.

В этом разделе мы рассмотрим то, как страницы, разработанные в ASP.NET MVC, могут работать вместо со страницами, написанными в ASP.NET Web Forms, и как страницы Web Forms могут использовать инфраструктуру маршрутизации URL.

Добавляем роуты для страниц Web Forms

Продолжая работу с примером интернет-магазина, представьте, что у нас есть страница под названием ProductsByCategory.aspx, первоначально написанная с помощью ASP.NET Web Forms, которая составляет перечни товаров, сгруппированных по категории, как показано на рисунке 9-4.

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

http://example.com/ProductsByCategory.aspx?category=Books

Рисунок 9-4 : Страница Web Forms ProductsByCategory

Вот код этой страницы.

Листинг 9-3: Код страницы ProductsByCategory
public partial class ProductsByCategory : Page
{
	private ProductRepository _productRepository
		= new ProductRepository();
	protected void Page_Load(object sender, EventArgs e)
	{
		string category = Request.QueryString["category"];
		var productsByCategory =
			_productRepository
			.GetProductsByCategory(category);
		_groupedProductsRepeater.DataSource =
			productsByCategory;
		_groupedProductsRepeater.DataBind();
	}
}

Строка 7: Получает категорию из строки запроса

Строки 8-10: Загружает продукты для категории

Строки 11-13: Связывает продукты с UI

Метод Page_Load вызывается после того, как загружена веб-форма. Он сначала извлекает категорию из строки запроса (если она указана), а затем передает ее в метод GetProductsByCategory в ProductRepository. Этот метод возвращает список объектов Product, сгруппированных по категориям (если категория не указана, метод GetProductsByCategory возвращает все товары). Эти товары затем связываются со свойством DataSource Repeater, которое используется для представления UI. Разметка страницы показана здесь.

Листинг 9-4: Разметка страницы Web Forms
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="ProductsByCategory.aspx.cs"
Inherits="RoutingSample.ProductsByCategory" %>
<!DOCTYPE html>
<html>
<head runat="server">
	<title>Products by Category</title>
	<link rel="Stylesheet"
		href="~/content/site.css" type="text/css" />
</head>
<body>
	<form runat="server">
		<ul>
			<asp:repeater runat="server"
				id="_groupedProductsRepeater">
				<ItemTemplate>
				<li>
					<strong><%# Eval("Category") %></strong>
					<ul>
						<asp:Repeater runat="server"
							DataSource='<%# Eval("Products") %>'>
						<ItemTemplate>
							<li><%# Eval("Name") %></li>
						</ItemTemplate>
						</asp:Repeater>
					</ul>
				</li>
				</ItemTemplate>
			</asp:repeater>
		</ul>
	</form>
</body>
</html>

Строка 14: Repeater создает список категорий

Строка 18: Выводит имя категории

Строки 20-21: Дочерний Repeater для товаров

Строка 23: Выводит название товара

Страница содержит Repeater, который выдает список категорий, где каждая категория содержит список товаров.

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

В файле Global.asax мы можем зарегистрировать другой роут, который соотнесет URL /Products-ByCategory со страницей ProductsByCategory.aspx, как показано в следующем листинге. Мы добавим его предпоследним (до catchall роута, который был определен ранее).

Листинг 9-5: Добавляем роут для страницы Web Forms
routes.MapPageRoute(
	"ProductsByCategory",
	"ProductsByCategory/{category}",
	"~/ProductsByCategory.aspx",
	checkPhysicalUrlAccess: true,
	defaults: new RouteValueDictionary(new{category="All"})
);

Строка 1: Добавление роута для веб формы

Вместо того, чтобы использовать метод MapRoute из предыдущих примеров, мы используем метод MapPageRoute, который появился в .NET 4 для добавления роутов для страниц Web Forms. Этот метод принимает несколько аргументов. Как и в MapRoute, первый – это имя роута, а второй - шаблон URL, который должен соответствовать роуту. Далее мы указать соответствующий приложению путь к странице Web Forms, которая должна обработать запрос. Четвертый аргумент указывает, следует ли ASP.NET проверять, имеет ли текущий пользователь доступ к физической странице ASPX. Наконец, мы предоставляем RouteValueDictionary, содержащий значения по умолчанию. В этом случае мы указываем, что если параметр категории опущен, то по умолчанию должна использоваться строка All.

Теперь, когда роут настроен, нам нужно изменить страницу так, чтобы извлечь параметр категории из RouteData, а не из строки запроса, как показано ниже.

Листинг 9-6: Изменяем страницу Web Forms, чтобы использовать RouteData
protected void Page_Load(object sender, EventArgs e)
{
	string category = (string)RouteData.Values["category"];
	var productsByCategory =
		_productRepository.GetProductsByCategory(category);
	_groupedProductsRepeater.DataSource = productsByCategory;
	_groupedProductsRepeater.DataBind();
}

Строка 3: Получает значение из данных роута

Метод Page_Load остался почти таким же, как раньше. Единственное изменение заключается в том, что теперь он получает имя категории из RouteData.Values, а не Request.QueryString. Свойство RouteData обеспечивает доступ ко всей информации о текущем роуте, и оно было включено в базовый класс Page в Web Forms 4.

Запуск приложения в этой точке и посещение URL /ProductsByCategory теперь будет иметь точно такой же результат, как на рисунке 9-4.

Маршрутизация запросов к страницам Web Forms - только часть работы, мы также хотим, чтобы страницы Web Forms могли ссылаться на действия контроллеров MVC для беспрепятственного перехода из одной части приложения в другую.

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

Например, мы можем изменить разметку из листинга 9-4 так, чтобы генерировать URL на страницу товара для каждого товара. Этого можно добиться с помощью метода GetRouteUrl, как показано здесь.

Листинг 9-7: Генерируем URL с помощью GetRouteUrl
<asp:repeater runat="server" id="_groupedProductsRepeater">
	<ItemTemplate>
		<li>
		<strong><%# Eval("Category") %></strong>
		<ul>
			<asp:Repeater runat="server"
				DataSource='<%# Eval("Products") %>'>
				<ItemTemplate>
					<li>
						<asp:HyperLink runat="server"
							NavigateUrl='<%# GetRouteUrl(new{
								controller = "Catalog",
								action = "Show",
								productCode=Eval("Code")
								}) %>'
							Text='<%# Eval("Name") %>' />
					</li>
				</ItemTemplate>
			</asp:Repeater>
		</ul>
		</li>
	</ItemTemplate>
</asp:repeater>

Строки 11-14: Устанавливает URL

В разметке элемента Repeater мы вызываем метод GetRouteUrl и связываем его значение со свойством NavigateUrl серверного элемента управленияasp:Hyperlink. Этот метод принимает анонимный тип, в котором мы указываем контроллер и действие, на которые мы хотим получить ссылку, а также код продукта (который извлекается из контекста привязки данных с помощью функции Eval). Есть другие перегруженные варианты для этого метода, которые могут использоваться с указанными роутами.

Теперь мы знаем, как можно определить роуты и для контроллеров, и страниц Web Forms. Далее мы рассмотрим, как отлаживать роуты, когда они работают не так, как ожидалось.