using MAX.Framework.Conventions; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.Extensions.DependencyInjection; namespace Store.Users.Tests; public sealed class ApiRoutePrefixTests { [Theory] [InlineData(typeof(PrefixRouteTestController), "Read", "api/prefix-tests/Read")] [InlineData(typeof(PrefixRouteTestController), "Absolute", "api/absolute-test")] [InlineData(typeof(PrefixRouteTestController), "ExistingAbsolute", "api/existing-absolute-test")] [InlineData(typeof(ExistingPrefixTestController), "Read", "api/v1/prefix-tests/Read")] [InlineData(typeof(ActionPrefixTestController), "Read", "api/action-prefix-test")] [InlineData(typeof(NonApiPrefixTestController), "Read", "non-api-prefix-test")] public void RoutesAndApiExplorer_UsePrefixOnlyForApiControllers(Type controllerType, string actionName, string expected) { var services = new ServiceCollection(); services.AddLogging(); services.AddControllers(options => options.Conventions.Add(new ApiRoutePrefixConvention())) .AddApplicationPart(typeof(PrefixRouteTestController).Assembly); using var provider = services.BuildServiceProvider(); var action = Assert.Single(provider.GetRequiredService() .ActionDescriptors.Items.OfType(), action => action.ControllerTypeInfo.AsType() == controllerType && action.ActionName == actionName); Assert.Equal(expected, action.AttributeRouteInfo!.Template); var description = Assert.Single(provider.GetRequiredService() .ApiDescriptionGroups.Items.SelectMany(group => group.Items), description => description.ActionDescriptor.Id == action.Id); Assert.Equal(expected, description.RelativePath); } } [ApiController] [Route("prefix-tests/[action]")] public sealed class PrefixRouteTestController : ControllerBase { [HttpPost] public string Read() { return "ok"; } [HttpPost("~/absolute-test")] public string Absolute() { return "ok"; } [HttpPost("/api/existing-absolute-test")] public string ExistingAbsolute() { return "ok"; } } [ApiController] [Route("api/v1/prefix-tests/[action]")] public sealed class ExistingPrefixTestController : ControllerBase { [HttpPost] public string Read() { return "ok"; } } [ApiController] public sealed class ActionPrefixTestController : ControllerBase { [HttpPost("action-prefix-test")] public string Read() { return "ok"; } } #pragma warning disable AV0014 // 故意保留非 API 控制器,验证约定的作用范围。 [ApiExplorerSettings(IgnoreApi = false)] public sealed class NonApiPrefixTestController : ControllerBase { [HttpPost("non-api-prefix-test")] public string Read() { return "ok"; } } #pragma warning restore AV0014