ASP.NET Core — создание API, маршруты, передача данных

В данной статье я хочу показать, как можно создавать API в ASP.NET Core. Для начала хочу обратить внимание на один момент, в предыдущей версии ASP.NET существует разграничение между ASP.NET Web API и ASP.NET MVC. В ASP.NET Core всё это объединено в ASP.NET Core MVC, все контроллеры наследуются от Microsoft.AspNetCore.Mvc.Controller.

ASP.NET Core MVC

Чтобы начать работать с ASP.NET Core MVC необходимо зайти в файл Startup.cs и в методе ConfigureServices добавить services.AddMvc(); В методе Configure добавить app.UseMvc();

Чтобы настроить маршрутизацию вы можете использовать маршрутизацию на основе соглашений и маршрутизацию на основе атрибутов. Использование смешанной маршрутизации так же допускается. Например:

app.UseMvc(config =>
{
    config.MapRoute(name: "Default",
        template: "{controller}/{action}/{id?}",
        defaults: new {Controller = "Home", Action = "Index"});
});

Можно обойтись без параметра defaults, записав как:

app.UseMvc(config =>
{
    config.MapRoute(name: "Default",
        template: "{controller=Home}/{action=Index}/{id?}");
});

Если рассмотреть это немного детальнее, то здесь:

  • {controller=Home} определяет Home в качестве объекта controller по умолчанию.
  • {action=Index} определяет Index в качестве объекта action по умолчанию.
  • {id?} определяет необязательный параметр id. Убрав ? параметр станет обязательным.

Данный маршрут очень распространён и, фактически, является стандартом. Из за этого появился новый метод: app.UseMvcWithDefaultRoute(); Он равносилен вышеописанному маршруту.

Код самого метода можно посмотреть здесь: https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs

public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app)
{
    if (app == null)
    {
        throw new ArgumentNullException(nameof(app));
    }

    return app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Маршрутизация с помощью атрибутов

Атрибуты можно добавлять как непосредственно на сам метод

[Route("")]
[Route("Home")]
public IActionResult Index(int id)
{
    return View();
}

Так и на контроллер целиком, тогда итоговый маршрут к методу будет включать заданный в атрибуте для контроллера и заданный непосредственно в самом методе:

[Route("Home")]
public class HomeController : Controller
{
    [Route("Index")]
    public IActionResult Index(int id)
    {
        return View();
    }
}

Если же вы хотите, чтобы использовалось имя вашего контроллера и его не нужно было бы менять в маршруте, например, когда сам контроллер изменит имя, то можно прописать [controller] в маршруте.

[Route("api/[controller]")]
public class HomeController : Controller
{
    [Route("index")]
    public IActionResult Get(int id)
    {
        return View();
    }
}

Маршрутизация с помощью атрибутов Http[Verb]

Вы можете пометить, какой тип HTTP запроса будет использовать ваш метод. Можно использовать атрибуты HttpGet, HttpPost, HttpPut, HttpPatch, HttpDelete

[HttpGet("all")]
public IActionResult GetAll()
{
    var result = GetResultData();
    return Ok(result);
}

Сейчас давайте добавим новый контроллер UserController, который будет использоваться в качестве API для получения данных пользователей.

[Route("api/[controller]")]
public class UsersController : Controller
{
    [HttpGet()]
    public IActionResult GetAll()
    {
        var users = UsersData.GetUsers();
        return Ok(users);
    }
}

Чтобы не добавлять работу с базой данных на данном этапе, добавлю данный класс, который будет содержать некоторые данные пользователей:

public class User
{
    public int Id;
    public string Name;
    public string Phone;
}

public class UsersData
{
    public static List GetUsers()
    {
        return new List()
        {
            new User() { Id = 1, Name = "Andrei", Phone = "+71111111111" },
            new User() { Id = 2, Name = "Gennady", Phone = "+72222222222" },
            new User() { Id = 3, Name = "Basil", Phone = "+73333333333" },
            new User() { Id = 4, Name = "Konstantin", Phone = "+74444444444" },
            new User() { Id = 5, Name = "Dmitriy", Phone = "+75555555555" }
        };
    }
}

Выполнив GET запрос /api/users в ответ получу список всех пользователей

Список всех пользователей в API запросе ASP.NET Core MVC

Добавлю метод для получения данных о конкретном пользователе, указав его идентификатор

[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    var user = UsersData.GetUsers().FirstOrDefault(x=>x.Id == id);
    return Ok(user);
}

Теперь, выполнив запрос /api/users/1 получим в результате

Получение конкретного пользователя в API запросе ASP.NET Core MVC

Сейчас, если ввести идентификатор пользователя которого нет, то в ответ вернёт пустое значение с 204 кодом (No Content). Чтобы возвращало ответ с 404 кодом, можно воспользоваться методом NotFound()

[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
    var user = UsersData.GetUsers().FirstOrDefault(x=>x.Id == id);
    if (user == null)
    {
        return NotFound();
    }
    return Ok(user);
}

Результат:

Получение конкретного пользователя в API запросе ASP.NET Core MVC

Здесь стоит обратить внимание, что само сообщение об ошибке не было передано, так как по умолчанию не будет предоставляться страница для их отображения. Чтобы начать их отображать достаточно в конфигурацию добавить app.UseStatusCodePages();

По умолчанию в ASP.NET Core данные сериализуются и десериализуются в JSON, но вы легко можете использовать другие форматы, например, XML. Для этого необходимо добавить новый OutputFormatters для XML это будет XmlDataContractSerializerOutputFormatter. Это позволит в отдаваемых данных использовать XML

services.AddMvc().AddMvcOptions(o => o.OutputFormatters.Add(
                new XmlDataContractSerializerOutputFormatter()));

В таком виде, если мы не удаляем работу с JSON, а добавляем XML, то будет использоваться принцип согласования контента. При обработке входящих данных будет использоваться Content-type заголовок, для исходящих данных заголовок Accept

Получение XML данных используя заголовок Accept в API запросе ASP.NET Core MVC

Также всё это можно настраивать. Например, вы могли заметить, что в JSON имена переменных начинаются с маленькой буквы, тогда как в модели они большие. Если для вас это критично, можете изменить эту логику, делается это следующим образом.

services.AddMvc().AddJsonOptions(o =>
    {
        var castedResolver = o.SerializerSettings.ContractResolver as DefaultContractResolver;
        if (castedResolver != null)
        {
            castedResolver.NamingStrategy = null;
        }
    }
);

Результат будет следующим:

Результат изменения параметров обработки JSON в ASP.NET Core MVC

Итог:

Был рассмотрен минимальный функционал, позволяющий создавать удобные API. Используя возможности ASP.NET Core MVC вы можете настраивать возможности под требуемые задачи. Для построения грамотных систем необходимо использовать возможности маршрутизации. Можно использовать маршрутизацию на основе соглашений и атрибутов. Если вы можете работать с различными форматами данных, то будет использоваться согласование контента, на основе заголовков, позволяя пользователю вашего API получать данные в нужном ему формате.

О более сложных приёмах работы с API в ASP.NET Core  поговорим в следующих статьях.

Приятного программирования.

Добавить комментарий