Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core 7
Центральным звеном в архитектуре ASP.NET Core MVC является контроллер. При получении запроса система маршрутизации выбирает для обработки запроса нужный контроллер и передает ему данные запроса. Контроллер обрабатывает эти данные и посылает обратно результат обработки.
В ASP.NET Core MVC контроллер представляет обычный класс на языке C#, который наследуется от абстрактного базового класса Microsoft.AspNetCore.Mvc.Controller. По умолчанию проект ASP.NET Core MVC содержит как минимум один контроллер - HomeController:
public class HomeController : Controller { private readonly ILogger<HomeController> _logger; public HomeController(ILogger<HomeController> logger) { _logger = logger; } public IActionResult Index() { return View(); } public IActionResult Privacy() { return View(); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } }
В данном случае мы видим, что констроллер имеет конструктор, через который посредством механизма dependency injection передается сервис ILogger, используемый для логгирования. Также контроллер определяет три метода - Index, Privacy и Error.
При использовании контроллеров существуют некоторые условности. Во-первых, в проекте контроллеры помещаются в каталог Controllers. И во-вторых, по соглашениям об именовании названия контроллеров обычно оканчиваются на суффикс "Controller", остальная же часть до этого суффикса считается именем контроллера, например, HomeController. Но в принципе эти условности необязательны.
Но есть также и обязательные условности, которые предъявляются к контроллерам. В частности, класс контроллера должен удовлетворять как минимум одному из следующих условий:
Класс контроллера имеет суффикс "Controller"
public class HomeController { //............ }
Класс контроллера наследуется от класса, который имеет суффикс "Controller"
public class Home : Controller { //............. }
К классу контроллера применяется атрибут [Controller]
[Controller] public class Home { //.................. }
Если нам нужен еще один контроллер, то мы можем добавить в папку Controllers новый класс, который будет наследоваться от класса Controller
. Либо мы
можем использовать готовый шаблон Controller Class:
Контроллер, как и любой класс на языке C#, может иметь поля, свойства, методы. По умолчанию HomeController имеет четыре метода, которые можно назвать действиями. Действия контроллера - это публичные методы, которые могут сопоставляться с запросами. Например, стандартный контроллер содержит метод Index - он имеет модификатор public и поэтому может использоваться для обработки запроса.
Чтобы обратиться контроллеру из веб-браузера, нам надо в адресной строке набрать адрес_сайта/Имя_контроллера/Действие_контроллера. Так, по запросу адрес_сайта/Home/Index система маршрутизации по умолчанию вызовет метод Index контроллера HomeController для обработки входящего запроса. Например:
Однако такое сопоставление строки url с названием контроллера и его метода происходит благодаря системе маршрутизации. Если мы обратимся к классу Startup и
его методу Configure()
, то мы можем найти там определение единственного для приложения маршрута:
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); });
Метод endpoints.MapControllerRoute
добавляет один маршрут с именем
default и шаблоном "{controller=Home}/{action=Index}/{id?}"
. Данный шаблон устанавливает трехсегментную структуру строки запроса:
controller/action/id. То есть в начале идет название контроллера, затем название действия, и далее может идти необязательный параметр id.
Собственно поэтому система может соотнести запрос типа localhost:xxxx/Home/Index с контроллером и его действием.
Однако не все методы контроллера являются действиями. Контроллер также может иметь непубличные методы - такие методы не рассматриваются как действия и соответственно не могут соотноситься с запросами. Например, определим в контроллере следующий метод:
protected internal string Hello() { return "Hello ASP.NET"; }
Поскольку его модификатор отличается от public
, то мы не сможем обратиться к этому методу с запросом localhost:xxxx/Home/Hello.
Хотя такие не публичные методы также могут быть полезными - в них можно определять какие-нибудь промежуточные вычисления и затем использовать в действиях контроллера.
При этом если мы изменим модификатор метода на public
, то метод Hello станет полноценным действием:
Возможно, сопоставление по умолчанию бывает не всегда удобно. Например, у нас есть класс в папке Controllers, но мы не хотим, чтобы он мог обрабатывать запрос и использоваться как контроллер. Чтобы указать, что этот класс не является контроллером, нам надо использовать над ним атрибут [NonController]:
[NonController] public class HomeController : Controller { //........... }
Аналогично, если мы хотим, чтобы какой-либо публичный метод контроллера не рассматривался как действие, то мы можем использовать над ним атрибут NonAction:
[NonAction] public string Hello() { return "Hello ASP.NET"; }
Атрибут [ActionName] позволяет для метода задать другое имя действия. Например:
[ActionName("Welcome")] public string Hello() { return "Hello ASP.NET"; }
В этом случае чтобы обратиться к этому методу, надо отправить запрос localhost:xxxx/Home/Welcome. А запрос localhost:xxxx/Home/Hello работать не будет.
В прошлой главе для обработки запросов использовалась следующая пара методов:
[HttpGet] public IActionResult Buy(int? id) { if (id == null) return RedirectToAction("Index"); ViewBag.PhoneId = id; return View(); } [HttpPost] public string Buy(Order order) { db.Orders.Add(order); // сохраняем в бд все изменения db.SaveChanges(); return "Спасибо, " + order.User + ", за покупку!"; }
Несмотря на то, что здесь два разных метода, но они в соответствии с именем образуют одно действие Buy. Допустимо определять в контроллере методы с одним и тем же именем, только в этом случае они должны различаться по параметрам, как в данном случае.
Кроме того, методы в рамках одного действия могут обслуживать разные запросы. Для указания типа запроса HTTP нам надо применить к методу один из атрибутов: [HttpGet], [HttpPost], [HttpPut], [HttpDelete] и [HttpHead]. Если атрибут явным образом не указан, то метод может обрабатывать все типы запросов: GET, POST, PUT, DELETE.