Перенаправления (редирект)

149

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

При выполнении перенаправления браузеру отправляется один из следующих двух кодов HTTP:

Код HTTP 302

Означает временное перенаправление. Это наиболее часто применяемый тип перенаправления.

Код HTTP 301

Означает постоянное перенаправление. Данный код должен применяться с осторожностью, т.к. он указывает получателю больше не запрашивать исходный URL, а использовать новый URL, который передается вместе с кодом перенаправления. В случае сомнений применяйте временное перенаправление, т.е. отправляйте код 302.

Перенаправление на точный URL

Наиболее базовый способ перенаправления браузера заключается в вызове метода Redirect() класса контроллера, который возвращает экземпляр класса RedirectResult, как показано в примере ниже:

using System;
using System.Web.Mvc;

namespace ControllersAndActions.Controllers
{
    public class ExampleController : Controller
    {
        // ...

        public RedirectResult Redirect()
        {
            return Redirect("/Basic/Index");
        }
    }
}

URL, на который необходимо выполнить перенаправление, выражается в виде строки и передается в качестве параметра методу Redirect(). Метод Redirect() обеспечивает временное перенаправление. Постоянное перенаправление можно реализовать с помощью метода RedirectPermanent(), как продемонстрировано в примере ниже:

using System;
using System.Web.Mvc;

namespace ControllersAndActions.Controllers
{
    public class ExampleController : Controller
    {
        // ...

        public RedirectResult Redirect()
        {
            return RedirectPermanent("/Basic/Index");
        }
    }
}

Модульное тестирование: перенаправление на точные URL

Перенаправление на точные URL тестировать очень просто. С помощью свойств Url и Permanent класса RedirectResult можно прочитать URL и вид перенаправления (постоянное или временное). Ниже приведен тестовый метод для перенаправления:

// ...
[TestMethod]
public void ControllerTest()
{
    // Организация - создание контроллера
    ExampleController controller = new ExampleController();

    // Действие - вызов метода действия
    RedirectResult result = controller.Redirect();

    // Утверждение - проверка результата
    Assert.IsTrue(result.Permanent);
    Assert.AreEqual("/Basic/Index", result.Url);
}
// ...

Обратите внимание, что тест обновлен для получения экземпляра класса RedirectResult при вызове метода действия.

Перенаправление на URL системы маршрутизации

При перенаправлении пользователя на другую часть приложения отправляемый URL должен быть допустимым в рамках схемы URL. С применением для перенаправления точных URL связана одна существенная проблема - любое изменение в схеме маршрутизации потребует поиска и обновления всех точных URL в коде. К счастью, можно использовать систему маршрутизации для генерирования допустимых URL с помощью метода RedirectToRoute(), который создает экземпляр класса RedirectToRouteResult, как показано в примере ниже:

using System;
using System.Web.Mvc;

namespace ControllersAndActions.Controllers
{
    public class ExampleController : Controller
    {
        // ...

        public RedirectToRouteResult RedirectRoute()
        {
            return RedirectToRoute(new
            {
                controller = "Basic",
                action = "Index",
                id = "MyId"
            });
        }
    }
}

Метод RedirectToRoute() выдает временное перенаправление. Для постоянного перенаправления применяйте метод RedirectToRoutePermanent(). Оба метода принимают анонимный тип, свойства которого затем передаются системе маршрутизации для генерирования URL.

Обратите внимание, что метод RedirectToRoute() возвращает объект RedirectRouteResult, поэтому метод действия был изменен для возврата указанного типа.

Модульное тестирование: перенаправления на URL системы маршрутизации

Ниже приведен модульный тест для текущего метода действия RedirectRoute контроллера Example:

// ...
[TestMethod]
public void ControllerTest()
{
    // Организация - создание контроллера
    ExampleController controller = new ExampleController();

    // Действие - вызов метода действия
    RedirectToRouteResult result = controller.RedirectRoute();

    // Утверждение - проверка результата
    Assert.IsFalse(result.Permanent);
    Assert.AreEqual("Basic", result.RouteValues["controller"]);
    Assert.AreEqual("Index", result.RouteValues["action"]);
    Assert.AreEqual("MyId", result.RouteValues["id"]);
}
// ...

Как видите, результат проверяется косвенным образом за счет просмотра информации о маршруте, предоставляемой объектом RedirectToRouteResult, поэтому нет необходимости в разборе URL.

Перенаправление на метод действия

Выполнить перенаправление на метод действия можно более элегантно, воспользовавшись методом RedirectToAction() (для временного перенаправления) или методом RedirectToActionPermanent() (для постоянного перенаправления). Они являются просто оболочками вокруг метода RedirectToRoute() и позволяют указывать значения для метода действия и контроллера без необходимости в создании анонимного типа:

// ...
public RedirectToRouteResult RedirectRoute()
{
     return RedirectToAction("Index");
}
// ...

Если указан только метод действия, предполагается, что он относится к текущему контроллеру. Для перенаправления на другой контроллер понадобится передать его имя в виде параметра, например:

// ...
public RedirectToRouteResult RedirectRoute()
{
     return RedirectToAction("Index", "Basic");
}
// ...

Доступны и другие перегруженные версии, которые можно использовать для предоставления дополнительных значений, применяемых при генерации URL. Они выражаются с помощью анонимного типа, который не умаляет предназначение удобного метода, но может сделать код более удобным для чтения.

Значения, предоставляемые для метода действия и контроллера, не проверяются перед их передачей системе маршрутизации. Вы сами отвечаете за то, что указываемые цели в действительности существуют.

Сохранение данных между перенаправлениями

Перенаправление приводит к отправке браузером полностью нового запроса HTTP, а это означает невозможность доступа к деталям исходного запроса. Если необходимо передать данные из одного запроса в другой, можно воспользоваться объектом TempData. Объект TempData похож на данные Session за исключением того, что значения TempData помечаются для удаления, когда они прочитаны, и удаляются после обработки запроса. Это идеальный прием для краткосрочных данных, которые должны быть предохранены между перенаправлениями.

Ниже приведен простой пример метода действия, в котором используется метод RedirectToAction():

// ...
public RedirectToRouteResult RedirectRoute()
{
    TempData["Message"] = "Привет";
    TempData["Date"] = DateTime.Now;
    return RedirectToAction("Index");
}
// ...

Когда этот метод обрабатывает запрос, он устанавливает значения в коллекции TempData, а затем перенаправляет браузер пользователя на метод действия Index() в том же самом контроллере. В целевом методе действия значения TempData могут быть прочитаны и переданы представлению:

// ...
public ViewResult Index()
{
    ViewBag.Message = TempData["Message"];
    ViewBag.Date = TempData["Date"];
    return View();
}
// ...

Более прямолинейный подход предусматривает чтение этих значений непосредственно в представлении:

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

День недели: @(((DateTime)TempData["Date"]).DayOfWeek)<br />
Сообщение из контроллера: @TempData["Message"]

Чтение значений в представлении избавляет от необходимости применения объекта ViewBag в методе действия. Тем не менее, результаты TempData потребуется приводить к соответствующим типам.

Чтобы извлечь из TempData значение, не помечая его как подлежащее удалению, следует воспользоваться методом Keep(), например:

// ...
ViewBag.Date = TempData.Keep("Date");
// ...

С помощью метода Keep() значение можно предохранить от удаления:

// ...
TempData.Keep("Date");
// ...

Метод Keep() не защищает значение навсегда. Если значение будет прочитано снова, оно будет помечено для удаления еще раз. Для сохранения значений, которые не должны удаляться после обработки запроса, применяйте вместо объекта TempData данные сеанса.

Пройди тесты
Лучший чат для C# программистов