自定义对象作为 MapRoutes 定义的控制器方法中的参数

发布于 2024-08-09 12:07:52 字数 1423 浏览 6 评论 0原文

考虑这个 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 will go to case 1
  • Home/Index/1.html will go to case 1
  • < code>Home/Index/1.json 将转到案例 2

这效果很好。但我讨厌检查字符串。如何实现一个enum来用作控制器方法中的resultFormat参数?


一些伪代码来解释基本思想:

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

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)

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

淡莣 2024-08-16 12:07:52

恕我直言,响应格式是一个横切问题,它不是控制器来搞乱它的。我建议你写一个 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 是一个可重用的组件,您可以将其应用于其他操作。

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.

稍尽春風 2024-08-16 12:07:52

您的伪代码将正常工作。默认 ModelBinder 自动将 url 中的字符串转换为 Models.ResultFormat 枚举。
但正如 Darin Dimitrov 所说,最好制作 ActionFilter。

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.

羁客 2024-08-16 12:07:52

这是我想出的 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
        };
    }
}

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
        };
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文