Хотя фреймворк ASP.NET Core по умолчанию предоставляет большой набор встроенных ограничений, их может быть недостаточно. И в этом случае мы можем определить свои кастомные ограничения маршрутов.
Для создания собственного ограничения маршрута нужно реализовать интерфейс IRouteConstraint с одним единственным методом Match, который имеет следующее определение:
Public Interface IRouteConstraint Public Function Match(httpContext As HttpContext, route As IRouter, routeKey As String, values As RouteValueDictionary, routeDirection As RouteDirection) As Boolean End Interface
Параметры метода:
httpContext
: объект HttpContext, который инкапсулирует информацию о HTTP-запросе
route
: объект IRouter, который представляет маршрут, в рамках которого применяется ограничение
routeKey
: объект String - название параметра маршрута, к которому применяется ограничение
values
: объект RouteValueDictionary, который представляет набор параметров маршрута в виде словаря, где ключи - названия параметров,
а значения - значения параметров маршрута
routeDirection
: объект перечисления RouteDirection, которое указывает, применяется ограничение при обработке запроса, либо при генерации ссылки
В качестве результата метод возвращает значение типа Boolean: true
, если зпрос удовлетворяет данному ограничению
маршрута, и false
, если не удовлетворяет.
Ограничение маршрута применяет этот интерфейс IRouteConstraint. Это вынуждает движок маршрутизации вызвать для ограничения маршрута метод
IRouteConstraint.Match, чтобы определить, применяется ли данное ограничение к данному запросу или нет. И только если данный метод возвращает true
, запрос может быть сопоставлен с маршрутом (конечно, если запрос также удовлетворяет и другим ограниченям, которые могут применяться).
Допустим, клиент в запросе через параметр должен передавать код, который равен некоторой строке. Для этого определим следующий класс ограничения маршрута:
Public Class SecretCodeConstraint Implements IRouteConstraint Dim secretCode As String 'допустимый код Public Sub New(secretCode As String) Me.secretCode = secretCode End Sub Public Function Match(httpContext As HttpContext, route As IRouter, routeKey As String, values As RouteValueDictionary, routeDirection As RouteDirection) As Boolean Implements IRouteConstraint.Match Return values(routeKey)?.ToString() = secretCode End Function End Class
Класс SecretCodeConstraint через конструктор принимает некий условно секретный код, которому должен соответствовать параметр маршрута. В методе Match()
с помощью выражения values[routeKey]
получаем из словаря values значение параметра маршрута, имя которого передается через routeKey
.
И затем это значение сравниванием с секретным кодом:
Return values(routeKey)?.ToString() = secretCode
Если оба значения равны, то возвращаем true
, что указывает, что маршрут соответствует данному ограничению.
Применим данное ограничение:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.AspNetCore.Http Imports Microsoft.AspNetCore.Routing Imports Microsoft.Extensions.DependencyInjection Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) ' проецируем класс SecretCodeConstraint на inline-ограничение secretcode builder.Services.Configure(Of RouteOptions)(Sub(options) options.ConstraintMap.Add("secretcode", GetType(SecretCodeConstraint))) ' альтернативное добавление класса ограничения 'builder.Services.AddRouting(Sub(options) options.ConstraintMap.Add("secretcode", GetType(SecretCodeConstraint))) Dim app = builder.Build() app.Map("/users/{name}/{token:secretcode(123466)}/", Function(name As String, token As Integer) $"Name: {name}{Environment.NewLine}Token: {token}") app.Map("/", Function(context As HttpContext) "Index Page") app.Run() End Sub Public Class SecretCodeConstraint Implements IRouteConstraint Dim secretCode As String 'допустимый код Public Sub New(secretCode As String) Me.secretCode = secretCode End Sub Public Function Match(httpContext As HttpContext, route As IRouter, routeKey As String, values As RouteValueDictionary, routeDirection As RouteDirection) As Boolean Implements IRouteConstraint.Match Return values(routeKey)?.ToString() = secretCode End Function End Class End Module
Здесь надо отметить следующий момент. Если мы хотим использовать класс ограничения как inline-ограничение внутри шаблона маршрута, то нам необходимо изменить настройки для сервиса RouteOptions:
builder.Services.Configure(Of RouteOptions)(Sub(options) options.ConstraintMap.Add("secretcode", GetType(SecretCodeConstraint)))
В данном случае параметр options
представляет объект RouteOptions. Его свойство ConstraintMap представляет
коллекцию применяемых ограничений. Метод Add() добавляет в эту коллекцию ограничение. Причем первый параметр этого метода представляет
ключ ограничения в коллекции ограничений, а второй параметр - собственно класс ограничения. Далее ключ ограничения затем можно будет применять как inline-ограничение, на которое проецируется класс ограничения.
В качестве альтернативы вместо этого вызова мы могли бы обратиться к сервисам маршрутизации и также настроить объект RouteOptions:
builder.Services.AddRouting(Sub(options) options.ConstraintMap.Add("secretcode", GetType(SecretCodeConstraint)))
После этого мы сможем использовать inline-ограничение в шаблоне маршрута:
"/users/{name}/{token:secretcode(123466)}/"
То есть здесь к параметру маршрута token
будет применяться ограничение SecretCodeConstraint. А строка 123466 - это тот секретный код,
который будет передаваться в конструктор объекта SecretCodeConstraint.
Другой пример. Допустим, мы хотим, чтобы параметр не мог иметь одно из набора значений:
Imports Microsoft.AspNetCore.Builder Imports Microsoft.AspNetCore.Http Imports Microsoft.AspNetCore.Routing Imports Microsoft.Extensions.DependencyInjection Module Program Sub Main(args As String()) Dim builder = WebApplication.CreateBuilder(args) builder.Services.Configure(Of RouteOptions)(Sub(options) options.ConstraintMap.Add("invalidnames", GetType(InvalidNamesConstraint)) End Sub) Dim app = builder.Build() app.Map("/users/{name:invalidnames}", Function(name As String) $"Name: {name}") app.Map("/", Function(context As HttpContext) "Index Page") app.Run() End Sub Public Class InvalidNamesConstraint Implements IRouteConstraint Dim names As String() = {"Tom", "Sam", "Bob"} Public Function Match(httpContext As HttpContext, route As IRouter, routeKey As String, values As RouteValueDictionary, routeDirection As RouteDirection) As Boolean Implements IRouteConstraint.Match Return Not names.Contains(values(routeKey)?.ToString()) End Function End Class End Module
В данном случае параметр маршрута name
НЕ должен представлять имена "Tom", "Sam" и "Bob":