зарегистрировать контроллер веб-API из библиотеки классов

У меня есть рабочий проект .NET Core, и я хочу добавить библиотеку, предоставляющую несколько конечных точек HTTP. Я должен остаться с рабочим проектом, я не могу изменить его на проект веб-API. Что я сделал до сих пор:

  • Я создал рабочий проект
  • Я создал проект библиотеки
  • Я добавил ссылку на библиотеку в рабочий проект
  • В Worker.csproj и Lib.csproj я добавил <FrameworkReference Include="Microsoft.AspNetCore.App" /> в группу элементов, чтобы получить доступ к материалам веб-конструктора.
  • Я установил пакет Microsoft.AspNetCore.Mvc.Core в проект библиотеки
  • В проект библиотеки я добавляю несколько классов расширений и контроллер веб-API для целей тестирования.

.

public static class IApplicationBuilderExtensions
{
    public static IApplicationBuilder AddLibrary(this IApplicationBuilder applicationBuilder)
    {
        applicationBuilder.UseRouting();
        applicationBuilder.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        return applicationBuilder;
    }
}

public static class IServiceCollectionExtensions
{
    public static IServiceCollection AddLibrary(this IServiceCollection services)
    {
        services.AddMvc(); // this might be not needed
        services.AddControllers();
        return services;
    }
}

public static class KestrelServerOptionsExtensions
{
    public static void AddLibrary(this KestrelServerOptions kestrelServerOptions)
    {
        kestrelServerOptions.ListenLocalhost(5000); // value from config
    }
}

[ApiController]
[Route("[controller]")]
public class UsersController : ControllerBase
{
    [HttpGet]
    public async Task<ActionResult> Test()
    {
        return Ok();
    }
}
  • В рабочем проекте я создал класс Startup

.

internal class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddLibrary();
    }

    public void Configure(IApplicationBuilder applicationBuilder)
    {
        applicationBuilder.AddLibrary();
    }
}
  • В рабочем проекте я изменил класс Program на

.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webHostBuilder =>
            {
                webHostBuilder.UseKestrel(kestrelServerOptions =>
                {
                    kestrelServerOptions.AddLibrary();
                }).UseStartup<Startup>();
            })
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
            });
}
  • Я запускаю проект и вызываю GET http://localhost:5000/users
  • Я ожидал 200, но получил 404, и отладчик не попал в конечную точку контроллера в проекте библиотеки.

Кто-нибудь знает, что мне не хватает?

Я могу Добавить конечную точку контроллера веб-API в Kestrel worker. проект, но я не могу добавить веб-контроллеры в проект библиотеки и вызывать их из библиотеки.


person Olaf Svenson    schedule 01.10.2020    source источник
comment
Хм, я сделал то же самое, и я могу воспроизвести его проблему ...   -  person Question3r    schedule 01.10.2020
comment
Я могу воспроизвести эту проблему, в журналах я вижу, что маршруты для / Users не найдены, но я не могу понять, почему. Вы можете заставить его работать, изменив свой рабочий проект на веб-проект, используя Microsoft.NET.Sdk.Web вместо Microsoft.NET.Sdk.Worker. С этим Sdk рабочая служба будет работать так же. Это не ответ на ваш вопрос, но это обходной путь.   -  person J.Loscos    schedule 02.10.2020
comment
да спасибо за ответ. К сожалению, я не могу изменить его на проект веб-API.   -  person Olaf Svenson    schedule 02.10.2020
comment
попробуйте изменить [Route("[controller]")] на [Route("[controller]/[action]")]. Я надеюсь, что это устранит ошибку. Спасибо,   -  person Muhammad Aftab    schedule 04.10.2020


Ответы (2)


У меня есть рабочее решение на моем компьютере.

Решение проблемы

Оказывается, это на самом деле очень просто. MVC загружает ApplicationPart< /a>s из разных сборок. В обычной настройке веб-приложения это кажется правильным, даже при использовании библиотек классов Razor. Однако при использовании рабочей службы и библиотеки или библиотеки классов Razor это не работает, поэтому маршрут для вашего контроллера не зарегистрирован.

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

public static class IServiceCollectionExtensions
{
    public static IServiceCollection AddLibrary(this IServiceCollection services)
    {
        services
            .AddControllers()
            // Notice the assembly is the type of this class, as this
            // is the assembly the controller is in.
            // You'll have to call this for every assembly you have
            // controllers in, except for any controllers
            // you might put in your worker service project.
            .AddApplicationPart(typeof(IServiceCollectionExtensions).Assembly);

        return services;
    }
}

Теперь запуск приложения и отправка запроса на http://localhost:5000/users будут работать. Я подтвердил, что это работает как для библиотек, так и для библиотек классов Razor.

маршрутизация рабочих служб

Я также должен сказать, что контроллеры в самом проекте рабочего сервиса будут работать как обычно — для них не требуется специальной настройки.

Вещи, которые вам не нужны

  1. Вам не нужен вызов services.AddMvc() в IServiceCollectionExtensions. Вызов этого без параметров — это аналогично вызову AddControllersWithViews() и AddRazorPages().
  2. Вам не нужен пакет Microsoft.AspNetCore.Mvc.Core в вашем библиотечном проекте — мой работает нормально только с добавленной вами ссылкой на фреймворк. Единственный пакет, который у меня есть, предназначен для Microsoft.AspNetCore.Server.Kestrel.Core.
person John H    schedule 04.10.2020
comment
эй, спасибо за ваше решение! добавление этого .AddApplicationPart(typeof(IServiceCollectionExtensions).Assembly); решило проблему. - person Olaf Svenson; 05.10.2020
comment
Но остался один вопрос: kestrelServerOptions.ListenLocalhost(myValueFromConfig); тем самым сервер начинает слушать два порта... 5000 и мое значение в конфиге... Сделал скриншот из консоли i.imgur.com/AvhcUDh.png - person Olaf Svenson; 05.10.2020
comment
поэтому прослушиватель отлично работает для моего настроенного порта, но почему он также прослушивает порт 5000? - person Olaf Svenson; 05.10.2020
comment
@OlafSvenson Нет проблем. Kestrel слушает у меня только один порт. Я только что попытался позвонить kestrelServerOptions.ListenLocalhost(n) с двумя разными номерами, и он привязан к двум разным портам. Так вы случайно позвонили дважды? (Может один раз из библиотеки, а один раз в вашей рабочей Program?) - person John H; 05.10.2020
comment
ах точно, извини мой плохой! - person Olaf Svenson; 05.10.2020
comment
@OlafSvenson Нет проблем! :) - person John H; 05.10.2020

Во-первых, ваш вопрос выглядит идентично указанному вопросу, и единственная разница заключается в вопросе. Итак, вы не сказали, почему ваш веб-контроллер не работает и какую ошибку вы получили?

Тогда вы не сказали, какую версию ASP.NET Core вы используете? Это важный фактор, потому что 2.x и 3.x — разные вещи.

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

Я использую .NET Core 5 RC, и у меня работает веб-контроллер. Единственное, с чем мне пришлось иметь дело, это компилировать представления Razor, потому что Sdk.Worker не компилирует представления по умолчанию. Когда я нажимаю на контроллер, я получаю сообщение об ошибке 500, говорящее, что представление не может быть найдено, хотя представление находится в правильном месте. Затем я использовал Компиляция среды выполнения Razor, и у меня все начинает работать.

Это все, что у меня есть:

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(webHost =>
    {
        webHost.UseKestrel(opt =>
        {
            opt.ListenLocalhost(5000);
        });
        webHost.UseStartup<Startup>();
    })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Worker>();
    });

Startup.cs

class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews().AddRazorRuntimeCompilation();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllerRoute(
                name: "MyArea",
                pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");
            endpoints.MapRazorPages();
        });
    }
}

TestController.cs

[Area("My")]
[Route("users")]
public class TestController : Controller
{
    [Route("")]
    public IActionResult Index()
    {
        return View();
    }
}
person weichch    schedule 04.10.2020