Custom objects as arguments in controller methods defined by MapRoutes

蓝咒 提交于 2019-12-04 16:01:28

问题


Consider this MapRoute:

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

And it's controller method:

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
    }
}

Here's the URL scenarios

  • Home/Index/1 will go to case 1
  • Home/Index/1.html will go to case 1
  • Home/Index/1.json will go to case 2

This works well. But I hate checking for strings. How would implement an enum to be used as the resultFormat parameter in the controller method?


Some pseudo-code to explain the basic idea:

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
    }
);

The controller method signature:

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

回答1:


IMHO the response format is a cross cutting concern and it's not the controller to mess with it. I would suggest you to write an ActionFilter for this job:

[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
    }
}

which then simplifies your controller action a bit:

[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);
}

The ActionFilter is a reusable component that you could apply to other actions.




回答2:


Your pseudo code will work correctly. Default ModelBinder automatically converts the string in the url to Models.ResultFormat enum. But it would be better to make ActionFilter, as said Darin Dimitrov.




回答3:


This is the ActionFilter I came up with:

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
        };
    }
}


来源:https://stackoverflow.com/questions/1656767/custom-objects-as-arguments-in-controller-methods-defined-by-maproutes

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!