URL Rewriting

Введение в URL Rewriting

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7

Последнее обновление: 21.12.2019

Функциональность 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

Метод 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", и переадресация не сработает:

UrlRewriting in ASP.NET Core

Но мы можем выйти из данной ситуации, использовав соответствующий элемент регулярных выражений:

var options = new RewriteOptions()
				.AddRedirect("(.*)/$", "$1")                // удаление концевого слеша
				.AddRedirect("(?i)home[/]?$", "home/index"); // переадресация с home на home/index
app.UseRewriter(options);

AddRewrite

Метод 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"

URL Rewriting in ASP.NET Core

При этом переадресации как таковой нет, статусный код 301/302 не отправляется клиенту.

Элементы регулярных выражений в URL Rewriting

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

Например, в рассмотренном выше примере с удалением концевого слеша применялась одна группа:

"(.*)/$"

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

При создании паттерна для редиректа или рерайтинга мы можем ссылать на группу по номеру. В примере с концевым слешем определяется одна группа, поэтому мы можем к ней обратиться через "$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 в ASP.NET Core MVC

В заключении стоит отметить, что использование url rewriting увеличивает накладные расходы на обработку запроса. Поэтому стоит по возможности избегать сложных комплексных правил переопределения строки запроса.

Кроме того, Rewriting Middleware не покрывает всех возможностей нативных модулей в IIS, Apache, Nginx, которые обеспечивают URL-rewriting. Также, нативные модули выше упомянутых веб-серверов демонстрируют большую производительность.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850