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

本文关键字:NET ASP MVC 自定义 路由 参数 于查询 查询 | 更新日期: 2023-09-27 18:22:31

我们正在将一个遗留应用程序迁移到.NETMVC,并且必须保持对旧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.

我想将这些相同的URL映射到我的控制器中的操作,即

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

我还能做什么吗?

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

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

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

会很好用的。

URL的主位/forums将与ForumsController匹配,并且将使用默认操作Index,因为它没有在URL中指定。id参数是可选的。然后,querystring中的每个项都将被设置为操作的相同命名参数。forumIdthreadId参数需要是可以为null的int,以便只为类似/forums?page=ForumsList URL的内容传递page参数。

虽然您可以简单地将应用程序设置为以这种方式工作,但我实际上建议将此特定操作保留为交换所。首先,我会创建一个自定义路由,这样您就不必将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
    }
}

这些重定向会指向你新的不错的论坛URL,比如/forums/{id}/threads。因此,如果用户通过旧的URL输入,他们只会被重定向到新版本,所有新用户从一开始就被发送到好的URL。

编辑

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