Пользовательские объекты как аргументы в методах контроллера, определенных MapRoutes - PullRequest
1 голос
/ 01 ноября 2009

Рассмотрим этот MapRoute:

MapRoute(
    "ResultFormat",
    "{controller}/{action}/{id}.{resultFormat}",
    new { controller = "Home", action = "Index", id = 0, resultFormat = "json" }
);

И это метод контроллера:

public ActionResult Index(Int32 id, String resultFormat)
{
    var dc = new Models.DataContext();

    var messages = from m in dc.Messages where m.MessageId == id select m;

    if (resultFormat == "json")
    {
        return Json(messages, JsonRequestBehavior.AllowGet); // case 2
    }
    else
    {
        return View(messages); // case 1
    }
}

Вот сценарии URL

  • Home/Index/1 перейдет к делу 1
  • Home/Index/1.html перейдет к делу 1
  • Home/Index/1.json перейдет к делу 2

Это хорошо работает. Но я ненавижу проверять наличие строк. Как реализовать enum для использования в качестве параметра resultFormat в методе контроллера?


Некоторый псевдокод, объясняющий основную идею:

namespace Models
{
    public enum ResponseType
    {
        HTML = 0,
        JSON = 1,
        Text = 2
    }
}

The MapRoute:

MapRoute(
    "ResultFormat",
    "{controller}/{action}/{id}.{resultFormat}",
    new {
        controller = "Home",
        action = "Index",
        id = 0,
        resultFormat = Models.ResultFormat.HTML
    }
);

Подпись метода контроллера:

public ActionResult Index(Int32 id, Models.ResultFormat resultFormat)

Ответы [ 3 ]

3 голосов
/ 01 ноября 2009

ИМХО, формат ответа - сквозная проблема, и с контроллером не стоит связываться с ним. Я бы предложил вам написать ActionFilter для этой работы:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public sealed class RespondToAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var resultFormat = filterContext.RouteData.Values["resultFormat"] as string ?? "html";
        ViewResult viewResult = filterContext.Result as ViewResult;
        if (viewResult == null)
        {
            // The controller action did not return a view, probably it redirected
            return;
        }
        var model = viewResult.ViewData.Model;
        if (string.Equals("json", resultFormat, StringComparison.OrdinalIgnoreCase))
        {
            filterContext.Result = new JsonResult { Data = model };
        }
        // TODO: you could add some other response types you would like to handle
    }
}

, что немного упрощает действие вашего контроллера:

[RespondTo]
public ActionResult Index(int id)
{
    var messages = new string[0];
    if (id > 0)
    {
        // TODO: Fetch messages from somewhere
        messages = new[] { "message1", "message2" };
    }
    return View(messages);
}

ActionFilter - это повторно используемый компонент, который можно применять к другим действиям.

0 голосов
/ 02 ноября 2009

Это ActionFilter, который я придумал:

public sealed class AlternateOutputAttribute :
                    ActionFilterAttribute, IActionFilter
{
    void IActionFilter.OnActionExecuted(ActionExecutedContext aec)
    {
        ViewResult vr = aec.Result as ViewResult;

        if (vr == null) return;

        var aof = aec.RouteData.Values["alternateOutputFormat"] as String;

        if (aof == "json") aec.Result = new JsonResult
        {
            JsonRequestBehavior = JsonRequestBehavior.AllowGet,
            Data = vr.ViewData.Model,
            ContentType = "application/json",
            ContentEncoding = Encoding.UTF8
        };
    }
}
0 голосов
/ 01 ноября 2009

Ваш псевдокод будет работать правильно. По умолчанию ModelBinder автоматически преобразует строку в URL в перечисление Models.ResultFormat. Но было бы лучше сделать ActionFilter, как сказал Дарин Димитров.

...