Создание ограничений маршрутов

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

Хотя фреймворк 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.

Создание ограничений маршрутов в ASP.NET Core и Visual Basic .NET

Другой пример. Допустим, мы хотим, чтобы параметр не мог иметь одно из набора значений:

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":

Создание ограничений маршрутов и интерфейс IRouteConstraint в ASP.NET Core и Visual Basic .NET
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850