Главная страница   /   4.1. Изучение контроллеров и действий (ASP.NET MVC 4 в действии

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

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

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

4.1. Изучение контроллеров и действий

Как вы уже поняли из главы 2, контроллер является одним из компонентов ASP.NET MVC приложения. Это класс, который содержит один или более одного открытого метода (действия), соответствующего конкретному URL-адресу. Эти действия выполняют в вашем приложении функции "клея", соединяя вместе данные, взятые из модели, и пользовательский интерфейс приложения (представление). В связи с этим важно понимать, как работают эти действия. В этом разделе вы получите лучшее понимание того, как работают методы действия контроллера, поскольку мы вкратце исследуем строение контроллера и рассмотрим, что обычно должно входить в состав метода действия этого контроллера. Но для начала давайте напомним себе, как выглядит действие контроллера. Ниже представлено действие Index нашего GuestbookController.

Листинг 4-1: Действие Index контроллера GuestbookController
public class GuestbookController : Controller
{
	private GuestbookContext _db = new GuestbookContext();
	public ActionResult Index()
	{
		var mostRecentEntries = (from entry in _db.Entries
														 orderby entry.DateAdded descending
														 select entry).Take(20);
		var model = mostRecentEntries.ToList();
		return View(model);
	}
}

Строка 1: Наследуется от Controller

Строка 4: Открытый метод действия

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

IController и базовые классы контроллера

Интерфейс IController определяет самый основной элемент контроллера – метод под названием Execute, который в качестве параметра получает объект RequestContext:

public interface IController
{
	void Execute(RequestContext requestContext);
}

Самый простой вид контроллера мог бы реализовать этот интерфейс, а затем переписать некоторый HTML для добавления отклика.

Листинг 4-2: Реализация интерфейса IController вручную
public class SimpleController : IController
{
	public void Execute(RequestContext requestContext)
	{
		requestContext.HttpContext.Response.Write("<h1>Welcome to the Guest Book.</h1>");
	}
}

Строка 1: Реализует IController

Строка 3: Определяет метод Execute

Строка 5: Записывает HTML для отклика

Данный контроллер реализует интерфейс IController путем определения метода Execute. Внутри этого метода мы можем напрямую получить доступ к объектам HttpContext, Request и Response. Такой способ определения контроллеров очень близок к метаязыку, но не очень полезен. Мы не можем отображать представления напрямую и прекращаем смешивать процесс отображения с логикой контроллера посредством написания HTML прямо внутри контроллера. Кроме того, мы не принимаем во внимание все полезные возможности фреймворка, например такие, как безопасность (которую мы рассмотрим в главе 8), связывание данных модели (глава 10) и результаты действий (глава 16). Мы также потеряли возможность определять метод действия – все запросы к этому контроллеру управляются методом Execute.

На самом деле маловероятно, что вам потребуется реализовывать интерфейс IController, потому что сама по себе такая возможность не особенно полезна (но такая возможность присутствует на тот случай, если по каким-то причинам вам будет необходимо пренебречь большинством других возможностей фреймворка). Чаще всего вместо этого вы будете выполнять наследование от базового класса. Существует два базовых класса, от которых вы можете унаследовать контроллер – ControllerBase и Controller.

Наследование от класса ControllerBase

Класс ControllerBase напрямую реализует интерфейс IController, но содержит инфраструктуру, необходимую для нескольких возможностей, которые мы уже рассматривали. Например, класс ControllerBase содержит свойство ViewData, которое, как вы видели ранее, может быть использовано для передачи данных в представление. Тем не менее, сам по себе класс ControllerBase не очень полезен – мы все еще не можем отображать представления или использовать методы действий. За это уже отвечает класс Controller.

Наследование от класса Controller

Класс Controller наследуется от класса ControllerBase, поэтому в него включены все свойства, определенные в классе ControllerBase (к примеру, свойство ViewData), но в тот же время данный класс добавляет значительное количество дополнительных функциональных возможностей. Этот класс содержит ControllerActionInvoker, который знает, как выбирать метод, который необходимо выполнить, на основании URL-адреса, и определяет такие методы, как метод View, который, как вы уже видели, может использоваться для отображения представления в рамках действия контроллера. Это тот класс, от которого вы будете наследовать свои классы при создании своих собственных контроллеров (и который мы будем продолжать использовать в этой книге в дальнейшем). В большинстве случаев нет причины выполнять наследование напрямую от ControllerBase или IController, но полезно знать, что они существуют, поскольку они играют важную роль в MVC конвейере.

Теперь, когда мы увидели, как класс становится контроллером, давайте перейдем к рассмотрению того, что представляет собой метод действия.

Что представляет собой метод действия

Из главы 2 вы узнали, что методы действия – это открытые методы класса контроллеров (в действительности, правила определения того, должен ли метод рассматриваться в качестве действия, несколько сложнее – мы рассмотрим эти правила в главе 16). Чаще всего метод действия возвращает экземпляр ActionResult (к примеру, ViewResult при вызове return View()). Но методы действия не обязаны возвращать экземпляры ActionResult. Например, действие могло бы возвращать результат void и напрямую записывать код отклика (подобно SimpleController в листинге 4.2.):

public class AnotherSimpleController : Controller
{
	public void Index()
	{
		Response.Write("<h1>Welcome to the Guest Book.</h1>");
	}
}

Того же результата можно было бы достичь путем возврата фрагмента HTML напрямую из действия контроллера:

public class AnotherSimpleController : Controller
{
	public string Index()
	{
		return "<h1>Welcome to the Guest Book.</h1>";
	}
}

Так можно поступить, потому что ControllerActionInvoker гарантирует, что возвращаемое значение действия всегда будет преобразовываться в ActionResult. Если действие уже возвращает ActionResult (например, ViewResult), то в этом случае этот метод просто вызывается. Тем не менее, если действие возвращает значение другого типа (в данном примере – типа String), то возвращаемое значение преобразовывается в объект ContentResult, который просто переписывает HTML для добавления отклика. Выходной результат будет таким же, как и при использовании ContentResult напрямую:

public class AnotherSimpleController : Controller
{
	public string Index()
	{
		return Content("<h1>Welcome to the Guest Book.</h1>");
	}
}

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

Помимо отображения разметки или возврата представления существуют другие доступные типы результатов действий. Например, вы можете перенаправить веб-браузер пользователя на другую страницу, возвращая RedirectToRouteResult (который вы использовали при вызове метода RedirectToAction в листинге 2.7 главы 2) или возвращая содержимое других типов, к примеру, JSON (который мы рассмотрим в главе 7, когда будем изучать Ajax-функциональность).

Также для того чтобы открытые методы контроллера не являлись действиями, вы можете использовать NonActionAttribute:

public class TestController : Controller
{
	[NonAction]
	public string SomePublicMethod()
	{
		return "Hello World";
	}
}

NonActionAttribute является примером селектора метода действия, который может использоваться для переопределения применяемой по умолчанию технологии, при которой название метода совпадает с названием действия. NonActionAttribute – самый простой вид селектора, который не допускает доступа к методу посредством URL. Вы уже встречались с еще одним примером селектора действия в главе 2 – селектор HttpPostAttribute, который гарантирует, что действие отвечает только на запросы HTTP POST.

Примечание

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

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

Теперь давайте рассмотрим, какого вида логика должна присутствовать внутри метода действия.