Получение входных данных
Контроллерам часто необходим доступ к входящим данным, таким как значения строки запроса, значения форм и параметры, полученные из входящего URL системой маршрутизации. Есть три основных способа доступа к этим данным:
- Получить их из набора контекстных объектов
- Получить данные, переданные в качестве параметров методу действия
- Явно вызвать связывание данных модели
Здесь мы рассмотрим подходы получения входных данных для методов действия, уделяя особое внимание использованию контекстных объектов и параметров метода действия. В главе 22 мы подробно изучим связывание данных модели.
Получение данных из контекстных объектов
Самый прямой способ получить данные заключается в том, чтобы извлечь их самостоятельно. Когда вы создаете контроллер путем наследования от базового класса Controller
, вы получаете доступ к набору полезных свойств для получения информации о запросе. Эти свойства включают Request
, Response
, RouteData
, HttpContext
и Server
. Каждое из них предоставляет информацию о различных аспектах запроса. Эти свойства получают различные типы данных из экземпляра запроса ControllerContext
(который может быть доступным через свойство Controller.ControllerContext
). Мы описали некоторые из наиболее часто используемых контекстных объектов в таблице 15-1.
Таблица 15-1: Наиболее часто используемые контекстные объекты
Свойство | Тип | Описание |
Request.QueryString |
NameValueCollection |
Переменные GET , отправленные с этим запросом |
Request.Form |
NameValueCollection |
Переменные POST , отправленные с этим запросом |
Request.Cookies |
HttpCookieCollection |
Куки, отправленные браузером с этим запросом |
Request.HttpMethod |
string |
HTTP метод (например, GET или POST ), используемый для этого запроса |
Request.Headers |
NameValueCollection |
Полный набор HTTP заголовков, отправленный с этим запросом |
Request.Url |
Uri |
Запрашиваемый URL |
Request.UserHostAddress |
string |
IP адрес пользователя, сделавшего запрос |
RouteData.Route |
RouteBase |
Выбранная запись из RouteTable.Routes для этого запроса |
RouteData.Values |
RouteValueDictionary |
Активные роутовые параметры (как полученные из URL, так и значения по умолчанию) |
HttpContext.Application |
HttpApplicationStateBase |
Состояние приложения |
HttpContext.Cache |
Cache |
Кэш приложения |
HttpContext.Items |
IDictionary |
Состояние текущего запроса |
HttpContext.Session |
HttpSessionStateBase |
Состояние сессии пользователя |
User |
IPrincipal |
Информация об аутентификации залогиненного пользователя |
TempData |
TempDataDictionary |
Информация о временных данных текущего пользователя |
Метод действия может использовать любой из этих контекстных объектов, чтобы получить информацию о запросе, как показано в листинге 15-5, который демонстрирует гипотетический метод действия.
Листинг 15-5: Метод действия использует контекстные объекты, чтобы получить информацию о запросе
public ActionResult RenameProduct() {
// Доступ к различным свойствам контекстных объектов
string userName = User.Identity.Name;
string serverName = Server.MachineName;
string clientIP = Request.UserHostAddress;
DateTime dateStamp = HttpContext.Timestamp;
AuditRequest(userName, serverName, clientIP, dateStamp, "Renaming product");
// Получение данных из Request.Form
string oldProductName = Request.Form["OldName"];
string newProductName = Request.Form["NewName"];
bool result = AttemptProductRename(oldProductName, newProductName);
ViewData["RenameResult"] = result;
return View("ProductRenamed");
}
Вы можете просмотреть широкий спектр доступной контекстной информации о запросе при помощи IntelliSense (в методе действия наберите this.
и просмотрите информацию во всплывающем окне) и Microsoft Developer Network (посмотрите System.Web.Mvc.Controller
и его базовые классы или System.Web.Mvc.ControllerContext
).
Использование параметров метода действия
Как вы видели в предыдущих главах, методы действий могут принимать параметры. Это более аккуратный способ получения входящих данных, чем извлечение их вручную из контекстных объектов, и благодаря этому методы действий легче читать. Например, предположим, у нас есть метод действия, который использует контекстные объекты таким образом:
...
public ActionResult ShowWeatherForecast() {
string city = (string)RouteData.Values["city"];
DateTime forDate = DateTime.Parse(Request.Form["forDate"]);
// ... здесь идет реализация прогноза погоды ...
return View();
}
...
Мы можем переписать его, чтобы использовать параметры:
...
public ActionResult ShowWeatherForecast(string city, DateTime forDate) {
// ... здесь идет реализация прогноза погоды ...
return View();
}
Это не только легче читать, но это также помогает с модульным тестированием: мы можем проверить метод действия без необходимости использовать mock-технологию для свойств класса контроллера.
Для полноты картины стоит отметить, что методам действия не разрешается параметры out
или ref
. Это не имело бы никакого смысла, если бы разрешалось, и ASP.NET MVC просто выбросит исключение, если он увидит такие параметры.
MVC фреймворк предоставит значения для наших параметров, проверив контекстные объекты, в том числе Request.QueryString
, Request.Form
и RouteData.Values
. Имена наших параметров рассматриваются как регистронезависимые, так что параметр метода действия city
можно заполнить значением из Request.Form["City"]
.
Понимание того, как создаются экземпляры объектов параметров
Базовый класс Controller
получает значения для параметров вашего метода действия с помощью MVC компонентов, называемых провайдерами значений и механизмами связывания данных модели.
Провайдеры значений представляют собой совокупность элементов данных, доступных для вашего контроллера. Есть встроенные провайдеры значений, которые получают элементы из Request.Form
, Request.QueryString
, Request.Files
и RouteData.Values
. Затем значения передаются механизмам связывания данных, которые пытаются привязать их к типам, которые методы действий требуют в качестве параметров.
Дефолтовые механизмы связывания данных могут создать и заполнить объекты любого .NET типа, в том числе коллекции и специфические пользовательские типы для конкретного проекта. Вы видели пример этого в главе 10, когда сообщения от администраторов были представлены нашему методу действия как один объект Product
, даже при том что отдельные значения были рассеяны среди элементов HTML формы. Мы расскажем о провайдерах значений и механизмах связывания данных модели в главе 22.
Понимание факультативных и обязательных параметров
Если MVC фреймворк не может найти значение параметра ссылочного типа (например, string
или object
), все равно будет вызван метод действия, но с использованием значения null
для этого параметра. Если значение не может быть найдено для параметра простого типа (например, int
или double
), то будет сгенерировано исключение, и метод действия не будет вызван. Вот почему это так:
- Параметры простого типа являются обязательными. Чтобы сделать их необязательными, либо укажите значение по умолчанию (см. следующий раздел), либо замените тип параметра на nullable (например,
int?
илиDateTime?
). Таким образом, MVC сможет передатьnull
, если значение будет не доступно. - Параметры ссылочного типа не являются обязательными. Чтобы сделать их обязательными (чтобы убедиться, что передается значение не-
null
), добавьте код в начало метода действия, который не принимает значенияnull
. Например, если значение равноnull
, выбрасывается исключениеArgumentNullException
.
Указание значений параметров по умолчанию
Если вы хотите обрабатывать запросы, которые не содержат значений для параметров метода действия, но вы не хотите проверять в коде наличие значений null
или выбрасывать исключения, вы можете использовать дополнительные параметры C#. В листинге 15-6 показан пример.
Листинг 15-6: Использование дополнительных параметров C# для метода действия
...
public ActionResult Search(string query= "all", int page = 1) {
// ...обработка запроса...
return View();
}
...
Мы обозначаем параметры как дополнительные путем присвоения значений, когда мы их определяем. В листинге мы предоставили значения по умолчанию для параметров query
и page
. MVC Framework попытается получить значения из запроса для этих параметров, но если доступных значений не будет, будут использоваться значения по умолчанию, которые мы указали.
Для параметра string query
, это означает, что нам не нужно проверять наличие значений null
. Если в запросе, который мы обрабатываем, не задана строка запроса, тогда наш метод действия будет вызван со строкой all
. Для параметра int
нам не нужно беспокоиться о том, что запросы завершатся ошибками, если нет значения page
. Наш метод действия будет вызываться со значением по умолчанию равным 1
.
Необязательные параметры могут быть использованы для литеральных типов: это типы, которые можно определить без использования ключевого слова new
, включая string
, int
и double
.
Внимание
Если запрос содержит значение для параметра, но оно не может быть преобразовано в правильный тип (например, если пользователь дает нечисловую строку для параметра
int
), то фреймворк передаст значение по умолчанию для этого типа параметра (например,0
для параметраint
) и зарегистрирует предоставленное значение как ошибку валидации в специальном контекстном объектеModelState
. Если вы не проверите ошибки валидации вModelState
, вы можете попасть в неприятную ситуацию, когда пользователь ввел неправильные данные в форму, но запрос обрабатывается, как если бы пользователь не ввел никаких данных вообще или ввел значение по умолчанию. См. главу 23 для информации о валидации иModelState
, который может быть использован, чтобы избежать таких проблем.