Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
В Web API также, как и в MVC, доступна такая возможность, как атрибуты маршрутизации. В прошлых темах мы рассмотрели базовый механизм обработки запросов в Web API на примере приложения, которое позволяло выполнять стандартные операции чтения, добавления, обновления и удаления объектов. Все эти операции соответствуют определенному методу протокола HTTP. Но что если мы захотим определить дополнительные методы, которые работают с определенными данными? Например, в прошлой теме рассматривалась модель Book, в которой определено свойство для хранения авторов:
public class Book { public int Id { get; set; } public string Name { get; set; } public string Author { get; set; } public int Year { get; set; } }
Метод получения всех авторов в контроллере Web API мог бы выглядеть таким образом:
public class ValuesController : ApiController { BookContext db = new BookContext(); public IEnumerable<Book> GetBooks() { return db.Books; } public IEnumerable<string> GetAuthors() { return db.Books.Select(b => b.Author).Distinct(); } //остальные методы }
Однако если бы мы запустили приложение, то браузер нам бы отобразил ошибку, поскольку у нас два метода GetBooks и GetAuthors могут обрабатывать запросы GET. И система маршрутизации не сможет определить, какой именно метод выбрать. В этом случае нам надо указать дополнительный маршрут для сопоставления с новым методом. Для этого мы можем использовать, например, введенные в MVC5 атрибуты маршрутизации:
[Route("api/values/authors")] public IEnumerable<string> GetAuthors() { return db.Books.Select(b => b.Author).Distinct(); }
Теперь мы можем получить всех авторов с помощью запроса api/values/authors, а стандартный запрос api/values/ будет обращаться к первому методу get.
В отношении методов Web API действует та же логика атрибутов маршрутизации, что и по отношению к обычным контроллерам MVC. Подобным образом можно определить, например, метод, который будет получать автора по определенной id книги:
[Route("api/values/{id}/author")] public string GetAuthor(int id) { Book b = db.Books.Find(id); if (b != null) return b.Author; return ""; }
В определении сегмента id мы использовали ограничение, чтобы явно указать, что этот сегмент должен представлять целое число: id:int
. Кроме
int мы можем задать еще ряд ограничений по типу:
alpha: соответствует только алфавитным символам латинского алфавита. Например, {id:alpha}
bool: соответствует логическлму значению. Например, {id:bool}
datetime: соответствует значению DateTime. Например, {id:datetime}
decimal: соответствует значению decimal. Например, {id:decimal}
double: соответствует значению double. Например, {id:double}
float: соответствует значению float. Например, {id:float}
length: соответствует строке определенной длины, либо ее длина должна быть в определенном диапазоне.
Например, {id:length(5)}
или {id:length(5, 15)}
long: соответствует значению long. Например, {id:long}
max: соответствует значению int, которое не больше значения max. Например, {id:max(99)}
.
Аналогичным образом действует ограничение min, только оно указывает на минимально допустимое значение сегмента.
maxlength: соответствует строке, длина которой не больше определенного значения. Например, {id:maxlength(20)}
.
Аналогичным образом работает ограничение minlength, указывая на минимально допустимую длину строки
range: указывает на диапазон, в пределах которого должно находиться значение сегмента. Например, {id:range(5, 20)}
regex: соответствует регулярному выражению. Например, {id:regex(^\d{3}-\d{3}-\d{4}$)}
Как и при определении маршрута, мы можем задать значения для параметров по умолчанию:
[Route("{id:int}/{name=volga}")] public string Test(int id, string name) { return id.ToString() + ". " + name; }
Так, если строка запроса не будет содержать последний параметр, то вместо него будет использоваться строка "volga".
Выше приводился пример атрибута маршрутизации с название контроллера в начале: [Route("Home/{id:int}/{name}")]
. Но если у нас вдруг
есть несколько подобных действий, обращение к которым должно начинаться с "Home", то удобно использовать префиксы:
[RoutePrefix("home")] public class HomeController : ApiController { [Route("{id:int}/{name}")] public string Test(int id, string name) { return id.ToString() + ". " + name; } [Route("{id:int}")] public string Sead(int id) { return id.ToString(); } [Route("~/lol/twit/{id:int}")] public string Twit(int id) { return id.ToString(); } }
Теперь запрос к обоим методам должен начинаться с Home: "Home/5/fds" или "Home/5". При этом префикс не обязательно должен совпадать с именем контроллера, а может иметь любое значение.
Последний маршрут устраняет действие префикса с помощью знака тильды (~) в начале маршрута. И чтобы к этому методу обратиться, надо будет использовать запрос http://localhost:6392/lol/twit/2.