ПО промежуточного слоя для переопределения URL-адресов в ASP.NET Core

Авторы: Кирк Ларкин (Kirk Larkin) и Рик Андерсон (Rick Anderson)

Эта статья содержит вводные сведения о переопределении URL-адресов и инструкции по использованию ПО промежуточного слоя для переопределения URL-адресов в приложениях ASP.NET Core.

Переопределение URL-адресов представляет собой изменение URL-адресов запросов на основе одного или нескольких предопределенных правил. Переопределение URL-адресов создает абстракцию между расположениями ресурсов и их адресами, позволяя убрать тесную связь между ними. Существует несколько сценариев, где может пригодиться переопределение URL-адресов:

  • Временное или постоянное перемещение или замещение ресурсов сервера с сохранением стабильных указателей для этих ресурсов.
  • Разделение обработки запросов между разными приложениями или разными областями одного приложения.
  • Удаление, добавление или переупорядочение сегментов URL-адресов для входящих запросов.
  • Оптимизация общедоступных URL-адресов в целях оптимизации для поисковых систем (SEO).
  • Разрешение использования понятных общедоступных URL-адресов, чтобы помочь посетителям прогнозировать содержимое, возвращаемое в результате запроса ресурса.
  • Перенаправление небезопасных запросов на защищенные конечные точки.
  • Запрет вставки прямых ссылок, когда внешний сайт использует статический ресурс, размещенный на другом сайте, привязывая ресурс к своему содержимому.

Переопределение URL-адресов может снижать производительность приложения. Ограничьте количество и сложность правил.

Перенаправление и переопределение URL-адресов

Разница между перенаправлением и переопределением URL-адресов может показаться незначительной, но она оказывает существенное влияние на предоставление ресурсов клиентам. ПО промежуточного слоя для переопределения URL-адресов в ASP.NET Core удовлетворяет потребность в обеих этих функциях.

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

Если /resourceперенаправляется на /different-resource, сервер отвечает, что клиенту следует получить ресурс в /different-resource с кодом состояния, указывающим, что такое перенаправление является временным или постоянным.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

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

  • Код состояния 301 - Moved Permanently используется, когда ресурс имеет новый постоянный URL-адрес и все последующие запросы для этого ресурса должны использовать новый URL-адрес. Клиент может кэшировать и повторно использовать отклик при получении кода состояния 301.

  • Код 302 - Found состояния используется, когда перенаправление является временным или обычно подлежит изменению. Код состояния 302 указывает клиенту, что не нужно сохранять URL-адрес и использовать его в будущем.

Дополнительные сведения о кодах состояния см. в статье RFC 9110: определения кода состояния.

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

Когда /resourceпереопределяется на /different-resource, сервер получает и запрашивает ресурс внутри в /different-resource.

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

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

Пример приложения с переопределением URL-адресов

Ознакомьтесь с функциями ПО промежуточного слоя переопределения URL-адресов на примере приложения. Это приложение применяет правила перенаправления и переопределения и показывает перенаправленный или переопределенный URL-адрес для нескольких сценариев.

Условия использования ПО промежуточного слоя для переопределения URL-адресов

Используйте ПО промежуточного слоя для переопределения URL-адресов, если невозможно использовать следующие подходы:

Используйте ПО промежуточного слоя, когда приложение размещается на сервере HTTP.sys.

Основные причины для использования переопределения URL-адресов на основе сервера в IIS, Apache и Nginx:

  • ПО промежуточного слоя не поддерживает все функции этих модулей.

    Некоторые функции серверных модулей не работают с проектами ASP.NET Core, например ограничения IsFile и IsDirectory для модуля переопределения IIS. В этих случаях следует использовать ПО промежуточного слоя.

  • Производительность ПО промежуточного слоя, скорее всего, не соответствует производительности модулей.

    Тестирование производительности — это единственный способ узнать точно, какой подход больше всего снижает производительность и является ли такое снижение незначительным.

Расширение и параметры

Задайте правила переопределения и перенаправления URL-адресов, создав экземпляр класса RewriteOptions для каждого правила с помощью методов расширения. Объедините в цепочку несколько правил в том порядке, в котором они должны быть обработаны. RewriteOptions передаются в ПО промежуточного слоя для переопределения URL-адресов по мере его добавления в конвейер запросов с помощью UseRewriter:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

В приведенном выше коде MethodRules — это определяемый пользователем класс. Дополнительные сведения см. в разделе RewriteRules.cs этой статьи.

Перенаправление не www на www

Три параметра разрешают приложению перенаправлять запросы, отличные от www, на www:

  • AddRedirectToWwwPermanent: постоянно перенаправляет запрос на www поддомен, если запрос не является.www Перенаправляет с кодом состояния Status308PermanentRedirect.

  • AddRedirectToWww: перенаправьте запрос на www поддомен, если входящий запрос неwww является. Перенаправляет с кодом состояния Status307TemporaryRedirect. Перегрузка позволяет предоставить код состояния для ответа. Используйте поле класса StatusCodes для назначения кода состояния.

Перенаправление URL-адреса

Используйте AddRedirect для перенаправления запросов. Первый параметр содержит регулярное выражение .NET, соответствующее пути входящего URL-адреса. Второй параметр является строкой замены. Третий параметр (при его наличии) указывает код состояния. Если код состояния не задан, по умолчанию используется код 302 — Found (найдено), указывающий, что ресурс временно перемещен или заменен.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

В браузере с включенными средствами разработчика выполните запрос для примера приложения по пути /redirect-rule/1234/5678. Регулярное выражение соответствует пути запроса в redirect-rule/(.*), и этот путь изменяется на /redirected/1234/5678. URL-адрес перенаправления отправляется обратно клиенту с кодом состояния 302 (найдено). Браузер выполняет новый запрос на URL-адрес перенаправления, отображаемый в адресной строке. Поскольку в примере приложения нет правил, соответствующих URL-адресу перенаправления:

  • Второй запрос получает от приложения ответ 200 (ОК).
  • Текст ответа показывает URL-адрес перенаправления.

При перенаправлении URL-адреса используется круговое обращение к серверу.

Предупреждение

Соблюдайте осторожность при задании правил перенаправления. Они оцениваются для каждого запроса, отправляемого в приложение, даже после перенаправления. Можно легко случайно создать бесконечный цикл перенаправлений.

Часть выражения, заключенная в скобки, называется группой записи. Точка (.) в выражении означает совпадение с любым символом. Звездочка (*) означает совпадение с предыдущим символом ноль или более раз. Таким образом, два последних сегмента пути URL-адреса, 1234/5678, перехватываются группой записи (.*). Любое значение, указанное в URL-адресе запроса после redirect-rule/, перехватывается этой группой записи.

В строке замены группы записи внедряются с помощью знака доллара ($), за которым следует порядковый номер записи. Первое значение группы записи можно получить с помощью $1, а второе — с помощью $2. После этого эти значения по очереди используются для групп записи в регулярном выражении. В регулярном выражении правила перенаправления в redirect-rule/(.*) присутствует всего одна группа записи, поэтому в строку замены внедряется тоже одна строка — $1. Когда применяется правило, URL-адрес становится /redirected/1234/5678.

Попробуйте использовать /redirect-rule/1234/5678 средства браузера на вкладке "Сеть".

Перенаправление URL-адресов на защищенную конечную точку

Используйте AddRedirectToHttps, чтобы перенаправлять HTTP-запросы на тот же узел и путь с использованием протокола HTTPS. Если код состояния не указан, ПО промежуточного слоя по умолчанию использует значение 302 (найдено). Если порт не указан:

  • ПО промежуточного слоя по умолчанию устанавливается на null.
  • Схема меняется на https (протокол HTTPS), и клиент обращается к ресурсу через порт 443.

В следующем примере показано, как задать код 301 - Moved Permanently состояния и изменить порт на порт HTTPS, используемый Kestrel на localhost. В рабочей среде порт HTTPS имеет значение NULL:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

int? localhostHTTPSport = null;
if (app.Environment.IsDevelopment())
{
    localhostHTTPSport = Int32.Parse(Environment.GetEnvironmentVariable(
                   "ASPNETCORE_URLS")!.Split(new Char[] { ':', ';' })[2]);
}

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        // localhostHTTPport not needed for production, used only with localhost.
        .AddRedirectToHttps(301, localhostHTTPSport)
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Используйте AddRedirectToHttpsPermanent, чтобы перенаправить небезопасные запросы на тот же узел и путь с использованием безопасного протокола HTTPS через порт 443. ПО промежуточного слоя задает код состояния 301 - Moved Permanently.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Примечание.

При перенаправлении на защищенную конечную точку без дополнительных правил перенаправления рекомендуется использовать ПО промежуточного слоя перенаправления на HTTPS. Дополнительные сведения см. в разделе Принудительное использование HTTPS.

В этом примере приложения показано, как использовать AddRedirectToHttps или AddRedirectToHttpsPermanent. Выполните небезопасный HTTP-запрос к приложению по адресу http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz. При проверке перенаправления с HTTP на HTTPS с использованием localhost:

  • Используйте URL-адрес HTTP с портом, который отличается от порта URL-адреса HTTPS. URL-адрес HTTP находится в файле Properties/launchSettings.json.
  • При удалении s из https://localhost/{port} происходит сбой, так как localhost не отвечает на HTTP на порту HTTPS.

На следующем рисунке показано средство браузера, вызываемое с помощью клавиши F12, для запрашивания http://redirect6.azurewebsites.net/iis-rules-rewrite/xyz с использованием приведенного выше кода.

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS

Переопределение URL-адресов

Используйте AddRewrite, чтобы создать правило для переопределения URL-адресов. Первый параметр содержит регулярное выражение, соответствующее пути входящего URL-адреса. Второй параметр является строкой замены. Третий параметр skipRemainingRules: {true|false} сообщает ПО промежуточного слоя, нужно ли пропустить дополнительные правила переопределения, если применяется текущее правило.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Попробуйте повторить запрос к https://redirect6.azurewebsites.net/rewrite-rule/1234/5678.

Знак вставки (^) в начале выражение означает, что сопоставление начинается с начала URL-адреса.

В предыдущем примере с правилом перенаправления redirect-rule/(.*) в начале регулярного выражения нет символа ^. Таким образом, для успешного совпадения до redirect-rule/ в пути могут стоять любые символы.

Путь Поиск совпадений (Match)
/redirect-rule/1234/5678 Да
/my-cool-redirect-rule/1234/5678 Да
/anotherredirect-rule/1234/5678 Да

Правило переопределения ^rewrite-rule/(\d+)/(\d+) соответствует путям, только если они начинаются с rewrite-rule/. В следующей таблице обратите внимание на разницу в сопоставлении.

Путь Поиск совпадений (Match)
/rewrite-rule/1234/5678 Да
/my-cool-rewrite-rule/1234/5678 No
/anotherrewrite-rule/1234/5678 No

После части ^rewrite-rule/ выражения стоят две группы записи (\d+)/(\d+). \d означает соответствие цифре (числу). Знак "плюс" (+) означает соответствие одному или нескольким предшествующим символам. Таким образом, URL-адрес должен содержать число, за которым идет прямая косая черта, за которой идет другое число. Эти группы записи внедряются в переопределенный URL-адрес как $1 и $2. Строка замены для правила переопределения помещает группы записи в строку запроса. Запрошенный путь /rewrite-rule/1234/5678 переопределяется, чтобы ресурс можно было получить по адресу /rewritten?var1=1234&var2=5678. Если в исходном запросе присутствует строка запроса, она сохраняется при переопределении URL-адреса.

Круговое обращение к серверу для возврата ресурса не выполняется. Если ресурс существует, он извлекается и возвращается клиенту с кодом состояния 200 (ОК). Так как клиент не перенаправляется, URL-адрес в адресной строке браузера не изменяется. Клиенты не могут узнать, что на сервере произошла операция переопределения URL-адреса.

Советы по повышению производительности при перезаписи URL-адресов и перенаправлении

Чтобы максимально ускорить отклик:

  • расположите правила переопределения в порядке от наиболее к наименее часто используемому;
  • Используйте skipRemainingRules: true при любой возможности, так как правила сопоставления потребляют много ресурсов и ухудшают время отклика приложения. пропускайте обработку оставшихся правил, когда совпадение найдено и обработка дополнительных правил не требуется.

Предупреждение

Злонамеренный пользователь может предоставить дорогостоящие для обработки входные данные для RegularExpressions, что делает возможными атаки типа "отказ в обслуживании". API платформы ASP.NET Core, использующие RegularExpressions, передают время ожидания. Например, классы RedirectRule и RewriteRule передаются в течение одного секунды.

Apache mod_rewrite

Для применения правил mod_rewrite Apache можно использовать AddApacheModRewrite. Убедитесь, что файл правил развертывается вместе с приложением. Дополнительные сведения и примеры правил mod_rewrite см. в статье о правилах mod_rewrite Apache.

StreamReader используется для чтения правил из файла ApacheModRewrite.txt:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Пример приложения перенаправляет запросы из /apache-mod-rules-redirect/(.\*) в /redirected?id=$1. Отклик имеет код состояния 302 (найдено).

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

Попробуйте повторить запрос к https://redirect6.azurewebsites.net/apache-mod-rules-redirect/1234.

ПО промежуточного слоя Apache поддерживает следующие переменные сервера в mod_rewrite Apache:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

Правила модуля переопределения URL-адресов для IIS

Чтобы использовать тот же набор правил, который применяется к модулю переопределения URL-адресов для IIS, используйте AddIISUrlRewrite. Убедитесь, что файл правил развертывается вместе с приложением. Не указывайте ПО промежуточного слоя использовать файл приложения web.config при работе в службах IIS Windows Server. В IIS эти правила нужно хранить за пределами файла приложения web.config, чтобы предотвратить конфликты с модулем переопределения для IIS. Дополнительные сведения и примеры правил модуля переопределения URL-адресов для IIS см. в разделах Использование модуля переопределения URL-адресов 2.0 и Справочник по конфигурации модуля переопределения URL-адресов.

Используется StreamReader для чтения правил из IISUrlRewrite.xml файла правил:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Пример приложения переопределяет запросы с /iis-rules-rewrite/(.*) на /rewritten?id=$1. Клиенту отправляется отклик с кодом состояния 200 (ОК).

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

Попробуйте повторить запрос к https://redirect6.azurewebsites.net/iis-rules-rewrite/xyz.

Приложения с активным модулем переопределения IIS с настроенными правилами на уровне сервера, которые нежелательным образом влияют на приложение:

  • Рассмотрите возможность отключения модуля переопределения IIS для приложения.
  • Дополнительные сведения см. в разделе Отключение модулей IIS.

Неподдерживаемые функции

ПО промежуточного слоя не поддерживает следующие функции в модуле переопределения URL-адресов для служб IIS:

  • Правила для исходящих подключений
  • Пользовательские переменные сервера
  • Подстановочные знаки
  • LogRewrittenUrl

Поддерживаемые переменные сервера

ПО промежуточного слоя поддерживает следующие переменные сервера в модуле переопределения URL-адресов для IIS:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

IFileProvider можно получить с помощью PhysicalFileProvider. Такой подход позволяет более гибко задавать расположение для файлов с правилами переопределения. Убедитесь, что эти файлы развертываются на сервере по указанному пути.

var fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

Правило, основанное на методе

Используйте Add, чтобы реализовать настраиваемую логику правил в методе. Add предоставляет RewriteContext, делая возможным использование HttpContext в методах переопределения. Свойство RewriteContext.Result определяет, как осуществляется дополнительная обработка в конвейере. Установите значение одного из полей RuleResult из следующей таблицы.

Результат перезаписи контекста Действие
RuleResult.ContinueRules (по умолчанию) Продолжение применения правил.
RuleResult.EndResponse Остановка применения правил и отправка отклика.
RuleResult.SkipRemainingRules Остановка применения правил и отправка контекста в следующий компонент ПО промежуточного слоя.
using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Пример приложения демонстрирует метод, который перенаправляет запросы на пути, которые заканчиваются .xml. Когда запрос выполняется для /file.xml:

  • Запрос перенаправляется в /xmlfiles/file.xml.
  • Код состояния получает значение 301 - Moved Permanently. Когда браузер отправляет новый запрос /xmlfiles/file.xmlна по промежуточному слоя статическим файлам, он обслуживает файл клиенту из папки wwwroot/xmlfiles . Для перенаправления необходимо явно указать код состояния ответа. В противном случае возвращается код состояния 200 (ОК) и перенаправление на стороне клиента не происходит.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")) ||
        request.Path.Value==null)
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

Этот подход также переопределяет запросы. В примере приложения показано переопределение пути для любого запроса текстового файла, чтобы отправить текстовый файл file.txt из папки wwwroot. ПО промежуточного слоя статических файлов предоставляет файл по обновленному пути запроса:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value != null &&
        request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

Правило, основанное на IRule

Используйте Add, чтобы использовать собственную логику правил в классе, реализующем интерфейс IRule. IRule позволяет более гибко применять правила, основанные на методах. Класс реализации может включать конструктор, который позволяет передавать параметры для метода ApplyRule.

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)
        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

Значения параметров в примере приложения для extension и newPath проверяются на соответствие нескольким условиям. В extension должно содержаться значение .png, .jpg или .gif. Если newPath не является допустимым, возникает исключение ArgumentException. Если запрос выполняется для image.png, запрос перенаправляется к /png-images/image.png. Если запрос выполняется для image.jpg, запрос перенаправляется к /jpg-images/image.jpg. Для кода состояния устанавливается значение 301 - Moved Permanently, а также задается значение context.Result, чтобы остановить обработку правил и отправить ответ.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)) ||
            request.Path.Value == null)
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

Попробуйте следующее:

  • Запрос PNG: https://redirect6.azurewebsites.net/image.png
  • Запрос JPG: https://redirect6.azurewebsites.net/image.jpg

Примеры регулярных выражений

Goal Пример строки регулярного выражения
и совпадения
Пример строки замены и
Пример выходных данных
Переопределение пути в строке запроса ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
Удаление косой черты в конце ^path2/(.*)/$
/path2/xyz/
$1
/path2/xyz
Добавление косой черты в конце ^path3/(.*[^/])$
/path3/xyz
$1/
/path3/xyz/
Запрет переопределения отдельных запросов ^(.*)(?<!\.axd)$ Или
^(?!.*\.axd$)(.*)$
Да: /path4/resource.htm
Нет: /path4/resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
Переупорядочение сегментов URL-адреса path5/(.*)/(.*)/(.*)
path5/1/2/3
path5/$3/$2/$1
path5/3/2/1
Замена сегмента URL-адреса ^path6/(.*)/segment2/(.*)
^path6/segment1/segment2/segment3
path6/$1/replaced/$2
/path6/segment1/replaced/segment3

В ссылках в предыдущей таблице используется следующий код, развернутый в Azure:

using Microsoft.AspNetCore.Rewrite;
using RewriteRules;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

using (StreamReader apacheModRewriteStreamReader =
    File.OpenText("ApacheModRewrite.txt"))
using (StreamReader iisUrlRewriteStreamReader =
    File.OpenText("IISUrlRewrite.xml"))
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent()
        .AddRedirect("redirect-rule/(.*)", "redirected/$1")
        .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2",
            skipRemainingRules: true)

        // Rewrite path to QS.
        .AddRewrite(@"^path/(.*)/(.*)", "path?var1=$1&var2=$2",
            skipRemainingRules: true)
        // Skip trailing slash.
        .AddRewrite(@"^path2/(.*)/$", "path2/$1",
            skipRemainingRules: true)
         // Enforce trailing slash.
         .AddRewrite(@"^path3/(.*[^/])$", "path3/$1/",
            skipRemainingRules: true)
         // Avoid rewriting specific requests.
         .AddRewrite(@"^path4/(.*)(?<!\.axd)$", "rewritten/$1",
            skipRemainingRules: true)
         // Rearrange URL segments
         .AddRewrite(@"^path5/(.*)/(.*)/(.*)", "path5/$3/$2/$1",
            skipRemainingRules: true)
          // Replace a URL segment
          .AddRewrite(@"^path6/(.*)/segment2/(.*)", "path6/$1/replaced/$2",
            skipRemainingRules: true)

        .AddApacheModRewrite(apacheModRewriteStreamReader)
        .AddIISUrlRewrite(iisUrlRewriteStreamReader)
        .Add(MethodRules.RedirectXmlFileRequests)
        .Add(MethodRules.RewriteTextFileRequests)
        .Add(new RedirectImageRequests(".png", "/png-images"))
        .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

    app.UseRewriter(options);
}

app.UseStaticFiles();

app.Run(context => context.Response.WriteAsync(
    $"Rewritten or Redirected Url: " +
    $"{context.Request.Path + context.Request.QueryString}"));

app.Run();

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

Этот документ содержит вводные сведения о переопределении URL-адресов и инструкции по использованию ПО промежуточного слоя для переопределения URL-адресов в приложениях ASP.NET Core.

Переопределение URL-адресов представляет собой изменение URL-адресов запросов на основе одного или нескольких предопределенных правил. Переопределение URL-адресов создает абстракцию между расположениями ресурсов и их адресами, позволяя убрать тесную связь между ними. Существует несколько сценариев, где может пригодиться переопределение URL-адресов:

  • Временное или постоянное перемещение или замещение ресурсов сервера с сохранением стабильных указателей для этих ресурсов.
  • Разделение обработки запросов между разными приложениями или разными областями одного приложения.
  • Удаление, добавление или переупорядочение сегментов URL-адресов для входящих запросов.
  • Оптимизация общедоступных URL-адресов в целях оптимизации для поисковых систем (SEO).
  • Разрешение использования понятных общедоступных URL-адресов, чтобы помочь посетителям прогнозировать содержимое, возвращаемое в результате запроса ресурса.
  • Перенаправление небезопасных запросов на защищенные конечные точки.
  • Запрет вставки прямых ссылок, когда внешний сайт использует статический ресурс, размещенный на другом сайте, привязывая ресурс к своему содержимому.

Примечание.

Переопределение URL-адресов может снижать производительность приложения. Следует по возможности ограничить число и сложность правил.

Просмотреть или скачать образец кода (описание загрузки)

Перенаправление и переопределение URL-адресов

Разница между перенаправлением и переопределением URL-адресов может показаться незначительной, но она оказывает существенное влияние на предоставление ресурсов клиентам. ПО промежуточного слоя для переопределения URL-адресов в ASP.NET Core удовлетворяет потребность в обеих этих функциях.

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

Если /resourceперенаправляется на /different-resource, сервер отвечает, что клиенту следует получить ресурс в /different-resource с кодом состояния, указывающим, что такое перенаправление является временным или постоянным.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

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

  • Код состояния 301 - Moved Permanently используется, когда ресурс имеет новый постоянный URL-адрес и вы хотите сообщить клиенту, что все последующие запросы для этого ресурса должны использовать новый URL-адрес. Клиент может кэшировать и повторно использовать отклик при получении кода состояния 301.

  • Код состояния 302 (найдено) используется, когда перенаправление является временным или может меняться. Код состояния 302 указывает клиенту, что не нужно сохранять URL-адрес и использовать его в будущем.

Дополнительные сведения о кодах состояния см. в статье RFC 9110: определения кода состояния.

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

Когда /resourceпереопределяется на /different-resource, сервер получает и запрашивает ресурс внутри в /different-resource.

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

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

Пример приложения с переопределением URL-адресов

Вы можете ознакомиться с функциями ПО промежуточного слоя переопределения URL-адресов на примере приложения. Это приложение применяет правила перенаправления и переопределения и показывает перенаправленный или переопределенный URL-адрес для нескольких сценариев.

Условия использования ПО промежуточного слоя для переопределения URL-адресов

Используйте ПО промежуточного слоя для переопределения URL-адресов, если невозможно использовать следующие подходы:

Используйте ПО промежуточного слоя, когда приложение размещается на сервере HTTP.sys.

Основные причины для использования переопределения URL-адресов на основе сервера в IIS, Apache и Nginx:

  • ПО промежуточного слоя не поддерживает все функции этих модулей.

    Некоторые функции серверных модулей не работают с проектами ASP.NET Core, например ограничения IsFile и IsDirectory для модуля переопределения IIS. В этих случаях следует использовать ПО промежуточного слоя.

  • Производительность ПО промежуточного слоя, скорее всего, не соответствует производительности модулей.

    Тестирование производительности — это единственный способ узнать точно, какой подход больше всего снижает производительность или является ли такое снижение незначительным.

Пакет

ПО промежуточного слоя перезаписи URL-адресов предоставляется пакетом Microsoft.AspNetCore.Rewrite, который неявно включается в приложения ASP.NET Core.

Расширение и параметры

Задайте правила переопределения и перенаправления URL-адресов, создав экземпляр класса RewriteOptions для каждого правила с помощью методов расширения. Несколько правил можно объединить в цепочку в том порядке, в котором они должны обрабатываться. RewriteOptions передаются в ПО промежуточного слоя для переопределения URL-адресов по мере его добавления в конвейер запросов с помощью UseRewriter:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Перенаправление не www на www

Три параметра разрешают приложению перенаправлять запросы, отличные от www, на www:

  • AddRedirectToWwwPermanent: постоянно перенаправляет запрос на www поддомен, если запрос не является.www Перенаправляет с кодом состояния Status308PermanentRedirect.

  • AddRedirectToWww: перенаправьте запрос на www поддомен, если входящий запрос неwww является. Перенаправляет с кодом состояния Status307TemporaryRedirect. Перегрузка позволяет предоставить код состояния для ответа. Используйте поле класса StatusCodes для назначения кода состояния.

Перенаправление URL-адреса

Используйте AddRedirect для перенаправления запросов. Первый параметр содержит регулярное выражение, соответствующее пути входящего URL-адреса. Второй параметр является строкой замены. Третий параметр (при его наличии) указывает код состояния. Если код состояния не задан, по умолчанию используется код 302 (найдено), указывающий, что ресурс временно перемещен или заменен.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

В браузере с включенными средствами разработчика выполните запрос для примера приложения по пути /redirect-rule/1234/5678. Регулярное выражение соответствует пути запроса в redirect-rule/(.*), и этот путь заменяется на /redirected/1234/5678. URL-адрес перенаправления отправляется обратно клиенту с кодом состояния 302 (найдено). Браузер выполняет новый запрос на URL-адрес перенаправления, отображаемый в адресной строке. Поскольку в примере приложения нет правил, соответствующих URL-адресу перенаправления:

  • Второй запрос получает от приложения ответ 200 (ОК).
  • Текст ответа показывает URL-адрес перенаправления.

При перенаправлении URL-адреса используется круговое обращение к серверу.

Предупреждение

Соблюдайте осторожность при задании правил перенаправления. Они оцениваются для каждого запроса, отправляемого в приложение, даже после перенаправления. Можно легко случайно создать бесконечный цикл перенаправлений.

Исходный запрос: /redirect-rule/1234/5678

Browser window with developer tools tracking the requests and responses: Add redirect

Часть выражения, заключенная в скобки, называется группой записи. Точка (.) в выражении означает совпадение с любым символом. Звездочка (*) означает совпадение с предыдущим символом ноль или более раз. Таким образом, два последних сегмента пути URL-адреса, 1234/5678, перехватываются группой записи (.*). Любое значение, указанное в URL-адресе запроса после redirect-rule/, перехватывается этой группой записи.

В строке замены группы записи внедряются с помощью знака доллара ($), за которым следует порядковый номер записи. Первое значение группы записи получается с помощью $1, второе — с помощью $2, после чего они продолжают по очереди использоваться для групп записи в регулярном выражении. В регулярном выражении правила перенаправления в примере приложения присутствует всего одна группа записи, поэтому в строку замены внедряется тоже одна строка — $1. Когда применяется правило, URL-адрес становится /redirected/1234/5678.

Перенаправление URL-адресов на защищенную конечную точку

Используйте AddRedirectToHttps, чтобы перенаправлять HTTP-запросы на тот же узел и путь с использованием протокола HTTPS. Если код состояния не указан, ПО промежуточного слоя по умолчанию использует значение 302 (найдено). Если порт не указан:

  • ПО промежуточного слоя по умолчанию устанавливается на null.
  • Схема меняется на https (протокол HTTPS), и клиент обращается к ресурсу через порт 443.

В следующем примере показано, как задать код 301 - Moved Permanently состояния и изменить порт на 5001.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttps(301, 5001);

    app.UseRewriter(options);
}

Используйте AddRedirectToHttpsPermanent, чтобы перенаправить небезопасные запросы на тот же узел и путь с использованием безопасного протокола HTTPS через порт 443. ПО промежуточного слоя задает код состояния 301 - Moved Permanently.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent();

    app.UseRewriter(options);
}

Примечание.

При перенаправлении на защищенную конечную точку без дополнительных правил перенаправления рекомендуется использовать ПО промежуточного слоя перенаправления на HTTPS. Дополнительные сведения см. в разделе Обязательное использование HTTPS.

Пример приложения позволяет продемонстрировать использование AddRedirectToHttps или AddRedirectToHttpsPermanent. Добавьте этот метод расширения в RewriteOptions. Выполните небезопасный запрос к приложению по любому URL-адресу. Закройте предостережение системы безопасности о том, что самозаверяющий сертификат не является доверенным, или создайте исключение, чтобы сделать сертификат доверенным.

Исходный запрос с использованием AddRedirectToHttps(301, 5001): http://localhost:5000/secure

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS

Исходный запрос с использованием AddRedirectToHttpsPermanent: http://localhost:5000/secure

Browser window with developer tools tracking the requests and responses: Add redirect to HTTPS permanent

Переопределение URL-адресов

Используйте AddRewrite, чтобы создать правило для переопределения URL-адресов. Первый параметр содержит регулярное выражение, соответствующее пути входящего URL-адреса. Второй параметр является строкой замены. Третий параметр skipRemainingRules: {true|false} сообщает ПО промежуточного слоя, нужно ли пропустить дополнительные правила переопределения, если применяется текущее правило.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Исходный запрос: /rewrite-rule/1234/5678

Browser window with developer tools tracking the request and response: Add rewrite

Карет (^) в начале выражение означает, что сопоставление начинается с начала URL-адреса.

В предыдущем примере с правилом перенаправления, redirect-rule/(.*), в начале регулярного выражения нет символа ^. Таким образом, для успешного совпадения до redirect-rule/ в пути могут стоять любые символы.

Путь Поиск совпадений (Match)
/redirect-rule/1234/5678 Да
/my-cool-redirect-rule/1234/5678 Да
/anotherredirect-rule/1234/5678 Да

Правило переопределения ^rewrite-rule/(\d+)/(\d+) соответствует путям, только если они начинаются с rewrite-rule/. В следующей таблице обратите внимание на разницу в сопоставлении.

Путь Поиск совпадений (Match)
/rewrite-rule/1234/5678 Да
/my-cool-rewrite-rule/1234/5678 No
/anotherrewrite-rule/1234/5678 No

После части ^rewrite-rule/ выражения стоят две группы записи (\d+)/(\d+). \d означает соответствие цифре (числу). Знак "плюс" (+) означает соответствие одному или нескольким предшествующим символам. Таким образом, URL-адрес должен содержать число, за которым идет прямая косая черта, за которой идет другое число. Эти группы записи внедряются в переопределенный URL-адрес как $1 и $2. Строка замены для правила переопределения помещает группы записи в строку запроса. Запрошенный путь /rewrite-rule/1234/5678 переопределяется, чтобы ресурс можно было получить по адресу /rewritten?var1=1234&var2=5678. Если в исходном запросе присутствует строка запроса, она сохраняется при переопределении URL-адреса.

Круговое обращение к серверу для получения ресурса не выполняется. Если ресурс существует, он извлекается и возвращается клиенту с кодом состояния 200 (ОК). Так как клиент не перенаправляется, URL-адрес в адресной строке браузера не изменяется. Клиенты не могут узнать, что на сервере произошла операция переопределения URL-адреса.

Примечание.

Используйте skipRemainingRules: true при любой возможности, так как правила сопоставления потребляют много ресурсов и ухудшают время отклика приложения. Чтобы максимально ускорить отклик приложения:

  • расположите правила переопределения в порядке от наиболее к наименее часто используемому;
  • пропускайте обработку оставшихся правил, когда совпадение найдено и обработка дополнительных правил не требуется.

Apache mod_rewrite

Для применения правил mod_rewrite Apache можно использовать AddApacheModRewrite. Убедитесь, что файл правил развертывается вместе с приложением. Дополнительные сведения и примеры правил mod_rewrite см. в статье о правилах mod_rewrite Apache.

StreamReader используется для чтения правил из файла ApacheModRewrite.txt:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Пример приложения перенаправляет запросы из /apache-mod-rules-redirect/(.\*) в /redirected?id=$1. Отклик имеет код состояния 302 (найдено).

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

Исходный запрос: /apache-mod-rules-redirect/1234

Browser window with developer tools tracking the requests and responses: Add Apache mod redirect

ПО промежуточного слоя поддерживает следующие переменные сервера в mod_rewrite Apache:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

Правила модуля переопределения URL-адресов для IIS

Чтобы использовать тот же набор правил, который применяется к модулю переопределения URL-адресов для IIS, используйте AddIISUrlRewrite. Убедитесь, что файл правил развертывается вместе с приложением. Не указывайте ПО промежуточного слоя использовать файл приложения web.config при работе в службах IIS Windows Server. В IIS эти правила нужно хранить за пределами файла приложения web.config, чтобы предотвратить конфликты с модулем переопределения для IIS. Дополнительные сведения и примеры правил модуля переопределения URL-адресов для IIS см. в разделах Использование модуля переопределения URL-адресов 2.0 и Справочник по конфигурации модуля переопределения URL-адресов.

Используется StreamReader для чтения правил из IISUrlRewrite.xml файла правил:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Пример приложения переопределяет запросы с /iis-rules-rewrite/(.*) на /rewritten?id=$1. Клиенту отправляется отклик с кодом состояния 200 (ОК).

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

Исходный запрос: /iis-rules-rewrite/1234

Browser window with developer tools tracking the request and response: Add IIS URL rewrite

Если имеется активный модуль переопределения для IIS, где настроены правила брандмауэра уровня сервера, способные негативно повлиять на ваше приложение, можно отключить этот модуль для приложения. Дополнительные сведения см. в разделе Отключение модулей IIS.

Неподдерживаемые функции

ПО промежуточного слоя не поддерживает следующие функции в модуле переопределения URL-адресов для служб IIS:

  • Правила для исходящих подключений
  • Пользовательские переменные сервера
  • Подстановочные знаки
  • LogRewrittenUrl

Поддерживаемые переменные сервера

ПО промежуточного слоя поддерживает следующие переменные сервера в модуле переопределения URL-адресов для IIS:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

Примечание.

Можно также получить IFileProvider через PhysicalFileProvider. Такой подход позволяет более гибко задавать расположение для файлов с правилами переопределения. Убедитесь, что эти файлы развертываются на сервере по указанному пути.

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

Правило, основанное на методе

Используйте Add, чтобы реализовать собственную логику правил в методе. Add предоставляет RewriteContext, который предоставляет HttpContext для использования в методе. RewriteContext.Result определяет, как осуществляется дополнительная обработка в конвейере. Установите значение одного из полей RuleResult из следующей таблицы.

Результат перезаписи контекста Действие
RuleResult.ContinueRules (по умолчанию) Продолжение применения правил.
RuleResult.EndResponse Остановка применения правил и отправка отклика.
RuleResult.SkipRemainingRules Остановка применения правил и отправка контекста в следующий компонент ПО промежуточного слоя.
public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Пример приложения демонстрирует метод, который перенаправляет запросы на пути, которые заканчиваются .xml. Если запрос выполняется для /file.xml, запрос перенаправляется к /xmlfiles/file.xml. Код состояния получает значение 301 - Moved Permanently. Когда браузер отправляет новый запрос /xmlfiles/file.xmlна по промежуточному слоя статическим файлам, он обслуживает файл клиенту из папки wwwroot/xmlfiles . Для перенаправления необходимо явно указать код состояния ответа. В противном случае возвращается код состояния 200 (ОК) и перенаправление на стороне клиента не происходит.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

Этот подход также переопределяет запросы. В примере приложения показано переопределение пути для любого запроса текстового файла, чтобы отправить текстовый файл file.txt из папки wwwroot. ПО промежуточного слоя статических файлов предоставляет файл по обновленному пути запроса:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

Правило, основанное на IRule

Используйте Add, чтобы использовать собственную логику правил в классе, реализующем интерфейс IRule. IRule позволяет более гибко применять правила, основанные на методах. Класс реализации может включать конструктор, который позволяет передавать параметры для метода ApplyRule.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Значения параметров в примере приложения для extension и newPath проверяются на соответствие нескольким условиям. extension должен содержать одно из значений: .png, .jpg или .gif. Если newPath не является допустимым, возникает исключение ArgumentException. Если запрос выполняется для image.png, запрос перенаправляется к /png-images/image.png. Если запрос выполняется для image.jpg, запрос перенаправляется к /jpg-images/image.jpg. Для кода состояния устанавливается значение 301 - Moved Permanently, а также задается значение context.Result, чтобы остановить обработку правил и отправить ответ.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)))
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

Исходный запрос: /image.png

Browser window with developer tools tracking the requests and responses for image.png

Исходный запрос: /image.jpg

Browser window with developer tools tracking the requests and responses for image.jpg

Примеры регулярных выражений

Goal Пример строки регулярного выражения
и совпадения
Пример строки замены и
Пример выходных данных
Переопределение пути в строке запроса ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
Удаление косой черты в конце (.*)/$
/path/
$1
/path
Добавление косой черты в конце (.*[^/])$
/path
$1/
/path/
Запрет переопределения отдельных запросов ^(.*)(?<!\.axd)$ или ^(?!.*\.axd$)(.*)$
Да: /resource.htm
Нет: /resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
Переупорядочение сегментов URL-адреса path/(.*)/(.*)/(.*)
path/1/2/3
path/$3/$2/$1
path/3/2/1
Замена сегмента URL-адреса ^(.*)/segment2/(.*)
/segment1/segment2/segment3
$1/replaced/$2
/segment1/replaced/segment3

Этот документ содержит вводные сведения о переопределении URL-адресов и инструкции по использованию ПО промежуточного слоя для переопределения URL-адресов в приложениях ASP.NET Core.

Переопределение URL-адресов представляет собой изменение URL-адресов запросов на основе одного или нескольких предопределенных правил. Переопределение URL-адресов создает абстракцию между расположениями ресурсов и их адресами, позволяя убрать тесную связь между ними. Существует несколько сценариев, где может пригодиться переопределение URL-адресов:

  • Временное или постоянное перемещение или замещение ресурсов сервера с сохранением стабильных указателей для этих ресурсов.
  • Разделение обработки запросов между разными приложениями или разными областями одного приложения.
  • Удаление, добавление или переупорядочение сегментов URL-адресов для входящих запросов.
  • Оптимизация общедоступных URL-адресов в целях оптимизации для поисковых систем (SEO).
  • Разрешение использования понятных общедоступных URL-адресов, чтобы помочь посетителям прогнозировать содержимое, возвращаемое в результате запроса ресурса.
  • Перенаправление небезопасных запросов на защищенные конечные точки.
  • Запрет вставки прямых ссылок, когда внешний сайт использует статический ресурс, размещенный на другом сайте, привязывая ресурс к своему содержимому.

Примечание.

Переопределение URL-адресов может снижать производительность приложения. Следует по возможности ограничить число и сложность правил.

Просмотреть или скачать образец кода (описание загрузки)

Перенаправление и переопределение URL-адресов

Разница между перенаправлением и переопределением URL-адресов может показаться незначительной, но она оказывает существенное влияние на предоставление ресурсов клиентам. ПО промежуточного слоя для переопределения URL-адресов в ASP.NET Core удовлетворяет потребность в обеих этих функциях.

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

Если /resourceперенаправляется на /different-resource, сервер отвечает, что клиенту следует получить ресурс в /different-resource с кодом состояния, указывающим, что такое перенаправление является временным или постоянным.

A WebAPI service endpoint has been temporarily changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The server sends back a 302 (Found) response with the new, temporary path for the service at version 2 /v2/api. The client makes a second request to the service at the redirect URL. The server responds with a 200 (OK) status code.

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

  • Код состояния 301 - Moved Permanently используется, когда ресурс имеет новый постоянный URL-адрес и вы хотите сообщить клиенту, что все последующие запросы для этого ресурса должны использовать новый URL-адрес. Клиент может кэшировать и повторно использовать отклик при получении кода состояния 301.

  • Код состояния 302 (найдено) используется, когда перенаправление является временным или может меняться. Код состояния 302 указывает клиенту, что не нужно сохранять URL-адрес и использовать его в будущем.

Дополнительные сведения о кодах состояния см. в статье RFC 9110: определения кода состояния.

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

Когда /resourceпереопределяется на /different-resource, сервер получает и запрашивает ресурс внутри в /different-resource.

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

A WebAPI service endpoint has been changed from version 1 (v1) to version 2 (v2) on the server. A client makes a request to the service at the version 1 path /v1/api. The request URL is rewritten to access the service at the version 2 path /v2/api. The service responds to the client with a 200 (OK) status code.

Пример приложения с переопределением URL-адресов

Вы можете ознакомиться с функциями ПО промежуточного слоя переопределения URL-адресов на примере приложения. Это приложение применяет правила перенаправления и переопределения и показывает перенаправленный или переопределенный URL-адрес для нескольких сценариев.

Условия для использования ПО промежуточного слоя для переопределения URL-адресов

Используйте ПО промежуточного слоя для переопределения URL-адресов, если невозможно использовать следующие подходы:

Кроме того, используйте ПО промежуточного слоя, когда приложение размещается на сервере HTTP.sys (который раньше назывался WebListener).

Основные причины для использования переопределения URL-адресов на основе сервера в IIS, Apache и Nginx:

  • ПО промежуточного слоя не поддерживает все функции этих модулей.

    Некоторые функции серверных модулей не работают с проектами ASP.NET Core, например ограничения IsFile и IsDirectory для модуля переопределения IIS. В этих случаях следует использовать ПО промежуточного слоя.

  • Производительность ПО промежуточного слоя, скорее всего, не соответствует производительности модулей.

    Тестирование производительности — это единственный способ узнать точно, какой подход больше всего снижает производительность или является ли такое снижение незначительным.

Пакет

Чтобы включить ПО промежуточного слоя в проект, добавьте ссылки на пакет в метапакет Microsoft.AspNetCore.App в файле проекта, который содержит пакет Microsoft.AspNetCore.Rewrite.

Если метапакет Microsoft.AspNetCore.App не используется, добавьте ссылку на проект в пакет Microsoft.AspNetCore.Rewrite.

Расширение и параметры

Задайте правила переопределения и перенаправления URL-адресов, создав экземпляр класса RewriteOptions для каждого правила с помощью методов расширения. Несколько правил можно объединить в цепочку в том порядке, в котором они должны обрабатываться. RewriteOptions передаются в ПО промежуточного слоя для переопределения URL-адресов по мере его добавления в конвейер запросов с помощью UseRewriter:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Перенаправление не www на www

Три параметра разрешают приложению перенаправлять запросы, отличные от www, на www:

  • AddRedirectToWwwPermanent: постоянно перенаправляет запрос на www поддомен, если запрос не является.www Перенаправляет с кодом состояния Status308PermanentRedirect.

  • AddRedirectToWww: перенаправьте запрос на www поддомен, если входящий запрос неwww является. Перенаправляет с кодом состояния Status307TemporaryRedirect. Перегрузка позволяет предоставить код состояния для ответа. Используйте поле класса StatusCodes для назначения кода состояния.

Перенаправление URL-адреса

Используйте AddRedirect для перенаправления запросов. Первый параметр содержит регулярное выражение, соответствующее пути входящего URL-адреса. Второй параметр является строкой замены. Третий параметр (при его наличии) указывает код состояния. Если код состояния не задан, по умолчанию используется код 302 (найдено), указывающий, что ресурс временно перемещен или заменен.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

В браузере с включенными средствами разработчика выполните запрос для примера приложения по пути /redirect-rule/1234/5678. Регулярное выражение соответствует пути запроса в redirect-rule/(.*), и этот путь заменяется на /redirected/1234/5678. URL-адрес перенаправления отправляется обратно клиенту с кодом состояния 302 (найдено). Браузер выполняет новый запрос на URL-адрес перенаправления, отображаемый в адресной строке. Поскольку в примере приложения нет правил, соответствующих URL-адресу перенаправления:

  • Второй запрос получает от приложения ответ 200 (ОК).
  • Текст ответа показывает URL-адрес перенаправления.

При перенаправлении URL-адреса используется круговое обращение к серверу.

Предупреждение

Соблюдайте осторожность при задании правил перенаправления. Они оцениваются для каждого запроса, отправляемого в приложение, даже после перенаправления. Можно легко случайно создать бесконечный цикл перенаправлений.

Исходный запрос: /redirect-rule/1234/5678

Add redirect: Browser window with developer tools tracking the requests and responses

Часть выражения, заключенная в скобки, называется группой записи. Точка (.) в выражении означает совпадение с любым символом. Звездочка (*) означает совпадение с предыдущим символом ноль или более раз. Таким образом, два последних сегмента пути URL-адреса, 1234/5678, перехватываются группой записи (.*). Любое значение, указанное в URL-адресе запроса после redirect-rule/, перехватывается этой группой записи.

В строке замены группы записи внедряются с помощью знака доллара ($), за которым следует порядковый номер записи. Первое значение группы записи получается с помощью $1, второе — с помощью $2, после чего они продолжают по очереди использоваться для групп записи в регулярном выражении. В регулярном выражении правила перенаправления в примере приложения присутствует всего одна группа записи, поэтому в строку замены внедряется тоже одна строка — $1. Когда применяется правило, URL-адрес становится /redirected/1234/5678.

Перенаправление URL-адресов на защищенную конечную точку

Используйте AddRedirectToHttps, чтобы перенаправлять HTTP-запросы на тот же узел и путь с использованием протокола HTTPS. Если код состояния не указан, ПО промежуточного слоя по умолчанию использует значение 302 (найдено). Если порт не указан:

  • ПО промежуточного слоя по умолчанию устанавливается на null.
  • Схема меняется на https (протокол HTTPS), и клиент обращается к ресурсу через порт 443.

В следующем примере показано, как задать код 301 - Moved Permanently состояния и изменить порт на 5001.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttps(301, 5001);

    app.UseRewriter(options);
}

Используйте AddRedirectToHttpsPermanent, чтобы перенаправить небезопасные запросы на тот же узел и путь с использованием безопасного протокола HTTPS через порт 443. ПО промежуточного слоя задает код состояния 301 - Moved Permanently.

public void Configure(IApplicationBuilder app)
{
    var options = new RewriteOptions()
        .AddRedirectToHttpsPermanent();

    app.UseRewriter(options);
}

Примечание.

При перенаправлении на защищенную конечную точку без дополнительных правил перенаправления рекомендуется использовать ПО промежуточного слоя перенаправления на HTTPS. Дополнительные сведения см. в разделе Обязательное использование HTTPS.

Пример приложения позволяет продемонстрировать использование AddRedirectToHttps или AddRedirectToHttpsPermanent. Добавьте этот метод расширения в RewriteOptions. Выполните небезопасный запрос к приложению по любому URL-адресу. Закройте предостережение системы безопасности о том, что самозаверяющий сертификат не является доверенным, или создайте исключение, чтобы сделать сертификат доверенным.

Исходный запрос с использованием AddRedirectToHttps(301, 5001): http://localhost:5000/secure

Add redirect to HTTPS: Browser window with developer tools tracking the requests and responses

Исходный запрос с использованием AddRedirectToHttpsPermanent: http://localhost:5000/secure

Add redirect to HTTPS permanent: Browser window with developer tools tracking the requests and responses

Переопределение URL-адресов

Используйте AddRewrite, чтобы создать правило для переопределения URL-адресов. Первый параметр содержит регулярное выражение, соответствующее пути входящего URL-адреса. Второй параметр является строкой замены. Третий параметр skipRemainingRules: {true|false} сообщает ПО промежуточного слоя, нужно ли пропустить дополнительные правила переопределения, если применяется текущее правило.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Исходный запрос: /rewrite-rule/1234/5678

Add rewrite: Browser window with developer tools tracking the request and response

Карет (^) в начале выражение означает, что сопоставление начинается с начала URL-адреса.

В предыдущем примере с правилом перенаправления, redirect-rule/(.*), в начале регулярного выражения нет символа ^. Таким образом, для успешного совпадения до redirect-rule/ в пути могут стоять любые символы.

Путь Поиск совпадений (Match)
/redirect-rule/1234/5678 Да
/my-cool-redirect-rule/1234/5678 Да
/anotherredirect-rule/1234/5678 Да

Правило переопределения ^rewrite-rule/(\d+)/(\d+) соответствует путям, только если они начинаются с rewrite-rule/. В следующей таблице обратите внимание на разницу в сопоставлении.

Путь Поиск совпадений (Match)
/rewrite-rule/1234/5678 Да
/my-cool-rewrite-rule/1234/5678 No
/anotherrewrite-rule/1234/5678 No

После части ^rewrite-rule/ выражения стоят две группы записи (\d+)/(\d+). \d означает соответствие цифре (числу). Знак "плюс" (+) означает соответствие одному или нескольким предшествующим символам. Таким образом, URL-адрес должен содержать число, за которым идет прямая косая черта, за которой идет другое число. Эти группы записи внедряются в переопределенный URL-адрес как $1 и $2. Строка замены для правила переопределения помещает группы записи в строку запроса. Запрошенный путь /rewrite-rule/1234/5678 переопределяется, чтобы ресурс можно было получить по адресу /rewritten?var1=1234&var2=5678. Если в исходном запросе присутствует строка запроса, она сохраняется при переопределении URL-адреса.

Круговое обращение к серверу для получения ресурса не выполняется. Если ресурс существует, он извлекается и возвращается клиенту с кодом состояния 200 (ОК). Так как клиент не перенаправляется, URL-адрес в адресной строке браузера не изменяется. Клиенты не могут узнать, что на сервере произошла операция переопределения URL-адреса.

Примечание.

Используйте skipRemainingRules: true при любой возможности, так как правила сопоставления потребляют много ресурсов и ухудшают время отклика приложения. Чтобы максимально ускорить отклик приложения:

  • расположите правила переопределения в порядке от наиболее к наименее часто используемому;
  • пропускайте обработку оставшихся правил, когда совпадение найдено и обработка дополнительных правил не требуется.

Apache mod_rewrite

Для применения правил mod_rewrite Apache можно использовать AddApacheModRewrite. Убедитесь, что файл правил развертывается вместе с приложением. Дополнительные сведения и примеры правил mod_rewrite см. в статье о правилах mod_rewrite Apache.

StreamReader используется для чтения правил из файла ApacheModRewrite.txt:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Пример приложения перенаправляет запросы из /apache-mod-rules-redirect/(.\*) в /redirected?id=$1. Отклик имеет код состояния 302 (найдено).

# Rewrite path with additional sub directory
RewriteRule ^/apache-mod-rules-redirect/(.*) /redirected?id=$1 [L,R=302]

Исходный запрос: /apache-mod-rules-redirect/1234

Add Apache mod redirect: Browser window with developer tools tracking the requests and responses

ПО промежуточного слоя поддерживает следующие переменные сервера в mod_rewrite Apache:

  • CONN_REMOTE_ADDR
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_FORWARDED
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_USER_AGENT
  • HTTPS
  • IPV6
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_METHOD
  • REQUEST_SCHEME
  • REQUEST_URI
  • SCRIPT_FILENAME
  • SERVER_ADDR
  • SERVER_PORT
  • SERVER_PROTOCOL
  • TIME
  • TIME_DAY
  • TIME_HOUR
  • TIME_MIN
  • TIME_MON
  • TIME_SEC
  • TIME_WDAY
  • TIME_YEAR

Правила модуля переопределения URL-адресов для IIS

Чтобы использовать тот же набор правил, который применяется к модулю переопределения URL-адресов для IIS, используйте AddIISUrlRewrite. Убедитесь, что файл правил развертывается вместе с приложением. Не указывайте ПО промежуточного слоя использовать файл приложения web.config при работе в службах IIS Windows Server. В IIS эти правила нужно хранить за пределами файла приложения web.config, чтобы предотвратить конфликты с модулем переопределения для IIS. Дополнительные сведения и примеры правил модуля переопределения URL-адресов для IIS см. в разделах Использование модуля переопределения URL-адресов 2.0 и Справочник по конфигурации модуля переопределения URL-адресов.

Используется StreamReader для чтения правил из IISUrlRewrite.xml файла правил:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Пример приложения переопределяет запросы с /iis-rules-rewrite/(.*) на /rewritten?id=$1. Клиенту отправляется отклик с кодом состояния 200 (ОК).

<rewrite>
  <rules>
    <rule name="Rewrite segment to id querystring" stopProcessing="true">
      <match url="^iis-rules-rewrite/(.*)$" />
      <action type="Rewrite" url="rewritten?id={R:1}" appendQueryString="false"/>
    </rule>
  </rules>
</rewrite>

Исходный запрос: /iis-rules-rewrite/1234

Add IIS URL rewrite: Browser window with developer tools tracking the request and response

Если имеется активный модуль переопределения для IIS, где настроены правила брандмауэра уровня сервера, способные негативно повлиять на ваше приложение, можно отключить этот модуль для приложения. Дополнительные сведения см. в разделе Отключение модулей IIS.

Неподдерживаемые функции

ПО промежуточного слоя, выпущенное вместе с ASP.NET Core 2.x, не поддерживает следующие функции в модуле переопределения URL-адресов для IIS:

  • Правила для исходящих подключений
  • Пользовательские переменные сервера
  • Подстановочные знаки
  • LogRewrittenUrl

Поддерживаемые переменные сервера

ПО промежуточного слоя поддерживает следующие переменные сервера в модуле переопределения URL-адресов для IIS:

  • CONTENT_LENGTH
  • CONTENT_TYPE
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_COOKIE
  • HTTP_HOST
  • HTTP_REFERER
  • HTTP_URL
  • HTTP_USER_AGENT
  • HTTPS
  • LOCAL_ADDR
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_PORT
  • REQUEST_FILENAME
  • REQUEST_URI

Примечание.

Можно также получить IFileProvider через PhysicalFileProvider. Такой подход позволяет более гибко задавать расположение для файлов с правилами переопределения. Убедитесь, что эти файлы развертываются на сервере по указанному пути.

PhysicalFileProvider fileProvider = new PhysicalFileProvider(Directory.GetCurrentDirectory());

Правило, основанное на методе

Используйте Add, чтобы реализовать собственную логику правил в методе. Add предоставляет RewriteContext, который предоставляет HttpContext для использования в методе. RewriteContext.Result определяет, как осуществляется дополнительная обработка в конвейере. Установите значение одного из полей RuleResult из следующей таблицы.

Результат перезаписи контекста Действие
RuleResult.ContinueRules (по умолчанию) Продолжение применения правил.
RuleResult.EndResponse Остановка применения правил и отправка отклика.
RuleResult.SkipRemainingRules Остановка применения правил и отправка контекста в следующий компонент ПО промежуточного слоя.
public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Пример приложения демонстрирует метод, который перенаправляет запросы на пути, которые заканчиваются .xml. Если запрос выполняется для /file.xml, запрос перенаправляется к /xmlfiles/file.xml. Код состояния получает значение 301 - Moved Permanently. Когда браузер отправляет новый запрос /xmlfiles/file.xmlна по промежуточному слоя статическим файлам, он обслуживает файл клиенту из папки wwwroot/xmlfiles . Для перенаправления необходимо явно указать код состояния ответа. В противном случае возвращается код состояния 200 (ОК) и перенаправление на стороне клиента не происходит.

RewriteRules.cs:

public static void RedirectXmlFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    // Because the client is redirecting back to the same app, stop 
    // processing if the request has already been redirected.
    if (request.Path.StartsWithSegments(new PathString("/xmlfiles")))
    {
        return;
    }

    if (request.Path.Value.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
    {
        var response = context.HttpContext.Response;
        response.StatusCode = (int) HttpStatusCode.MovedPermanently;
        context.Result = RuleResult.EndResponse;
        response.Headers[HeaderNames.Location] = 
            "/xmlfiles" + request.Path + request.QueryString;
    }
}

Этот подход также переопределяет запросы. В примере приложения показано переопределение пути для любого запроса текстового файла, чтобы отправить текстовый файл file.txt из папки wwwroot. ПО промежуточного слоя статических файлов предоставляет файл по обновленному пути запроса:

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

RewriteRules.cs:

public static void RewriteTextFileRequests(RewriteContext context)
{
    var request = context.HttpContext.Request;

    if (request.Path.Value.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
    {
        context.Result = RuleResult.SkipRemainingRules;
        request.Path = "/file.txt";
    }
}

Правило, основанное на IRule

Используйте Add, чтобы использовать собственную логику правил в классе, реализующем интерфейс IRule. IRule позволяет более гибко применять правила, основанные на методах. Класс реализации может включать конструктор, который позволяет передавать параметры для метода ApplyRule.

public void Configure(IApplicationBuilder app)
{
    using (StreamReader apacheModRewriteStreamReader = 
        File.OpenText("ApacheModRewrite.txt"))
    using (StreamReader iisUrlRewriteStreamReader = 
        File.OpenText("IISUrlRewrite.xml")) 
    {
        var options = new RewriteOptions()
            .AddRedirect("redirect-rule/(.*)", "redirected/$1")
            .AddRewrite(@"^rewrite-rule/(\d+)/(\d+)", "rewritten?var1=$1&var2=$2", 
                skipRemainingRules: true)
            .AddApacheModRewrite(apacheModRewriteStreamReader)
            .AddIISUrlRewrite(iisUrlRewriteStreamReader)
            .Add(MethodRules.RedirectXmlFileRequests)
            .Add(MethodRules.RewriteTextFileRequests)
            .Add(new RedirectImageRequests(".png", "/png-images"))
            .Add(new RedirectImageRequests(".jpg", "/jpg-images"));

        app.UseRewriter(options);
    }

    app.UseStaticFiles();

    app.Run(context => context.Response.WriteAsync(
        $"Rewritten or Redirected Url: " +
        $"{context.Request.Path + context.Request.QueryString}"));
}

Значения параметров в примере приложения для extension и newPath проверяются на соответствие нескольким условиям. extension должен содержать одно из значений: .png, .jpg или .gif. Если newPath не является допустимым, возникает исключение ArgumentException. Если запрос выполняется для image.png, запрос перенаправляется к /png-images/image.png. Если запрос выполняется для image.jpg, запрос перенаправляется к /jpg-images/image.jpg. Для кода состояния устанавливается значение 301 - Moved Permanently, а также задается значение context.Result, чтобы остановить обработку правил и отправить ответ.

public class RedirectImageRequests : IRule
{
    private readonly string _extension;
    private readonly PathString _newPath;

    public RedirectImageRequests(string extension, string newPath)
    {
        if (string.IsNullOrEmpty(extension))
        {
            throw new ArgumentException(nameof(extension));
        }

        if (!Regex.IsMatch(extension, @"^\.(png|jpg|gif)$"))
        {
            throw new ArgumentException("Invalid extension", nameof(extension));
        }

        if (!Regex.IsMatch(newPath, @"(/[A-Za-z0-9]+)+?"))
        {
            throw new ArgumentException("Invalid path", nameof(newPath));
        }

        _extension = extension;
        _newPath = new PathString(newPath);
    }

    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;

        // Because we're redirecting back to the same app, stop 
        // processing if the request has already been redirected
        if (request.Path.StartsWithSegments(new PathString(_newPath)))
        {
            return;
        }

        if (request.Path.Value.EndsWith(_extension, StringComparison.OrdinalIgnoreCase))
        {
            var response = context.HttpContext.Response;
            response.StatusCode = (int) HttpStatusCode.MovedPermanently;
            context.Result = RuleResult.EndResponse;
            response.Headers[HeaderNames.Location] = 
                _newPath + request.Path + request.QueryString;
        }
    }
}

Исходный запрос: /image.png

For image.png: Browser window with developer tools tracking the requests and responses

Исходный запрос: /image.jpg

For image.jpg: Browser window with developer tools tracking the requests and responses

Примеры регулярных выражений

Goal Пример строки регулярного выражения
и совпадения
Пример строки замены и
Пример выходных данных
Переопределение пути в строке запроса ^path/(.*)/(.*)
/path/abc/123
path?var1=$1&var2=$2
/path?var1=abc&var2=123
Удаление косой черты в конце (.*)/$
/path/
$1
/path
Добавление косой черты в конце (.*[^/])$
/path
$1/
/path/
Запрет переопределения отдельных запросов ^(.*)(?<!\.axd)$ или ^(?!.*\.axd$)(.*)$
Да: /resource.htm
Нет: /resource.axd
rewritten/$1
/rewritten/resource.htm
/resource.axd
Переупорядочение сегментов URL-адреса path/(.*)/(.*)/(.*)
path/1/2/3
path/$3/$2/$1
path/3/2/1
Замена сегмента URL-адреса ^(.*)/segment2/(.*)
/segment1/segment2/segment3
$1/replaced/$2
/segment1/replaced/segment3

Дополнительные ресурсы