Как создать промежуточное ПО с конечными точками API в .NET Core

Я создал веб-приложение с веб-API. Приложение содержит некоторые Controllers например TodoController:

namespace TodoApi.Controllers
{
    [Route("api/[controller]")]
    public class TodoController : Controller
    {
        private readonly TodoContext _context;

        public TodoController(TodoContext context)
        {
            _context = context;
        }       

        [HttpGet]
        public IEnumerable<TodoItem> GetAll()
        {
            return _context.TodoItems.ToList();
        }
    }
}

Если я создаю запрос GET - /api/todo - я получаю список задач из базы данных.

У меня есть список контроллеров и конечных точек API, как указано выше.

Я хотел бы распространить этот API на другое приложение, идеально похожее на промежуточное программное обеспечение - моя идея заключается в том, чтобы зарегистрироваться в Startup.cs следующим образом:

public void ConfigureServices(IServiceCollection services)
{
  services.AddTodoApi();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
  app.UseTodoApi();
}

Это будет отличный вариант использования моего API, но я не знаю, как конечные точки API этого контроллера переписываются как промежуточное ПО и возвращают те же данные JSON, что и при использовании классического Controllers.

Как я могу записать middleware в .NET Core для создания API endpoints?


person Jenan    schedule 08.08.2017    source источник


Ответы (2)


Вместо отдельного промежуточного программного обеспечения вы можете настроить промежуточное программное обеспечение MVC для обнаружения контроллеров из другой сборки:

// using System.Reflection;

public void ConfigureServices(IServiceCollection services)
{
    ...
    services
      .AddMvc()
      .AddApplicationPart(typeof(TodoController).GetTypeInfo().Assembly);

Контроллеры являются частью промежуточного программного обеспечения MVC, они не являются отдельной частью конвейера запросов (но это и есть промежуточное программное обеспечение). Когда вы регистрируете пользовательское промежуточное ПО, оно по умолчанию вызывается при каждом запросе, и у вас есть HttpContext context в качестве входного параметра для работы с/редактирования данных запроса/ответа. Но ASP.NET Core предоставляет расширения Map*, которые используются как соглашение для разветвления трубопровода.

Карта разветвляет конвейер запросов на основе совпадений заданного пути запроса. Если путь запроса начинается с заданного пути, ветвь выполняется.

Пример:

private static void HandleMapTodo(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("/api/todo was handled");
    });
}

public void Configure(IApplicationBuilder app)
{
    app.Map("/api/todo", HandleMapTodo);
}

Обратите внимание, что поскольку промежуточное ПО ничего не знает о промежуточном ПО MVC, у вас есть доступ только к «сырым» запросам и нет таких функций, как привязка модели или фильтры действий MVC.

person Set    schedule 09.08.2017
comment
Возможно ли теоретически с помощью .AddApplicationPart добавить несколько MVC Razor Views? Если да, можете ли вы дать образец? - person Jenan; 09.08.2017
comment
@Jenan не использовал это, поэтому не уверен на 100%, но теоретически это должно работать, если у вас есть представления, правильно расположенные в опубликованном выводе. Подумайте, может быть, задать другой вопрос, если вы попытаетесь найти какие-либо проблемы с представлениями... - person Set; 09.08.2017

Поскольку это похоже на идеальный подход к микросервисам (похожий на то, что моя команда делает прямо сейчас), я бы создал клиентскую сборку, которая может использовать ваш API, тот, который содержит ваш TodoController, если вы определяете контракт и интерфейс, для этого API, вы можете зарегистрировать его в другой своей сборке, поскольку это промежуточное ПО, а также вы можете имитировать это поведение в своих модульных тестах.

Итак, как я уже сказал, вы можете внедрить свой клиент в метод ConfigureServices, вы можете создать:

public static IServiceCollection AddTodoRestClient(this IServiceCollection services)
{
    services.AddSingleton<ITodoRestClient, TodoRestClient>();
    return services;
}

Также учтите, что вам нужно будет указать enpoint, поэтому это может выглядеть так:

public static IServiceCollection AddConfiguredTodoClient(this IServiceCollection services, string todoEndpoint)
{
    AddTodoClient(services);
    ITodoRestClient todoRestClient = services.BuildServiceProvider().GetService<ITodoRestClient>();
    // Imagine you have a configure method...
    todoRestClient.Configure(services, todoEndpoint);
    return services;
}

Вы можете создать эти методы в классе TodoRestClientInjector и использовать их в методе Configure при запуске.

Я надеюсь, что это помогает

--- ПОДРОБНЕЕ, ЧТОБЫ ОТВЕЧАТЬ НА КОММЕНТАРИИ ---

Для меня TodoClient — это клиентская библиотека Rest, которая реализует вызовы ToDo API (я отредактировал предыдущий код, чтобы он был TodoRestClient), такие методы, как, например, CreateTodoItem(TodoDto todoItem), реализация которого будет вызывать TodoController.Post([FromBody] item) или GetTodos(), который вызовет TodoController.Get() и т.д. и т.п....

Что касается точек... Этот подход подразумевает наличие (по крайней мере) двух разных приложений (приложений .NET Core), с одной стороны, приложения ASP NET Core, в котором есть ваш TodoController, а с другой стороны, консольное приложение или другое приложение ASP NET. Core API, в каком классе запуска вы будете выполнять настройку клиента Rest (клиент Todo Rest)...

В подходе микросервисов с использованием docker в среде разработки вы будете использовать docker-compose-yml, но в традиционном подходе вы будете использовать конкретные порты для определения конечных точек...

Итак, представьте, что во второй службе у вас есть контроллер, которому нужно использовать TodoController, для достижения которого я буду использовать описанный выше подход, и «SecondController» будет выглядеть так:

  public class SecondController : Controller
    {
        private readonly SecondContext _context;
        private readonly TodoRestClient _todoRestClient;

        public TodoController(SecondContext context, ITodoRestClient todoRestClient)
        {
            _context = context;
            _todoRestClient= todoRestClient;
        }       

// Whatever logic in this second controller... but the usage would be like:

_todoRestClient.GetTodos()

}

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

Опять же, я надеюсь, что это поможет.

Хуан

person Juan    schedule 09.08.2017
comment
В ПОРЯДКЕ. Я, вероятно, вижу этот системный подход DI, но как я могу создать конкретные конечные точки API? Где связан метод контроллеров? Не могли бы вы подробнее объяснить свою идею с использованием API? Что такое TodoClient? - person Jenan; 09.08.2017
comment
В дополнение к моему последнему редактированию: а) Создайте клиент REST в .NET Core: docs.microsoft.com/en-us/dotnet/csharp/tutorials/ b) Polly, библиотека устойчивости NET и обработки временных сбоев: github.com/App-vNext/Polly - person Juan; 09.08.2017