После многих попыток и прочтения статей я решил разместить здесь свой выпуск. То, что я хочу, заключается в следующем: я работаю над api-версиями приложения. Поддерживаемый формат версии .NET Core (Microsoft.AspNetCore.Mvc.Versioning
пакет) - Major.Minor, и это то, что я хочу использовать в проекте, над которым я работаю. Я хочу иметь запасную версию на тот случай, если младшая версия не указана клиентом.
Я использую ядро .NET 2.2 и использую api-version
, указанное в шапке. Соответствующая конфигурация управления версиями API выглядит следующим образом:
services.AddApiVersioning(options => {
options.ReportApiVersions = true;
options.ApiVersionReader = new HeaderApiVersionReader("api-version");
options.ErrorResponses = new ApiVersioningErrorResponseProvider();
});
У меня есть следующие два контроллера для каждой версии: (контроллеры упрощены ради этого вопроса SO):
[ApiVersion("1.0")]
[Route("api/[controller]")]
public class ValueControllerV10 : Controller
{
[HttpGet(Name = "collect")]
public String Collect()
{
return "Version 1.0";
}
}
[ApiVersion("1.1")]
[Route("api/[controller]")]
public class ValueControllerV11 : Controller
{
[HttpGet(Name = "collect")]
public String Collect()
{
return "Version 1.1";
}
}
Если клиент указывает api-version=1.0
, то используется ValueControllerV10. И, конечно, если клиент указывает api-version=1.1
, тогда ValueControllerV11 используется, как и ожидалось.
А теперь приходит моя проблема. Если клиент указывает api-version=1
(так что только основная версия без вспомогательной версии), то используется ValueControllerV10. Это потому, что ApiVersion.Parse("1")
равно ApiVersion.Parse("1.0")
, если я не ошибаюсь. Но в этом случае я хочу вызвать последнюю версию данной основной версии, которая в моем примере равна 1.1.
Мои попытки:
Первый: Указание [ApiVersion("1")]
в ValueControllerV11
[ApiVersion("1")]
[ApiVersion("1.1")]
[Route("api/[controller]")]
public class ValueControllerV11 : Controller
{
[HttpGet(Name = "collect")]
public String Collect()
{
return "Version 1.1";
}
}
не работает, ведет
AmbiguousMatchException: The request matched multiple endpoints
Чтобы решить эту проблему, я предложил второй подход:
Второй : с использованием пользовательского IActionConstraint
. Для этого я следовал этим статьям:
Затем я создал следующий класс:
[AttributeUsage(AttributeTargets.Method)]
public class HttpRequestPriority : Attribute, IActionConstraint
{
public int Order
{
get
{
return 0;
}
}
public bool Accept(ActionConstraintContext context)
{
var requestedApiVersion = context.RouteContext.HttpContext.GetRequestedApiVersion();
if (requestedApiVersion.MajorVersion.Equals(1) && !requestedApiVersion.MinorVersion.HasValue)
{
return true;
}
return false;
}
}
И используется в ValueControllerV11
:
[ApiVersion("1")]
[ApiVersion("1.1")]
[Route("api/[controller]")]
public class ValueControllerV11 : Controller
{
[HttpGet(Name = "collect")]
[HttpRequestPriority]
public String Collect()
{
return "Version 1.1";
}
}
Ну, это решает AmbiguousMatchException
, но переопределяет поведение по умолчанию пакета Microsoft.AspNetCore.Mvc.Versioning
, поэтому, если клиент использует api-version 1.1
, он получает 404 Not Found обратно, что понятно в соответствии с реализацией HttpRequestPriority
Третий : Использование MapSpaFallbackRoute
в Startup.cs
, условно:
app.MapWhen(x => x.GetRequestedApiVersion().Equals("1") && x.GetRequestedApiVersion().MinorVersion == null, builder =>
{
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new {controller = nameof(ValueControllerV11), action = "Collect"});
});
});
app.UseMvc();
Это тоже не работает, никакого влияния. Имя MapSpaFallbackRoute
дает мне также ощущение, что это не то, что мне нужно использовать ...
Итак, мой вопрос: как я могу ввести альтернативное поведение «использовать последние» для случая, когда младшая версия не указана в api-version
? Заранее спасибо!