Возможная ошибка с маршрутизацией ASP.NET MVC 3?

Обычно я не ставил заголовок вроде этого в вопросе, но я уверен, что это ошибка (или по дизайну?)

Я создал совершенно новое веб-приложение ASP.NET MVC 3.

Затем я перешел на страницу /Home/About.

URL-адрес этой страницы:

http://localhost:51419/Home/About

Затем я изменил URL-адрес на это:

http://localhost:51419/(A(a))/Home/About

И страница работала? Рассматривая значения маршрута, controller = Home, Action = About. Он проигнорировал первую часть?

И если я посмотрю на все ссылки в источнике:

<link href="/(A(a))/Content/Site.css" rel="stylesheet" type="text/css" />
<script src="/(A(a))/Scripts/jquery-1.5.1.min.js" type="text/javascript"></script>
<script src="/(A(a))/Scripts/modernizr-1.7.min.js" type="text/javascript"></script>

<li><a href="/(A(a))/">Home</a></li>
<li><a href="/(A(a))/Home/About">About</a></li>

Посмотрите, как она поддерживала эту первую часть? Он, как движок маршрутизации, считает его частью домена или что-то еще?

У меня такое ощущение, что это регулярное выражение, потому что если я изменил URL-адрес на:

http://localhost:51419/(A(a))/Home/About

(например, изменили прописную букву A на нижний регистр)

Это 404's.

Может ли кто-нибудь пролить свет на это? Это ошибка или дизайн?

Ответы

Ответ 1

Это, как представляется, связано с Cookieless Sessions в конвейере ASP.NET. Он обрабатывает этот шаблон URL внутри CookielessHelper.cs(System.Web.Security) при обработке запроса:

    // This function is called for all requests -- it must be performant.
    //    In the common case (i.e. value not present in the URI, it must not
    //    look at the headers collection
    internal void RemoveCookielessValuesFromPath() 
    {
        // See if the path contains "/(XXXXX)/" 
        string   path      = _Context.Request.ClientFilePath.VirtualPathString; 
        // Optimize for the common case where there is no cookie
        if (path.IndexOf('(') == -1) { 
            return;
        }
        int      endPos    = path.LastIndexOf(")/", StringComparison.Ordinal);
        int      startPos  = (endPos > 2 ?  path.LastIndexOf("/(", endPos - 1, endPos, StringComparison.Ordinal) : -1); 

        if (startPos < 0) // pattern not found: common case, exit immediately 
            return; 

        if (_Headers == null) // Header should always be processed first 
            GetCookielessValuesFromHeader();

        // if the path contains a cookie, remove it
        if (IsValidHeader(path, startPos + 2, endPos)) 
        {
            // only set _Headers if not already set 
            if (_Headers == null) { 
                _Headers = path.Substring(startPos + 2, endPos - startPos - 2);
            } 
            // Rewrite the path
            path = path.Substring(0, startPos) + path.Substring(endPos+1);

            // remove cookie from ClientFilePath 
            _Context.Request.ClientFilePath = VirtualPath.CreateAbsolute(path);
            // get and append query string to path if it exists 
            string rawUrl = _Context.Request.RawUrl; 
            int qsIndex = rawUrl.IndexOf('?');
            if (qsIndex > -1) { 
                path = path + rawUrl.Substring(qsIndex);
            }
            // remove cookie from RawUrl
            _Context.Request.RawUrl = path; 

            if (!String.IsNullOrEmpty(_Headers)) { 
                _Context.Request.ValidateCookielessHeaderIfRequiredByConfig(_Headers); // ensure that the path doesn't contain invalid chars 
                _Context.Response.SetAppPathModifier("(" + _Headers + ")");

                // For Cassini and scenarios where aspnet_filter.dll is not used,
                // HttpRequest.FilePath also needs to have the cookie removed.
                string filePath = _Context.Request.FilePath;
                string newFilePath = _Context.Response.RemoveAppPathModifier(filePath); 
                if (!Object.ReferenceEquals(filePath, newFilePath)) {
                    _Context.RewritePath(VirtualPath.CreateAbsolute(newFilePath), 
                                         _Context.Request.PathInfoObject, 
                                         null /*newQueryString*/,
                                         false /*setClientFilePath*/); 
                }
            }
        }
    } 

Ваш шаблон соответствует этому:

    ///////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////// 
    // Make sure sub-string if of the pattern: A(XXXX)N(XXXXX)P(XXXXX) and so on. 
    static private bool IsValidHeader(string path, int startPos, int endPos)
    { 
        if (endPos - startPos < 3) // Minimum len is "X()"
            return false;

        while (startPos <= endPos - 3) { // Each iteration deals with one "A(XXXX)" pattern 

            if (path[startPos] < 'A' || path[startPos] > 'Z') // Make sure pattern starts with a capital letter 
                return false; 

            if (path[startPos + 1] != '(') // Make sure next char is '(' 
                return false;

            startPos += 2;
            bool found = false; 
            for (; startPos < endPos; startPos++) { // Find the ending ')'

                if (path[startPos] == ')') { // found it! 
                    startPos++; // Set position for the next pattern
                    found = true; 
                    break; // Break out of this for-loop.
                }

                if (path[startPos] == '/') { // Can't contain path separaters 
                    return false;
                } 
            } 
            if (!found)  {
                return false; // Ending ')' not found! 
            }
        }

        if (startPos < endPos) // All chars consumed? 
            return false;

        return true; 
    }

Ответ 2

Вам может потребоваться добавить IgnoreRoute к вашему сопоставлению маршрутов, но я признаю, что не знаю, какой формат предоставить, чтобы соответствовать всем вашим возможным пустым файлам.

Ответ 3

Я согласен с анализом @pjumble, но не с его решением.
Отключить аутентификацию без использования cookie для проверки подлинности формы или анонимной аутентификации.

Это предотвратит отказ пользователей cookie для проверки подлинности. Но кто бы ни заботился, у всех теперь есть файлы cookie, так как ни один современный веб-сайт не будет работать без него.

Добавить в web.config:

<anonymousIdentification enabled="false" />
<authentication mode="None" />

или

<anonymousIdentification enabled="true" cookieless="UseCookies" ... />
<authentication mode="Forms">
  <forms name="Auth" cookieless="UseCookies" ... />
</authentication>