Канонизировать URL в нижний регистр, не нарушая файловую систему или культуру? - PullRequest
8 голосов
/ 24 января 2012

Канонизация URL-адресов в нижний регистр

Я хочу написать модуль HTTP, который преобразует URL-адреса в нижний регистр.Моя первая попытка игнорировала международные наборы символов и прекрасно работает:

// Convert URL virtual path to lowercase
string lowercase = context.Request.FilePath.ToLowerInvariant();

// If anything changed then issue 301 Permanent Redirect
if (!lowercase.Equals(context.Request.FilePath, StringComparison.Ordinal))
{
    context.Response.RedirectPermanent(...lowercase URL...);
}

Тест Турции (международные культуры):

Но как насчет культур, отличных от en-US?Я сослался на Turkey Test , чтобы придумать тестовый URL:

http://example.com/Iıİi

Этот маленький коварный драгоценный камень разрушает любое представление о том, что преобразование регистра в URL-адресах просто!Его строчные и прописные версии, соответственно, следующие:

http://example.com/ııii
http://example.com/IIİİ

Для преобразования регистра для работы с турецкими URL-адресами мне сначала пришлось установить текущую культуру ASP.NET на турецкий язык:

<system.web>
    <globalization culture="tr-TR" />
</system.web>

Затем мне пришлось изменить свой код, чтобы использовать текущую культуру для преобразования регистра:

// Convert URL virtual path to lowercase
string lowercase = context.Request.FilePath.ToLower(CultureInfo.CurrentCulture);

// If anything changed then issue 301 Permanent Redirect
if (!lowercase.Equals(context.Request.FilePath, StringComparison.Ordinal))
{
    context.Response.RedirectPermanent(...);
}

Но подождите!Будет ли StringComparison.Ordinal все еще работать?Или я должен использовать StringComparison.CurrentCulture?Я действительно не уверен ни в одном!

Имена файлов: он получает НАМНОГО ХОРОШО!

Даже если вышеперечисленное работает, используя текущую культуру для конверсий случаев ломает NTFS файловая система!Допустим, у меня есть статический файл с именем Iıİi.html:

http://example.com/Iıİi.html

Несмотря на то, что файловая система Windows нечувствительна к регистру, она не использует языковую культуру.Преобразование вышеуказанного URL-адреса в нижний регистр приводит к значению 404 Not Found, поскольку файловая система не считает два имени равными:

http://example.com/ııii.html

Правильно ли преобразован регистр имен файлов?КТО ЗНАЕТ?!

В статье MSDN, Рекомендации по использованию строк в .NET Framework , есть примечание (примерно в середине статьи):

Примечание: Строковое поведение файловой системы, разделов и значений реестра и переменных среды лучше всего представлено в виде StringComparison.OrdinalIgnoreCase.

А? Лучше всего представлено ??? Это лучшее, что мы можем сделать в C #?Итак, каково правильное преобразование регистра в файловую систему? Кто знает? !!? Все, что мы можем сказать, - это то, что сравнение строк с использованием приведенного выше кода, вероятно, будет работать МОСТЕ времени.

Резюме: два преобразования: статические / динамические URL-адреса

  1. Итак, мы видели, что статические URL --- URL-адреса, имеющие путь к файлу, который соответствует реальному каталогу / файлу в файловой системе - должны использовать неизвестное преобразование, котороетолько «лучше всего представлен» StringComparison.OrdinalIgnoreCase.И, пожалуйста, обратите внимание, что метода string.ToLowerOrdinal() нет, поэтому очень трудно точно определить, какой именно перевод прецедентов соответствует сравнению строки OrdinalIgnoreCase.Использование string.ToLowerInvariant(), вероятно, является лучшим выбором, но это нарушает языковую культуру.
  2. С другой стороны, динамические URL-адреса --- URL-адреса с путем к файлу, который не соответствует реальному файлуна диске (который отображается в вашем приложении) --- может использовать string.ToLower(CultureInfo.CurrentCulture), но это нарушает сопоставление файловой системы, и несколько неясно, какие существуют крайние случаи, которые могут нарушить эту стратегию.

Таким образомПохоже, что преобразование сначала требует определения того, является ли URL-адрес статическим или динамическим, прежде чем выбрать один из двух методов преобразования.Для статических URL существует неопределенность, как изменить регистр, не нарушая файловую систему Windows.Для динамических URL-адресов сомнительно, что преобразование регистра с использованием культуры аналогично нарушит URL-адрес.

Вот так!У кого-нибудь есть решение этой проблемы?Или мне просто закрыть глаза и притвориться, что все в ASCII?

Ответы [ 3 ]

5 голосов
/ 24 января 2012

Здесь я бы поставил под сомнение предположение, что при попытке автоматического преобразования URL-адресов в нижний регистр есть какая-либо утилита.

Независимо от того, является ли полный URL-адрес чувствительным к регистру или нет, полностью зависит отвеб-сервер, каркас веб-приложений и базовая файловая система.

Вы только гарантируете нечувствительность к регистру в схеме (http://, и т. д.) и части имени хоста в URL.И помните, что не все схемы URL (например, file и news) даже включают имя хоста.

Все остальное может быть чувствительно к регистру для сервера, включая пути (/), имена файлов, запросы (?), фрагменты (#) и информация о полномочиях (имена пользователей / пароли перед @ в mailto, http, ftp и некоторые другие схемы).

2 голосов
/ 24 января 2012

У вас есть несовместимые цели.

  1. Имейте культурно-чувствительное снижение регистра. Если турецкий язык кажется плохим, вы не хотите знать о некоторых грузинских сценариях, не говоря уже о том, что ß либо в верхнем регистре до SS, либо реже в SZ - в любом случае для полного регистра - складывая, где lower("ß") будет соответствовать lower(upper("ß")), вы должны считать его эквивалентным хотя бы одной из этих двухсимвольных последовательностей. Как правило, мы стремимся к складыванию, а не к падению, если это возможно (здесь невозможно).

  2. Используйте это в не зависящем от культуры контексте. URI в конечном итоге являются непрозрачными строками. То, что они могут иметь понятное для человека понимание, полезно как для программистов, так и для пользователей, поисковых систем и маркетологов, но их конечная задача - определить ресурс путем прямого сравнения с учетом регистра.

  3. Сопоставьте это с NTFS, которая имеет сохраняющую регистр чувствительность к регистру на основе отображений в файле $ UpCase, что она делает путем сравнения форм слов в верхнем регистре (в по крайней мере, не нужно решать, Σ в нижнем регистре до σ или ς, без учета культурных особенностей.

  4. Предположительно преуспевают с точки зрения SEO и читабельности человека. Это вполне может быть частью вашей первоначальной цели, но пока это не очень легко, слишком легко прочитать, но и для других людей, и для машин это не так. Раскладной чехол теряет информацию.

Я предлагаю другой подход.

  1. Начните с вашей стартовой строки, какой бы она ни была и откуда она взялась (имя файла NTFS, запись в базе данных, привязка HttpHandler в web.config). Имейте это как свою каноническую форму. Во что бы то ни стало, есть правила, согласно которым люди должны создавать эти строки в соответствии с какой-либо канонической формой и, возможно, применять их там, где это возможно, но если что-то проскальзывает, что нарушает ваши правила, то примите это как официальное каноническое имя для этого ресурса, независимо от того, насколько вам это не нравится.

  2. Насколько это возможно, каноническое имя должно быть единственным, «видимым» внешним миром. Это может быть реализовано программно или просто в качестве наилучшей практики, поскольку канонизация после факта с 301-м не решит тот факт, что внешние объекты не знают, что вы делаете это, пока они не разыграют URI.

  3. Когда запрос получен, проверьте его в соответствии с тем, как он будет использоваться. Следовательно, хотя вы можете использовать определенную культуру (или нет) для тех случаев, когда вы выполняете поиск ресурсов самостоятельно, с помощью так называемых «статических» URI, ваша логика может сознательно следовать за логикой NTFS, просто используя NTFS для выполнения работа:

    1. Найти сопоставленный файл, игнорируя вопрос чувствительности к регистру.
    2. Если не совпадает, то 404, кого волнует дело?
    3. Если найти, выполните порядковое сравнение с учетом регистра, если оно не совпадает, то 301 с отображением с учетом регистра.
    4. В противном случае действуйте как обычно.

Edit:

В некотором смысле вопрос доменных имен более сложен. Правила для IDN должны охватывать больше вопросов и меньше места для маневра. Тем не менее, это также проще, по крайней мере, с точки зрения канонизации.

(Я собираюсь проигнорировать канонизацию того, используется ли www. и т. Д., Хотя я предполагаю, что это часть той же работы здесь, это раздвигает сферу, и мы могли бы в конечном итоге написать книгу между нами, если мы не останавливаемся где-то:)

У ИДИ есть свои собственные правила канонизации (и некоторые другие формы нормализации), определенные в RFC 3491. Если вы собираетесь канонизировать доменные имена в случае, следуйте этому.

Делает красиво и просто ответить, не так ли? :)

Здесь также меньше давления, поскольку поисковые системы должны признать, что http://example.net/thisisapath и http://example.net/thisIsAPath могут быть одним и тем же ресурсом, но они также должны признать, что они могут отличаться, и именно здесь все SEO Преимущество канонизации одного из них (неважно, какое) исходит от.

Тем не менее, они знают, что example.net и EXAMPLE.NET не могут быть разными сайтами, поэтому у SEO мало преимуществ в том, чтобы убедиться, что они одинаковы (но это хорошо для таких вещей, как кэши и списки истории, которые не делают что сами прыгают). Конечно, проблема остается в том факте, что www.example.net или даже maAndPasExampleEmporium.us может быть тем же сайтом, но опять же, это уходит от проблем со случаем.

Существует также простое дело, что в большинстве случаев нам никогда не приходится иметь дело с более чем парой дюжин различных доменов, поэтому иногда работаем больше, а не умнее (т.е. просто убедитесь, что все они настроены правильно и не сделать что-нибудь программно!) может сделать свое дело.

Последнее замечание: важно не канонизировать сторонний URI. Вы можете закончить тем, что сломаете вещи, если вы измените путь (они могут не относиться к нему без учета регистра), и вы, по крайней мере, можете нарушить их немного отличную канонизацию. Лучше оставить их как есть.

0 голосов
/ 24 января 2012

Во-первых никогда использовать преобразования регистра для сравнения строк. Он без необходимости распределяет строку, имеет излишне малое влияние на производительность, может привести к ObjectReferenceException, если значение равно нулю, и может привести к неправильному сравнению.

Если это достаточно важно для вас, я бы вручную просмотрел файловую систему и использовал ваши собственные сравнения с каждым именем файла / каталога. Вы должны быть в состоянии использовать HTTP-заголовок Accept-Language или Accept-Encoding (если в него включена культура), чтобы найти подходящую культуру для использования. Если у вас есть CultureInfo, вы можете использовать его для сравнения строк:

var ci = CultureInfo.CurrentCulture; // Use Accept-Language to derive this.
ci.CompareInfo.Compare("The URL", "the url", CompareOptions.IgnoreCase);

Я бы делал это только по HTTP 404; обработчик HTTP 404 будет искать соответствующий файл, а затем HTTP 301 пользователя по URL-адресу с правильным регистром (поскольку обход файловой системы вручную может стать дорогим).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...