Содержание
В нашем случае, под понятием «маршрут» понимается URI на который пользователь отправляет запрос и ASP.NET Core сопоставляет этот маршрут с конкретным действием контроллера. В проектах ASP.NET Core Web API используется маршрутизация на основе атрибутов. Этот механизм маршрутизации включается в обязательном порядке, если контроллер использует атрибут [ApiController]
. В этой части мы рассмотрим один из вариантов настройки маршрутизации в проекте ASP.NET Core Web API — с использованием атрибута Route
.
Пример использования атрибута Route
Создадим новое приложение ASP.NET Core Web API и посмотрим на описание контроллера WeatherForecastController
, который создается по умолчанию в папке Controllers
:
[ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } } }
В этом контроллере атрибут Route
применен непосредственно к классу:
[Route("[controller]")]
В параметрах атрибута указано одно из зарезервированных имен системы маршрутизации ASP.NET Core — controller
. Это означает, что для все действия контроллера будут сопоставляться URI https://localhost[:port]/WeatherForecast
, то есть на место слова controller
будет подставляться имя контроллера без суффикса Controller
.
При использовании такого подхода к настройке маршрутизации в контроллере (когда атрибут Route
указывается только для класса котроллера) следует обратить внимание на следующий момент — действия в таком контроллере должны обрабатывать различные HTTP-методы. Это означает, что в контроллере не должно быть двух действий, обрабатывающих, например, GET-запросы. Для демонстрации, добавим в контроллер вот такое действие:
[HttpGet] public string GetNext() { return string.Empty; }
Теперь у нас в контроллере два действия — Get()
и GetNext()
обрабатывающих GET-запросы. Теперь попробуем выполнить запрос из HTTP-файла проекта и вместо ответа получим вот такую ошибку:
Два действия контроллера обрабатывают один и тот же HTTP-метод GET и имеют один и тот же путь из-за чего и возникает конфликт. Чтобы разрешить эту проблему мы должны либо переопределить метод HTTP, который будет обрабатываться действием, например, указать атрибут [HttpPost]
, что не всегда является возможным, либо указать для действия другой путь. Как разрешается такая проблема с помощью атрибута Route
разберемся далее.
Класс RouteAttribute
Класс RouteAttribute
является наследником класса Attribute
и предоставляет следующие свойства:
Name |
Имя маршрута |
Order |
Порядок маршрута. Сначала выполняются маршруты с более низким порядком. Если ни действие, ни контроллер не определяют порядок, используется значение по умолчанию 0 . |
Template |
Шаблон маршрута. |
Чаще всего используется только третье свойство этого класса — Template
, отвечающее за шаблон маршрута. При этом, атрибут Route
может применяться как к классу в целом, так и к отдельным его методам, а также использоваться для одной и той же цели несколько раз, о чем свидетельствует атрибут AttributeUsage
класса:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class RouteAttribute : Attribute, IRouteTemplateProvider
Так, например, для контроллера WeatherForecastController
мы могли бы применить атрибут Route
следующим образом:
[Route("[controller]")] [Route("Example")] public class WeatherForecastController : ControllerBase
В этом случае, наше приложение будет сопоставлять с одним и тем же действием два маршрута:
- /weatherforecast
- /example
Теперь, мы можем выполнить запрос, например, GET-запрос или по пути /WeatherForecast
или по пути /Example
и получить один и тот же результат. Возможность множественного использования атрибута для одной и той же цели (класса или метода) позволяет нам создавать гибкую систему маршрутизации в приложении.
Указание маршрутов для действия контроллера
Вернемся к примеру с ошибкой. Чтобы разместить в контроллере несколько действий, обрабатывающих одни и те же HTTP-запросы необходимо, чтобы у этих методов были различные пути. Попробуем изменить метод GetNext()
следующим образом:
[HttpGet] [Route("/next")] //новый путь public string GetNext() { return string.Empty; }
Теперь, чтобы выполнить запрос ко второму действию контроллера, мы должны составить вот такой запрос в http-файле проекта:
@WebApplication3_HostAddress = http://localhost:5225 GET {{WebApplication3_HostAddress}}/next/
Теперь можно снова запустить приложение и убедиться, что запросы к обоим действиям не вызывают ошибок. Таким образом, атрибут Route
, определенный для класса, будет содержать путь (или пути) к методам API по умолчанию, а атрибуты указанные для действий контроллера — переопределять путь по умолчанию. Также, мы могли бы использовать зарезервированное имя для атрибута Route
, примененного к действию:
[HttpGet] [Route("[action]")] //новый путь public string GetNext() { return string.Empty; }
Здесь action
— зарезервированное имя. В этом случае, путь будет складываться из пути, указанного в атрибуте для класса контроллера и имени метода. Не обязательно комбинировать атрибуты Route
для класса и метода. Мы можем, например, указать Route
только для класса (как это сделано в контроллере по умолчанию), а можем указать атрибуты только для действий контроллера:
[ApiController] // [Route("[controller]")] // [Route("example")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet] [Route("[action]")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } [HttpGet] [Route("[action]")] //новый путь public string GetNext() { return string.Empty; } [HttpPost] [Route("[action]")] public string Post() { return string.Empty; } }
Здесь стоит обратить внимание на то, что для всех трех действий контроллера необходимо указывать атрибут Route
. Методы API будут теперь выглядеть следующим образом:
/Get
/GetNext
/Post
Если для действия контроллера API не указан путь, то при запуске приложения мы получим ошибку. Например, уберем путь для действия Get()
:
[HttpGet] // [Route("[action]")] отсутствие маршрута приведет к ошибке при запуске приложения public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); }
При попытке запуска приложения мы увидим следующую ошибку:
Так как Get()
является действием контроллера (у метода класса определен атрибут [HttpGet]
), то у него обязательно должен быть определен маршрут.
Каким образом будет выводиться маршрут для действия?
Здесь следует различать два варианта работы ASP.NET Core:
- если шаблон маршрута для действия не содержит в начале символ «/», то шаблоны маршрута контроллера и действия складываются. Например, в нашем случае, шаблон маршрута не содержит лидирующего «/», следовательно, шаблоны маршрута для действия будут такими: [controller]/[action] или api/[controller]/[action]. В рабочем приложении пути запроса будут такими: tasks/get или api/tasks/get.
- если шаблон маршрута для действия содержит в начале символ «/», то шаблоном маршрута для действия будет только шаблон, указанный для действия контроллера.
Итого
В этой части мы рассмотрели основные моменты настройки системы маршрутизации в приложениях ASP.NET Core Web API на основе контроллеров. Для настройки маршрутов использовался атрибут Route
, который может применяться как к классам, так и отдельным методам (действиям) контроллера многократно. Приложения ASP.NET Core Web API используют маршрутизацию на основе атрибутов, поэтому для каждого действия контроллера указывается своя уникальная пара значений «путь/http-метод». Используя атрибут Route мы можем задавать путь к действиям контроллера по умолчанию или же указывать для одного и того же действия несколько путей. На этом, тема маршрутизации в контроллерах Web API не завершается — в следующей части мы рассмотрим ещё один способ настройки системы маршрутизации с использованием атрибутов.