Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Функциональность URL Rewriting позволяет контролировать доступ по определенным URL-адресам. В частности, мы можем выполнить переопределение адресов, которые используются для доступа к ресурсам приложения. Например, URL Rewriting позволяет решить такие стандартные проблемы, как перенаправление с домена с www на домен без www и наоборот или перенаправление с протокола http на https.
URL Rewriting реализуется до того, как запрос попадет в систему маршрутизации, и начнется его непосредственное выполнение в конвейере MVC. Запрошенный адрес изначально может отсутствовать в приложении, однако URL Rewriting может изменить этот адрес на любой приемлемый.
Для подключения компонента URL Rewriting используется метод расширения UseRewriter(), который в качестве параметра принимает объект RewriteOptions, задающий все правила переопределения адресов URL:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Rewrite; // Пакет с middleware URL Rewriting namespace UrlRewritingApp { public class Startup { public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); // подключаем URL Rewriting var options = new RewriteOptions(); app.UseRewriter(options); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); }); } } }
Правда, в данном случае для RouteOptions пока еще не определено никаких правил переопределения URL, которые мы можем задать с помощью специальных методов:
AddRedirect(): выполняет переадресацию с отправкой статусного кода HTTP 301
AddRewrite(): подменяет один адрес другим
AddRedirectToWww(): выполняет переадресацию на поддомен WWW
AddRedirectToWwwPermanent(): выполняет переадресацию на поддомен WWW с отправкой статусного кода HTTP 301 (постоянная переадресация)
AddRedirectToHttps(): выполняет переадресацию на протокол HTTPS
AddRedirectToHttpsPermanent(): выполняет переадресацию на протокол HTTPS с отправкой статусного кода HTTP 301 (постоянная переадресация)
AddIISUrlRewrite(): в качестве источника правил для переопределения URL использует правила для IIS
AddApacheModRewrite(): в качестве источника правил для переопределения URL использует правила для Apache
Метод AddRedirect()
в качестве первого параметра принимает регулярное выражение, которому должен соответствовать входящий адрес URL.
В качестве адреса в метод AddRedirect передается та часть URL, которая образуется с помощью объединения значений Request.Path
и
Request.QueryString
. То есть, если полный запрошенный адрес http://localhost:1234/home/index?id=4
, то в метод AddRedirect передается
home/index?id=4
Второй параметр также представляет собой выражение, которое указывает, по какому адресу нужно выполнять переадресацию. Например:
public void Configure(IApplicationBuilder app) { var options = new RewriteOptions().AddRedirect("(.*)/$", "$1"); app.UseRewriter(options); app.UseStaticFiles(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); }); }
Регулярное выражение "(.*)/$"
указывает на любой адрес, который завершается слешем. Второй параметр указывает, что в качестве адреса
для перенаправления будет использоваться та часть исходного URL, которая идет до слеша: (.*)
. То есть "$1" указывает на первую группу в регулярном выражении "(.*)/$".
То есть в данном случае удаляется концевой слеш (например, перенаправляется с "localhost:1234/home/index/" на "localhost:1234/home/index").
Рассмотрим другую ситуацию. Например, мы хотим перенаправлять с адреса home на home/index:
public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); // подключаем URL Rewriting var options = new RewriteOptions() .AddRedirect("(.*)/$", "$1") // удаление концевого слеша .AddRedirect("home[/]?$", "home/index"); // переадресация с home на home/index app.UseRewriter(options); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); endpoints.MapGet("/home", async context => { await context.Response.WriteAsync("Home Page!"); }); endpoints.MapGet("/home/index", async context => { await context.Response.WriteAsync("Home Index Page!"); }); }); }
Для примера с помощью метода endpoints.MapGet
заданы тестовые маршруты, в итоге при обращении по адресу "home" произойдет переадресация
на адрес "home/index", и мы увидим в браузере строку "Home Index Page!".
При этом можно задать последовательно сразу несколько правил, как в данном случае.
Но важно учитывать, что передаваемый в метод AddRedirect()
адрес ресгистрозависим. То есть мы можем обратиться по адресу
"HoMe", и переадресация не сработает:
Но мы можем выйти из данной ситуации, использовав соответствующий элемент регулярных выражений:
var options = new RewriteOptions() .AddRedirect("(.*)/$", "$1") // удаление концевого слеша .AddRedirect("(?i)home[/]?$", "home/index"); // переадресация с home на home/index app.UseRewriter(options);
Метод AddRewrite()
подменяет входящий адрес другим. Первый параметр метода указывает на регулярное выражение, которому должен соответствовать
адрес. Второй параметр указывает, на какой адрес надо подменить входящий. Третий параметр - булевое значение устанавливает, надо ли прекратить применение остальных правил,
если адрес соответствует выражению из первого параметра. Например:
public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); // подключаем URL Rewriting var options = new RewriteOptions() .AddRedirect("(.*)/$", "$1") .AddRewrite("home/index", "home/about", skipRemainingRules: false); app.UseRewriter(options); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); endpoints.MapGet("/home/about", async context => { await context.Response.WriteAsync($"About: {context.Request.Path}"); }); endpoints.MapGet("/home/index", async context => { await context.Response.WriteAsync("Home Index Page!"); }); }); }
Правило
AddRewrite("home/index", "home/about", skipRemainingRules: false);
указывает, что при запросе "home/index" в реальности запрос будет сопоставляться с маршрутом "home/about"
При этом переадресации как таковой нет, статусный код 301/302 не отправляется клиенту.
Ключевым элементом, который используется при определении шаблонов адресов, являются группы - набор выражений, которые заключаются в скобки.
Например, в рассмотренном выше примере с удалением концевого слеша применялась одна группа:
"(.*)/$"
то есть знак точки "." означает любой символ, знак звездочки "*" означает, что таких символов может быть произвольное количество. И все это объединяется в одну группу - "(.*)". Таким образом, в данном случае группой будет все символы, которые идут до конечного слеша.
При создании паттерна для редиректа или рерайтинга мы можем ссылать на группу по номеру. В примере с концевым слешем определяется одна группа, поэтому мы можем к ней обратиться через "$1" - после символа $ идет номер группы.
Для понимания работы групп при рерайтинге/редиректе рассмотрим несколько примеров:
var options = new RewriteOptions() .AddRedirect("(.*)/$", "$1") .AddRewrite(@"home/index/(\d+)", "home/about?id=$1", skipRemainingRules: false);
В данном случае если адрес соответствует выражению "home/index/(\d+)" (например, "home/index/3"), то фактически происходит обращение по адресу "home/about?id=$1" - $1 здесь также указывает на первую группу в регулярном выражении - (\d+).
Другой пример. Определим следующее правило url rewriting:
public void Configure(IApplicationBuilder app) { var options = new RewriteOptions() .AddRewrite(@"product/(\w+)/(\d+)", "home/products?cat=$1&id=$2", skipRemainingRules: false); app.UseRewriter(options); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); endpoints.MapGet("/home/products", async context => { await context.Response.WriteAsync($"cat: {context.Request.Query["cat"].ToString()} " + $"id: {context.Request.Query["id"].ToString()}"); }); }); }
В данном случае используется следующее правило:
AddRewrite(@"product/(\w+)/(\d+)", "home/products?cat=$1&id=$2", skipRemainingRules: false);
Это правило будет траслировать любой запрос типа "product/tablet/23" в запрос в виде "home/products/cat=tablet?id=23". То есть теперь у нас две группы: (\w+) и (\d+). Соответственно мы к ним можем обратиться через $1 и $2.
В заключении стоит отметить, что использование url rewriting увеличивает накладные расходы на обработку запроса. Поэтому стоит по возможности избегать сложных комплексных правил переопределения строки запроса.
Кроме того, Rewriting Middleware не покрывает всех возможностей нативных модулей в IIS, Apache, Nginx, которые обеспечивают URL-rewriting. Также, нативные модули выше упомянутых веб-серверов демонстрируют большую производительность.