Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
Итак, в прошлой теме были определены два маршрута:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "ActionRoute", routeTemplate: "api/{controller}/{action}" ); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); } }
Пусть у нас есть оба метода, которые обрабатывают запросы GET и соответствуют обоим маршрутам:
public class ValuesController : ApiController { public string GetValue() { return "getvalue"; } public string Get(int id) { return "id: " + id.ToString(); } }
Но в данном случае работать будет только один из них, а именно первый метод - GetValue. И если мы обратимся к приложению с запросом api/values/3, то естественно получим ошибку.
Дело в том, что все маршруты регистрируются в порядке определения, и первым в коллекции маршрутов в данном случае будет маршрут "ActionRoute". При поступлении запроса система маршрутизации берет первый соответствующий запросу маршрут. Так как первым будет стоять "ActionRoute", то он и будет обрабатывать как запрос api/values/getvalue, так и запрос api/values/3
Почему первый маршрут будет обрабатывать также и второй запрос? Дело в том, что запрос api/values/3 также соответствует шаблону api/{controller}/{action}. В данном случае сегмент {controller} сопоставляется со значением "values", а сегмент {action} со значением "3". Но естественно у нас нет в контроллере метода с названием "3", поэтому приложение возвратит ошибку. Чтобы избавиться от ошибки, мы можем использовать ограничения.
Ограничения маршрутов позволяют более точно настроить применение маршрутов. Web API предоставляет набор встроенных ограничений:
AlphaRouteConstraint: запрос соответствует маршруту, если сегмент, к которому применяется ограничение, состоит только из алфавитных символов
BoolRouteConstraint: запрос соответствует маршруту, если сегмент, к которому применяется ограничение, имеет значение true или false
DateTimeRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет объект DateTime
DecimalRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет объект decimal
DoubleRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет объект double
FloatRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет объект float
IntRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет объект int
LongRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет объект long
HttpMethodConstraint: маршрут соответствует запросам только определенного типа (GET, POST)
MaxLengthRouteConstraint / MinLengthRouteConstraint: определяют максимальную и минимальную длину сегмента в символах
MaxRouteConstraint / MinRouteConstraint: определяют максимальное и минимальное числовое значение для сегмента
RangeRouteConstraint: запрос соответствует маршруту, если сегмент предоставляет числовое значение int в определенном диапазоне
RegexRouteConstraint: задает регулярное значение, которому должен соответствовать сегмент
Используем ограничения, чтобы решить проблему сопоставления маршрутов, обозначенную в начале темы:
using System.Web.Http.Routing.Constraints; public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "ActionRoute", routeTemplate: "api/{controller}/{action}", defaults: new { }, constraints: new { action = new AlphaRouteConstraint() } ); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new {}, constraints: new { id = new IntRouteConstraint() } ); } }
Поскольку в первом маршруте метод должен состоять из алфавитных символов, то к нему применяется ограничение AlphaRouteConstraint. Так как значения по умолчанию
здесь нам не нужны, то мы оставляем параметр defaults: new { }
пустым.
Во-втором случае параметр id должен представлять число, поэтому к нему применяется ограничение IntRouteConstraint.
Все классы ограничений являются реализациями интерфейса IHttpRouteConstraint, расположенного в пространстве имен System.Net.Http
:
public interface IHttpRouteConstraint { bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection); }
Интерфейс IHttpRouteConstraint
определяет метод Match, который принимает следующие параметры:
request
: данные запроса в виде объекта HttpRequestMessage
route
: объект маршрута IHttpRoute
, который используется для обработки запроса
values
: словарь, содержащий наборы пар "название параметра - ограничение параметра"
routeDirection
: объект HttpRouteDirection
указывает, должен ли маршрут применяться к входящим запросам или используется
для генерации исходящих ссылок URL
И чтобы создать свое ограничение, нам надо реализовать данный интерфейс. Добавим в проект специальную папку. Назовем ее, например, Util. И добавим в нее новый класс CustomConstraint:
using System.Collections.Generic; using System.Net.Http; using System.Web.Http.Routing; namespace WebApiApp.Util { public class CustomConstraint: IHttpRouteConstraint { private string uri; public CustomConstraint(string uri) { this.uri = uri; } public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) { return !(uri == request.RequestUri.AbsolutePath); } } }
В данном случае в конструктор класс принимает некоторый путь, который не должен совпадать с запрошенным ресурсом. И если он не совпадает, то метод Match возвращает true.
Применим ограничение к маршруту:
config.Routes.MapHttpRoute( name: "ActionRoute", routeTemplate: "api/{controller}/{action}", defaults: new {}, constraints: new { action = new AlphaRouteConstraint(), myConstraint = new CustomConstraint("/api/values/get") } );
Теперь все запросы "/api/values/get" будут игнорироваться.