使用 IdentityServer 在 .NET API 上找不到控制器路由

Controller routes not being found on .NET API with IdentityServer

我有一个 .NET API 服务,我设置它来处理身份验证和一些用户管理端点。服务配置如下:

public class Program {

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}

public class Startup {

    public Startup(IConfiguration configuration) {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services) {

        _ = services.AddScoped<IUserRequester, UserRequester>(_ =>
            new UserRequester(Configuration.GetSection("AzureTableStore.UserLogin").Get<TableStoreConfiguration>()));
        _ = services.AddControllers().AddNewtonsoftJson(options =>
            options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());

        X509Certificate2 certData = DownloadCertificate(Configuration.GetSection("APICertificate").Get<Secret>());
        IIdentityServerBuilder builder = services.AddIdentityServer().AddSigningCredential(certData);
        builder.AddInMemoryClients(Clients.Get());
        builder.AddInMemoryIdentityResources(Identities.Get());
        builder.AddInMemoryApiResources(Apis.GetResources());
        builder.AddInMemoryApiScopes(Apis.GetScopes());

        builder.Services.Configure<TableStoreConfiguration>(Configuration.GetSection("AzureTableStore.UserLogin"));
        builder.Services.Configure<RedisConfiguration>(Configuration.GetSection("RedisCache"));
        builder.Services.AddTransient<IRedisConnection, RedisConnection>();
        builder.Services.AddTransient<IUserRequester, UserRequester>();
        builder.Services.AddTransient<IProfileService, ProfileService>();
        builder.Services.AddTransient<IResourceOwnerPasswordValidator, PasswordValidator>();
        builder.Services.AddTransient<IAuthorizationCodeStore, AuthorizationCodeStore>();
        builder.Services.AddTransient<IReferenceTokenStore, ReferenceTokenStore>();
        builder.Services.AddTransient<IRefreshTokenStore, RefreshTokenStore>();
        builder.Services.AddTransient<IUserConsentStore, UserConsentStore>();

        services.AddSwaggerGen(c => c.SwaggerDoc("v1", new OpenApiInfo { Title = "Authentication", Version = "v1" }));
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
        if (env.IsDevelopment()) {
            app.UseDeveloperExceptionPage();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Authentication v1"));
        }

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseIdentityServer();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
            endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller}/{action=Index}/{id?}"));
    }

    private static X509Certificate2 DownloadCertificate(Secret secret) {
        KeyVaultSecret key = new Provider(secret.KeyVaultName).GetSecretAsync(secret.SecretName).Result;
        return new X509Certificate2(Convert.FromBase64String(key.Value), string.Empty, X509KeyStorageFlags.UserKeySet);
    }
}

我的 API 控制器看起来像这样(省略函数体):

[Route("users")]
[ApiController]
internal sealed class UserController : ControllerBase {

    private Data.IUserRequester Requester { get; }

    public UserController(Data.IUserRequester requester) {
        Requester = requester ?? throw new ArgumentNullException(nameof(requester));
    }

    [HttpGet("{email}")]
    public async Task<IActionResult> GetUserAsync(string email, CancellationToken token) {
        ...
    }

    [HttpPost]
    public async Task<IActionResult> AddUserAsync(User input, CancellationToken token) {
        ...
    }

    [HttpDelete]
    public async Task<IActionResult> DeleteUserAsync(string email, CancellationToken token) {
        ...
    }
}

这在我调用 https://localhost:{PORT}/connect/token 和其他与身份验证相关的端点时有效。我遇到的问题是,当我向 https://localhost:{PORT}/users 发送 POST 请求时,我收到 404 not-found 响应。这告诉我我的服务找不到我的控制器。但是,我在没有 Identity Server 的情况下使用相同的控制器设置的其他服务并且它们工作正常,那么我在这里做错了什么?

也许尝试制作控制器classpublic?密封在这里也没有任何意义。