Маршрутизация в ASP.NET Core. Создание собственных ограничений маршрутов (интерфейс IRouteConstraint)

Несмотря на то, что ASP.NET Core предлагает довольно большой перечень стандартных ограничений для параметров маршрутов, позволяющих покрыть, если не все 100, то 99,9% потребностей разработчика, всё же таких ограничений может оказаться недостаточно для работы и поэтому в ASP.NET Core мы можем самостоятельно создавать собственные ограничения. Рассмотрим как это можно сделать.

Интерфейс IRouteConstraint

Для создания собственного ограничения маршрута необходимо реализовать интерфейс IRouteConstraint, который имеет всего один метод Match() со следующей сигнатурой:

Match(HttpContext, IRouter, String, RouteValueDictionary, RouteDirection)

здесь httpContext — объект типа HttpContext, содержащий сведения об HTTP-запросе.
route — маршрутизатор IRouter, к которому относится это ограничение.
routeKey — имя проверяемого параметра.
valuesсловарь RouteValueDictionary, содержащий параметры для URL-адреса, в котором ключ — это имя параметра.
routeDirection — объект RouteDirection, указывающий, когда выполняется проверка ограничения: при обработке входящего запроса или при создании URL-адреса.

В качестве результата Match() возвращает значение типа booltrue, если запрос удовлетворяет данному ограничению маршрута, и false, если не удовлетворяет. Таким образом, метод вызывается для каждого параметра маршрута и, если для всех параметров метод Match() вернул true, то запрос пользователя может быть сопоставлен с данным маршрутом (выполняется код конечной точки).

Посмотрим, как мы можем создать свое ограничение, используя интерфейс IRouteConstraint.

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

Создадим собственное ограничение маршрута, которое будет проверять все символы в верхнем регистре и сравнивать их с некоторой секретной строкой. Для начала, создадим класс, реализующий интерфейс IRouteConstraint

public class UpperCaseSecret : IRouteConstraint
{
    private string _secret;
    
    public UpperCaseSecret(string secret)
    {
        _secret = secret.ToUpper();
    }

    public bool Match(HttpContext? httpContext, IRouter? route, string routeKey, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (!values.TryGetValue(routeKey, out var routeValue)) //если не смогли получить значение параметра
        {
            return false;
        }
        //если значение пустая строка или null
        string routeValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);
        if (string.IsNullOrEmpty(routeValueString)) 
        {
            return false;
        }
        //не нашли ни одного символа в верхнем регистре
        var upChars = routeValueString.Where(c => char.IsUpper(c));
        if (upChars.Any() == false) 
        {
            return false;
        }
        string secretCode = string.Concat(upChars);
        return secretCode == _secret;
    }
}

В конструктор класса передпется секретное слово. Затем, в методе Match() из параметра выбираются все символы в верхнем регистре с использованием метода расширения Linq Where и полученное множество символов сравнивается с секретным словом. Если слова совпадают, то метод возвращает true.

Чтобы применить пользовательское ограничение маршрута, тип ограничения необходимо зарегистрировать с помощью метода ConstraintMapв контейнере DI следующим образом:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRouting(options => options.ConstraintMap.Add("UpperCaseSecret", typeof(UpperCaseSecret)));

var app = builder.Build();
//здесь остальной код метода Main

Теперь применим наше ограничение маршрута для конечной точки:

app.MapGet("/tasks/{secretWord:UpperCaseSecret(hello)}", async (context) =>
{
    context.Response.ContentType = "text/html; charset=utf-8";

    StringBuilder sb = new StringBuilder();
    sb.AppendLine("<h1>Список задач</h1>");
    sb.AppendLine("<table>");
    sb.AppendLine("<tr><td>Id</td><td>Задача</td><td>Начало</td><td>Окончание</td></tr>");
    foreach (ToDoTask task in toDoList)
    {
        sb.AppendLine($"<tr><td>{task.Id}</td><td>{task.Name}</td><td>{task.Start: dd:mm:yyyy}</td><td>{task.End: dd:mm:yyyy}</td></tr>");
    }

    await context.Response.WriteAsync(sb.ToString());
});

здесь параметр secretWord имеет ограничение UpperCaseSecret. Если второй сегмент пути будет содержать символы с помощью которых мы сможем составить слово HELLO, то на страницу будет выведен список задач, который мы составляли в этой части. Посмотрим на результат:

Итого

Для создания собственных ограничений маршрутов мы должны реализоваться интерфейс IRouteConstraint и зарегистрировать полученный тип ограничения  с помощью метода ConstraintMap в контейнере DI. В конструкторы классов, реализующих IRouteConstraint можно передавать различные значения, которые будут влиять на результат выполнения метода IRouteConstraint.Match

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии