基于查询参数的ASP.NET MVC自定义路由

时间:2014-09-19 14:11:30

标签: c# asp.net .net asp.net-mvc asp.net-mvc-4

我们正在将遗留应用程序迁移到.NET MVC,并且必须保持旧URL的向后兼容性。考虑一个论坛应用程序,其中URL看起来像这样

http://example.com/forums?page=ForumsList - shows a list of all forums
http://example.com/forums?page=Threads&forumId=1 - shows list of threads in forum 1
http://example.com/forums?page=Posts&forumId=1&threadId=1 - shows posts in forum 1, thread 1.

我希望将这些相同的网址映射到我的控制器中的操作,即

public ActionResult ForumsList(){}
public ActionResult Threads(string forumId){}
public ActionResult Posts(string forumId, string threadId){}

不重复我们遗留系统中的维护噩梦

public ActionResult Index(string page){
    if (page == "Forums"){
         return Forums();
    }
    else if (page == "Threads){
         return Threads(Request.Params["forumId"]);
    }
    else if (page == "Posts"){
         return Posts(Request.Params["forumId"], Request.Params["threadId"]);
    }
    // oh, there's more.  a lot more.
}

我在自定义路由上找到的所有文档似乎都表明它只能解析URL的路径部分,而不是params。我尝试了以下,但无济于事:

        routes.MapRoute(
            name: "TestRoutes",
            url: "{controller}/?page={action}",
            defaults: new {controller = "Forums", action = "ForumsList"});

我能做的其他事情吗?

1 个答案:

答案 0 :(得分:1)

实际上,你不需要做任何特别的事情。默认情况下,modelbinder将尝试将查询字符串中的任何内容作为参数附加到action方法。您只需使用一个操作来处​​理/代理所有请求:

public class ForumsController : Controller
{
    public ActionResult Index(string page, int? forumId, int? threadId)
    {
        ...
    }
}

只有默认路线:

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

工作得很好。

网址的主要部分/forums将匹配ForumsController,并且将使用Index的默认操作,因为它未在网址中指定。 id参数是可选的。然后,将为操作的同名参数设置查询字符串中的每个项目。 forumIdthreadId参数需要为可为空的整数,以便只传递page参数,例如您的/forums?page=ForumsList网址。

虽然您可以简单地将应用程序设置为以这种方式工作,但我实际上建议将此特定操作保留为交换所。首先,我要创建自定义路线,因此您不必将Index行动用于此:

routes.MapRoute(
    name: "OldForums",
    url: "forums",
    defaults: new { controller = "OldForums", action = "RedirectToNewURLs" }
);

然后:

public class OldForumsController : Controller
{
    public ActionResult RedirectToNewURLs(string page, int? forumId, int? threadId)
    {
        if (page == "ForumList")
        {
            return RedirectToActionPermanent("Index", "Forums");
        }

        if (page == "Threads")
        {
            return RedirectToActionPermanent("Threads", "Forums", { id = forumId });
        }

        // etc
    }
}

这些重定向会指向您的新好论坛网址,例如/forums/{id}/threads。因此,如果用户输入旧网址,他们只会被重定向到新版本,并且所有新用户都会从一开始就被发送到好网址。

修改

实际上,我忘了指出这种方法的一个复杂因素是你实际上不能将URL /forums用于你的实际论坛索引。你可以使用类似/forums/all之类的东西来区分或只是做一些像单调化的东西,即forum。所有其他路由都可以使用forums前缀正常工作,因为只有该字符串本身才会与自定义路由匹配。例如,像forums/1这样的东西会落到默认路线上。