В более ранних технологиях на платформе ASP.NET (например, в ASP.NET MVC 5) правила для URL Rewriting задавались в основном для IIS с помощью файла конфигурации web.config. И в ASP.NET Core мы также можем использовать все эти правила с помощью специального метода AddIISUrlRewrite().
Итак, добавим в корень проекта новый xml-файл urlrewrite.xml:
<rewrite> <rules> <rule name="Redirect from home to home/index"> <match url = "^home$"/> <conditions> <add input="{REQUEST_URI}" pattern="home" /> </conditions> <action type="Redirect" url ="home/index" /> </rule> </rules> </rewrite>
Здесь определено одно правило, которое выполняет переадресацию с адреса "/home" на адрес "/home/index". Теперь применим это правило в классе Startup:
using Microsoft.AspNetCore.Rewrite; // Пакет с middleware URL Rewriting var builder = WebApplication.CreateBuilder(); var app = builder.Build(); IHostEnvironment? env = app.Services.GetService<IHostEnvironment>(); if(env is not null) { var options = new RewriteOptions() .AddIISUrlRewrite(env.ContentRootFileProvider, "urlrewrite.xml"); app.UseRewriter(options); } app.MapGet("/", async context => await context.Response.WriteAsync("Hello World!")); app.MapGet("/home", async context => await context.Response.WriteAsync("Home Page!")); app.MapGet("/home/index", async context => await context.Response.WriteAsync("Home Index Page!")); app.Run();
В качестве первого параметра в AddIISUrlRewrite
передается провайдер файлов. В данном случае используем встроенный провайдер из добавлемого по умолчанию сервиса IHostEnvironment.
Второй параметр представляет путь к файлу.
Также загружать файл конфигурации следующим образом:
using System.IO; using (StreamReader iisReader = File.OpenText("urlrewrite.xml")) { var options = new RewriteOptions().AddIISUrlRewrite(iisReader); app.UseRewriter(options); }
Все правила определяются в элементе <rules>. Каждое правило, представленное элементом <rule>
, состоит из трех частей:
Pattern – выражение, которому должна соответствовать строка запроса и которое задается в элементе <match>
Conditions – различные дополнительные условия, которым должен соответствовать URL-адрес. Например, значения HTTP-заголовков, пути к файлам и т.д.
Action – действие, которое должно выполняться, если строка URL соответствует регулярному выражению в Pattern и условиям Conditions
Мы можем использовать несколько правил одновременно, но их выполнение не всегда обязательно. Поэтому у каждого элемента rule
определен атрибут StopProcessing
. Если имеет значение true
, то после выполнения действия в элементе <action>
адрес URL,
создаваемый данным правилом, передается в конвейер обработки запроса, а другие правила не будут обрабатываться.
Переопределение URL имеет следующий порядок действий:
Строка запроса сравнивается с выражением в элементе match. Если обнаружится, что запрошенный адрес не соответствует выражению, то модуль URL Rewrite Module прекращает обрабатывать текущее правило и переходит к следующему (если задано несколько правил)
Если строка запроса соответствует выражению в элементе match и при этом не задано никаких дополнительных условий с помощью элемента
<conditions>
, то URL Rewrite Module выполняет действие, которое определено в правиле с помощью элемента <action>
.
Если строка запроса соответствует выражению в элементе match и также определены дополнительные условия, то URL Rewrite Module проверяет эти условия. И если URL соответствует этим условиям, то выполняется действие action
Условия, задаваемые элементом <conditions>
, определяют дополнительную логику оценки URL на соответствие правилу.
Каждое отдельное условие задается с помощью элемента <add >
и настраивается с помощью следующих атрибутов:
input
: определяет объект, который будет использоваться условием для оценки. В частности, в примере выше используется
input="{REQUEST_URI}"
, где "REQUEST_URI" представляет переменную сервера, хранящую запрошенный адрес url. Тут также могут использоваться и другие переменные сервера.
pattern
: определяет регулярное выражение, которому должен соответствовать объект
matchType
: принимает следующие значения:
Pattern
: в этом случае объект (в данном случае адрес URL) сопоставляется с выражением в атрибуте pattern. При других значениях
атрибут pattern не учитывается
IsFile
: определяет, является ли объект (адрес URL) файлом в файловой системе
IsDirectory
: определяет, является ли объект (адрес URL) каталогом в файловой системе
ignoreCase
: указывает, надо ли игнорировать регистр адреса URL. По умолчанию равно true, поэтому регистр не учитывается
negate
: если равно true, то правило применяется, если условие НЕ учитывается. По умолчанию равно false
Если выражение и условия, определяемые правилом, соответствуют объекту (например, адресу URL), то выполняется определенное действие, заданное элементом
<action>
. Действия могут быть нескольких типов. Тип задается с помощью атрибута type
, который принимает следующие значения:
Rewrite: заменяет текущую строку запроса URL другой строкой
Redirect: выполняет редирект, посылая клиенту статусный код 3хх.
CustomResponse: отправляет клиенту определенный статусный код, а также может отправлять специфическое сообщение
AbortRequest: сбрасывает подключение для текущего клиента
Другие атрибуты элемента action:
url
: строка, которая будет заменять текущую строку запроса URL
appendQueryString
: определяет, должна ли сохраняться та часть строки запроса, которая идет после названия домена и порта. По умолчанию
имеет значение true, что значит, что строка запроса со всеми параметрами за исключением названия домена будет сохраняться.
redirectType
: статусный код переадресации при использовании типа Redirect (301 – Permanent, 302 – Found,
303 – See other, 307 – Temporary)
statusCode
: определяет статусный код в качестве ответа клиенту при использовании типа CustomResponse
subStatusCode
: определяет вспомогательный статусный код при использовании типа CustomResponse
statusReason
: определяет сообщение, отправляемое клиенту вместе со статусным кодом при использовании типа CustomResponse
statusDescription
: определяет сообщение, отправляемое клиенту в теле ответа при использовании типа CustomResponse
При изменении URL мы можем использовать следующие переменные сервера:
QUERY_STRING
: параметры запроса
HTTP_HOST
: домен
SERVER_PORT
: номер порта
SERVER_PORT_SECURE
и HTTPS
: указывают, использует ли клиент защищенное подключение
REQUEST_URI
: полная строка запроса
URL представляется в следующем виде: http(s)://<host>:<port>/<path>?<querystring>
.
Допустим, пользователь обращается к URL http://www.somesite.com/home/index?id=2&name=3. Тогда IIS сегментирует ее следующим образом:
path
: представляет сегмент home/index
. Эта часть затем сравнивается правилом с выражением, определенным в элементе <match>
QUERY_STRING
: в данном случае сегмент параметров id=2&name=3
HTTP_HOST
: сегмент www.somesite.com
SERVER_PORT
: если номер порта не указан, то по умолчанию равен 80.
SERVER_PORT_SECURE
равен 0, а HTTPS
содержит OFF
REQUEST_URI
: сегмент home/index?id=2&name=3
При создании условий для правил мы можем ссылаться на эти переменные через выражение вида "{НАЗВАНИЕ_ПЕРЕМЕННОЙ}". Например, нам нужно условие, согласно которому в строке параметров должен быть числовой параметр id:
<add input="{QUERY_STRING}" pattern="id=([0-9]+)" />
Кроме того, нам доступны заголовки HTTP-запроса, например, строку юзер-агента мы можем получить с помощью выражения "{HTTP_USER_AGENT}".
При использовании заголовков запроса надо учитывать, что все дефисы в названии заголовков (например, User-Agent) заменяются символами подчеркивания. Все строчные буквы заменяются заглавными, а к названию переменных добавляется префикс "HTTP_". Как например, из заголовка User-Agent создается переменная HTTP_USER_AGENT.
Обратные ссылки представляют отдельные сегменты выражений, которые используются в условиях. Например:
<rewrite> <rules> <rule name="Redirect"> <match url = "(.*)"/> <conditions> <add input="{REQUEST_URI}" pattern="([a-z]+)/([a-z]+)/([0-9]+)" matchType="Pattern" /> </conditions> <action type="Redirect" url ="{C:1}/{C:3}" /> </rule> </rules> </rewrite>
Все обратные ссылки представляют выражения типа {C:N}, где N - число от 0 до 9. При этом значение {C:0}
представляет всю попадающую под условие строку.
То есть из строки запроса "home/index/2" генерировались бы три обратных ссылки C:1 = "home"
, C:2 = "index"
и C:3 = "2"
.
И в соответствии с элементом action
шла бы переадресация на адрес "home/2" (то есть "{C:1}/{C:3}").
Кроме условий для создания обратных ссылок могут применяться выражения в элементе match
. Все обратные ссылки из выражения match
доступны через выражения типа {R:N}, где N - число от 0 до 9. При этом значение {R:0}
представляет всю попадающую под условие строку.
Так, рассмотрим другой пример. Допустим, у нас есть правило:
<rule name="Rewrite query" stopProcessing="true"> <match url="^home/products/([0-9]+)/([_0-9a-z-]+)" /> <action type="Rewrite" url="home/products?id={R:1}&name={R:2}" /> </rule>
Например, при запросе http://localhost:50645/Home/Products/2/phones мы получим следующие сегменты:
{R:0} = "Home/Products/2/phones"
{R:1} = "2"
{R:2} = "phones"
В итоге будет формироваться следующая строка URL: http://localhost:50645/Home/Products?id=2&name=phones
Для тестирования определим следующее приложение:
using Microsoft.AspNetCore.Rewrite; // Пакет с middleware URL Rewriting var builder = WebApplication.CreateBuilder(); var app = builder.Build(); IHostEnvironment? env = app.Services.GetService<IHostEnvironment>(); if(env is not null) { var options = new RewriteOptions() .AddIISUrlRewrite(env.ContentRootFileProvider, "urlrewrite.xml"); app.UseRewriter(options); } app.MapGet("/", async context => await context.Response.WriteAsync("Hello World!")); app.MapGet("/home/products", async context => await context.Response.WriteAsync($"Values: id = {context.Request.Query["id"]} name = {context.Request.Query["name"]}")); app.Run();
Еще один пример - переадресация с домена без www на субдомен www:
<rule name="Redirect to WWW" enabled="true" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{HTTP_HOST}" pattern=".*" /> </conditions> <action type="Redirect" url="http://www.localhost:50645/{R:0}" /> </rule>
Или обратное действие - перенаправление с www на домен без www (для домена localhost):
<rule name="Redirect to NonWWW" stopProcessing="true"> <match url=".*" /> <conditions logicalGrouping="MatchAll"> <add input="{HTTP_HOST}" pattern="^localhost" negate="true" /> </conditions> <action type="Redirect" url="http://localhost:50645/{R:0}" /> </rule>