简体   繁体   English

MVC:如何在第一个路由参数中管理URL中的slahses

[英]MVC: How to manage slahses in URL int the first route parameter

I need to map two variables that could contain slashes, to a controller, in my ASP MVC application. 我需要在我的ASP MVC应用程序中将两个可能包含斜杠的变量映射到控制器。 Let's see this with an example. 让我们看一个例子。

在此输入图像描述

  • Repository and Path will be URL-encoded parameters. 存储库和路径将是URL编码的参数。
  • Repository can have 0 slashes or 1 slash as a maximum (rep or rep/module) 存储库可以有0个斜杠或1个斜杠作为最大值(rep或rep / module)
  • Path can have an arbitrary number of slashes. 路径可以有任意数量的斜杠。

For example these are valid URLs: 例如,这些是有效的URL:

http://mysite/rep/Items
http://mysite/rep/module/Items/foo/bar/file.c

Someone could give some suggestions about how to define this route? 有人可以就如何定义这条路线提出一些建议吗?

Looks like a custom route might cut the mustard: 看起来像自定义路线可能会削减芥末:

public class MyRoute: Route
{
    public MyRoute()
        : base("{*catchall}", new MvcRouteHandler())
    {
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var rd = base.GetRouteData(httpContext);
        if (rd == null)
        {
            // we do not have a match for {*catchall}, although this is very
            // unlikely to ever happen :-)
            return null;
        }

        var segments = httpContext.Request.Url.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
        if (segments.Length < 4)
        {
            // we do not have the minimum number of segments
            // in the url to have a match
            return null;
        }

        if (!string.Equals("items", segments[1], StringComparison.InvariantCultureIgnoreCase) &&
            !string.Equals("items", segments[2], StringComparison.InvariantCultureIgnoreCase))
        {
            // we couldn't find "items" at the expected position in the url
            return null;
        }

        // at this stage we know that we have a match and can start processing

        // Feel free to find a faster and more readable split here
        string repository = string.Join("/", segments.TakeWhile(segment => !string.Equals("items", segment, StringComparison.InvariantCultureIgnoreCase)));
        string path = string.Join("/", segments.Reverse().TakeWhile(segment => !string.Equals("items", segment, StringComparison.InvariantCultureIgnoreCase)).Reverse());

        rd.Values["controller"] = "items";
        rd.Values["action"] = "index";
        rd.Values["repository"] = repository;
        rd.Values["path"] = path;
        return rd;
    }
}

which could be registered before the standard routes: 可以在标准路线之前注册:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.Add("myRoute", new MyRoute());

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );
}

And if you intend to put arbitrary strings in the path portion of your urls I hope you are aware of the Zombie Operating Systems which might surprise you. 如果您打算在网址的路径部分放置任意字符串,我希望您知道可能会让您感到惊讶的Zombie Operating Systems

Finally, based in the answer of Darin Dimitrov , I implemented the following custom route, that solves my problem: 最后,根据Darin Dimitrov的答案,我实现了以下自定义路线,解决了我的问题:

public class RepositoryRoute : Route
{
    public RepositoryRoute(string name, string url, object defaults)
        : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler())
    {
        string moduleUrl = url.Replace(
            REPOSITORY_PARAMETER, REPOSITORY_PARAMETER + MODULE_PARAMETER);
        mModuleRoute = new Route(
            moduleUrl, new RouteValueDictionary(defaults), new MvcRouteHandler());
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        RouteData rd = mModuleRoute.GetRouteData(httpContext);

        if (rd == null)
            return base.GetRouteData(httpContext);

        if (!rd.Values.ContainsKey(MODULE))
            return rd;

        // set repository as repo/submodule format
        // if a submodule is present in the URL
        string repository = string.Format("{0}/{1}",
            rd.Values[REPOSITORY],
            rd.Values[MODULE]);

        rd.Values.Remove(MODULE);
        rd.Values[REPOSITORY] = repository;

        return rd;
    }

    Route mModuleRoute;

    const string REPOSITORY = "repository";
    const string MODULE = "module";

    const string REPOSITORY_PARAMETER = "{" + REPOSITORY + "}/"; // {repository}/
    const string MODULE_PARAMETER = "{" + MODULE + "}/"; // {module}/
}

Which is registered in the following way: 哪个是以下列方式注册的:

       routes.Add(new RepositoryRoute(
                        "Items",
                        "{repository}/Items/{*path}",
                        new { controller = "Items", action = "Index", path = "/" }
        ));

The route uses an internal route, that defines a module parameter, and if it's found, I concat it to the repository, and remove it. 该路由使用内部路由,该路由定义模块参数,如果找到,则将其连接到存储库,然后将其删除。 So mapping repository or repository/module is transparent. 因此映射存储库或存储库/模块是透明的。

If you cannot live with "old fashioned" style parameters and URL encoding, I think the only way you can achieve it is like this. 如果您不能使用“老式”样式参数和URL编码,我认为您可以实现它的唯一方法就是这样。 Note that this is not tested but should basically work. 请注意,这不是测试,但应该基本上工作。 Also I've put the controller name at the start and the Items separator is now essentially meaningless apart from acting as a delimiter. 此外,我将控制器名称放在开头,除了作为分隔符之外, Items分隔符现在基本上没有意义。

Controller 调节器

Create a controller with a single, parameterless method: 使用单个无参数方法创建控制器:

public class GetRepo : Controller
{
    public ActionResult Index()
    {
        //TBC
        return View();
    }
}

Routing 路由

Ensure routing is set up to allow http://www.example.com/GetRepo/ anything to route to your index method. 确保路由设置为允许http://www.example.com/GetRepo/ 任何内容路由到您的索引方法。

Note that the GetRepo part is important as otherwise what happens if your URL is www.example.com/blah/repo/items/other/stuff and you happen to have a controller called blah ? 请注意, GetRepo部分很重要,否则如果您的URL是www.example.com/blah/repo/items/other/stuff并且您碰巧有一个名为blah的控制器会发生什么?

The Magic 魔法

Now you deconstruct the Url manually by using Request.Url.AbsolutePath . 现在,您使用Request.Url.AbsolutePath手动解构Url。

var urlPath = Request.Url.AbsolutePath;

//Split the path by '/', drop the first entry as it's the action method
var parts = urlPath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)
    .Skip(1).ToList();

//Find where "Items" separator appears:
var posOfItems = parts.IndexOf("Items");

//Everything before separator is the repo:
var repo = string.Join("/", parts.Take(posOfItems));

//Everything after separator is the path:
var path = string.Join("/", parts.Skip(posOfItems + 1));

//Now do something with repo/path variables

You cannot do this mapping correctly, because of nature of this problem. 由于此问题的性质,您无法正确执行此映射。 Try to get a pencil and map following URL to repository and path: 尝试获取铅笔并将URL映射到存储库和路径:

http://mysite/rep/Items/Items/Items/Items/Items/Items/Items/Items/Items

There are multiple mappings: 有多个映射:

1) Repository = Items Path = Items/Items/Items/Items/Items/Items/Items 1)存储库=项目路径=项目/项目/项目/项目/项目/项目/项目

2) Repository = Items/Items Path = Items/Items/Items/Items/Items/Items 2)存储库=项目/项目路径=项目/项目/项目/项目/项目/项目

and so on.... 等等....

So either you should 所以要么你应该

  1. Pass parameters as query string 将参数传递为查询字符串
  2. Define multiple routes for each of format of repository (and add parts to full repository name in controller method) 为每种格式的存储库定义多个路由(并在控制器方法中将部件添加到完整存储库名称)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM