Как стать автором
Обновить

ActionResult на все случаи жизни

Время на прочтение4 мин
Количество просмотров15K
Чем дальше в «лес», тем толще MVC framework. Вот на днях вышел preview второй версии. Бегло ознакомившись с возможностями, сильно порадовался. Но затем вспомнил о простой и очень интересной возможности RoR, которой пока нет (даже в планах) в MS MVC.
Речь идёт о возможности указать «расширение» для Action метода и, тем самым, определить тип View.


Для начала, рассмотрим пример. Запрос ht_p://localhost/Blog/Comments/0 возвращает html страницу, содержащую список коментариев к статье номер 0 в блоге. В данном случае Blog — это имя контроллера, Comments — название метода, 0 — идентификатор записи.
Код контроллера будет выглядеть примерно так:

public class BlogController : Controller
{
  public ActionResult Comments(int id)
  {
    var strings = new[] { "Один", "Два", "Три" }; // Представим, что это комментарии
    return View(strings);
  }
}

* This source code was highlighted with Source Code Highlighter.


Тут всё просто!

А как поступить, если нам необходимо в добавок к странице html получить список коментариев сериализованный в json? Самый простой способ, это создать ещё один метод, назвать его, скажем, CommentsJson, и явно вызвать метод Json(). Например, так:

public ActionResult CommentsJson(int id)
{
  var strings = new[] { "Один", "Два", "Три" }; // Представим, что это комментарии
  return Json(strings);
}


* This source code was highlighted with Source Code Highlighter.


Теперь мы можем получить json список по адресу ht_p://localhost/Blog/CommentsJson/0.

Задача решена, расходимся? Скажу прямо, меня такой код не устраивает. Во первых, дублирование, во вторых, мне не нравится url, который придётся использовать в последствии, в третьих, нет возможности использовать схему в других методах.

В таком случае, нам на помощь придёт возможность создать свой собственный ActionResult. Постараемся решить задачу «в лоб». Изменим метод Comments следующим образом:

public class BlogController : Controller
{
  public ActionResult Comments(int id)
  {
    var strings = new[] { "Один", "Два", "Три" }; // Представим, что это комментарии
    return new ComplexResult
        {
          Json = () => Json(strings),
          View = () => View(strings)
        };
  }
}

* This source code was highlighted with Source Code Highlighter.


Как видно, класс ComplexResult должен быть унаследован от ActionResult.
Попробуем реализовать его так:

public class ComplexResult : ActionResult
{
  public override void ExecuteResult(ControllerContext context)
  {
    object value;
    if (context.RouteData.Values.TryGetValue("result", out value))
    {
      var result = value.ToString();
      if (result.Equals("json", StringComparison.InvariantCultureIgnoreCase))
      {
        Json().ExecuteResult(context);
        return;
      }
    }

    View().ExecuteResult(context);
  }

  public Func<JsonResult> Json { get; set; }

  public Func<ViewResult> View { get; set; }
}


* This source code was highlighted with Source Code Highlighter.


Здесь надо объяснить: при обработке результата мы проверяем наличие строки «result» в списке значений, полученных при разборе запроса. Убеждаемся, что пользователь отправил строчку «json» и запускаем JsonResult, заранее определённый пользователем. Если строка не найдена или она не равна «json», запускаем ViewResult. Логика довольно проста.

Параллельно с этим добавим строки в настройку routes:

public static void RegisterRoutes(RouteCollection routes)
{
  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

  // Наш route должен стоять перед default
  routes.MapRoute("Result", "{controller}/{action}.{result}/{id}",
    new { controller = "Home", action = "Index", id = "", result = "view" });

  routes.MapRoute("Default", "{controller}/{action}/{id}",
          new { controller = "Home", action = "Index", id = "" });
}

* This source code was highlighted with Source Code Highlighter.


Теперь мы имеем возможность получить объект json явно указав это в строке запроса ht_p://localhost/Blog/Comments.json/0. Или ht_p://localhost/Blog/Comments/0 — когда нам нужен html. И самое главное, можем легко использовать ComplexResult в других методах, не засоряя код повторяющимися методами.

Фантазия подсказывает, что подобным образом можно добавить в ComplexResult другие стандартные или нестандартные (XmlResult, RssResult и пр.) ActionResults. Возможно, нас ждёт это в следующих версиях ASP.NET MVC.

Код написан, теперь оправдания: рекомендую использовать код, написанный в этой заметке только для ознакомления с возможностями MVC framework, очевидно — он не безопасен, для упрощения опущены некоторые проверки и условия.

Спасибо за внимание.
Теги:
Хабы:
0
Комментарии9

Публикации