Я использую библиотеку управления версиями ASP для создания версии Rest API контроллера MVC в микросервисе.
using Asp.Versioning;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
[ApiController]
[ApiVersion("1")]
[ControllerName("example")]
[Route("v{version:apiVersion}/[controller]")]
внутри этого контроллера у меня есть несколько действий, например. один, чтобы получить версию:
[HttpGet("version")]
[Produces("text/plain")]
[ProducesResponseType(StatusCodes.Status200OK)]
public IActionResult GetVersion() => ...
Если я выполняю операцию curl
GET с любым номером версии, я получаю соответствующий маршрут для версии. В некотором смысле это неудивительно, поскольку я использую для действия сопоставление, нейтральное по версии. Однако, учитывая, что маршрут контроллера имеет [ApiVersion] только 1, я ожидал, что любой другой необъявленный номер версии приведет к тому, что ошибка 404 не будет найдена.
curl -X GET http://localhost:8127/v1/system/version
0.0.0.0%
curl -X GET http://localhost:8127/v1337/system/version
0.0.0.0%
Я неправильно настроил это? Возможно ли, чтобы необъявленные версии отклонялись, а не сопоставлялись? В UrlSegmentApiVersionReader значение по умолчанию (и предполагается, что по умолчанию) не указано для моего кода, поскольку все версии явно указаны для версий URL.
services.AddApiVersioning(opt =>
{
opt.ApiVersionReader = new UrlSegmentApiVersionReader();
}).AddApiExplorer(opt =>
{
opt.GroupNameFormat = "'v'V";
});
Я следовал примеру, приведенному здесь в ASP.NET Core с MVC (Core).
Привет @Rena, Спасибо за твой вклад/идеи. Я обновил билет, добавив дополнительную информацию, надеюсь, это поможет вам и другим!
Привет @Syntax, не могли бы вы поделиться файлом проекта? Или даже лучше поделиться минимальным репозиторием, который мог бы воспроизвести вашу проблему. Протестировав ваш общий код, он работает нормально.
Когда вы говорите, что все работает нормально, вы имеете в виду, что получаете 404 NotFound по запросу с номером версии, отличным от версии 1?
Привет @Syntax, ок, я понял. Вы не хотите получать сообщение об ошибке 400, например: The HTTP resource that matches the request URI 'https://localhost:5001/v3/WeatherForecast/version' does not support the API version '3'.
. Только хочу, чтобы ошибка 404 не найдена.
Если вы используете платформу до .net 6, это кажется невозможным. Если вы используете .net 6 или более позднюю версию .net 6, вы можете установить options.UnsupportedApiVersionStatusCode = 404;
. Кроме того, так и должно быть [ApiVersion(1.0)]
. Использование [ApiVersion("1")
не может работать нормально.
Извините за медленный ответ, как показано выше, когда я запрашиваю бессмысленные версии, я получаю 200 (как показано в результатах Curl выше). Я был бы рад получить 400, именно 200 ответов с кодом статуса побудили меня опубликовать этот вопрос, если честно.
Если вы хотите вернуть ответ о недопустимой версии с кодом 404 Not Found, это кажется невозможным до версии .NET 6. Вы можете добиться этого, используя @Chris Martinez do, установив options.UnsupportedApiVersionStatusCode = 404;
. Вам необходимо установить пакет Asp.Versioning.Mvc.ApiExplorer
вместо старой версии Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
с помощью или выше.
.NET 6.
Кроме того, обратите внимание, что вместо [ApiVersion(1.0)]
должно быть [ApiVersion("1")
.
Вы можете настроить ответ об ошибке, настроив IErrorResponseProvider
и установив поставщика на options.ErrorResponses
. Чтобы настроить поставщика, он должен выглядеть следующим образом:
public class CustomErrorResponseProvider : DefaultErrorResponseProvider
{
public override IActionResult CreateResponse(ErrorResponseContext context)
{
if (context.ErrorCode == "UnsupportedApiVersion")
{
context.StatusCode = (int)HttpStatusCode.NotFound;
}
return base.CreateResponse(context);
}
}
Тогда вы увидите, что StatusCode
доступен только для чтения, то есть вы не можете изменить код состояния. Проверьте исходный код ниже:
public partial class ErrorResponseContext
{
/// <summary>
/// Gets the associated HTTP status code.
/// </summary>
/// <value>The associated HTTP status code.</value>
#if WEBAPI
public HttpStatusCode StatusCode { get; }
#else
public int StatusCode { get; }
#endif
/// <summary>
/// Gets the associated error code.
/// </summary>
/// <value>The associated error code.</value>
public string ErrorCode { get; }
/// <summary>
/// Gets the associated error message.
/// </summary>
/// <value>The error message.</value>
public string Message { get; }
/// <summary>
/// Gets the detailed error message.
/// </summary>
/// <value>The detailed error message, if any.</value>
public string? MessageDetail { get; }
}
Это очень, очень старая версия управления версиями API. IErrorResponseProvider
и DefaultErrorResponseProvider
больше даже не существуют. Аналогичного подхода можно добиться, используя IProblemDetailsWriter
. Доступна обратная совместимость для преобразования сведений о проблеме в старый формат объекта ошибки.
Привет @Chris Мартинес, да. Но вы можете видеть это использование services.xxxx
. Кажется, что целевой фреймворк выпущен до версии .NET 6, которая не будет использовать новый пакет Asp.Versioning.Mvc.ApiExplorer
. Новый пакет используется за пределами .NET 6, а регистрация службы за пределами .NET 6 будет builder.Services.xxx
.
Заметки для потомков; Я использую .Net 8, у меня есть зависимость Asp.Versioning.Mvc.ApiExplorer v 8.1.0. UnsupportedApiVersionStatusCode
выглядит именно так, как мне нужно, если бы я хотел преобразовать 400 в 404 (что, честно говоря, меня меньше беспокоит). Больше всего меня беспокоит то, что GET для неподдерживаемых версий выдает ответ с кодом состояния 200. ре. атрибут версии, оба доступны (двойной и строковый), я предпочитаю использовать строку, потому что лично мне не нужна версия 1.0, и я рад увеличивать версии как целые числа.
Как оказалось, использование [Version(1.0)]
нормально и правильно отображает карты, когда проблемы с конфигурацией были исправлены, спасибо за предложение и отзыв!
@Rena раньше 6.0
(~ 2,5 года назад) это был способ установить/изменить код состояния HTTP для ошибок управления версиями API. Вы не можете точно определить, какой пакет используется, всего лишь по services
, поскольку вы могли бы просто сначала получить значение с помощью var services = builder.Services;
. Поскольку все старые пакеты устарели, я обычно не ожидаю, что с ними будут связаны новые вопросы, но они могли бы быть. Для меня подарками были using Asp.Versioning;
и .AddApiVersioning().AddApiExplorer()
, что означает IApiVerisoningBuilder
в 6.0
+.
Чтобы добавить ценность этому ответу, если вы используете - теперь устаревшие - Microsoft.AspNetCore.Mvc.Versioning.*
пакеты, этот ответ - правильный способ преобразовать ответ об ошибке.
Что произойдет, если версия API не совпадает, уже давно остается серой зоной. Ни один сценарий не подчеркивает это больше, чем управление версиями по сегменту URL. Это лишь одна из многих проблем, связанных с использованием этого метода управления версиями (а также отсутствием RESTful).
Существующее поведение — вернуться 400
. Начиная с управления версиями API 6.0
, в расширениях маршрутизации вносятся изменения, которые в некоторых случаях, но не во всех, приведут к 404
при версионировании по сегменту URL. В случаях, когда код не 404, ответ по-прежнему будет 400
. Это сделано в первую очередь для сохранения обратной совместимости. Если вы хотите вернуться 404
, есть простой аварийный люк. Вам просто нужно установить опцию:
builder.Services
.AddApiVersioning(options =>
{
options.ApiVersionReader = new UrlSegmentApiVersionReader();
options.UnsupportedApiVersionStatusCode = 404;
})
.AddMvc()
.AddApiExplorer(options => options.GroupNameFormat = "'v'V");
Это по-прежнему будет выдавать ProblemDetails
, если они настроены, но код состояния будет 404
вместо 400
.
Если я правильно прочитал выше, то без указания UnsupportedApiVersionStatusCode
, не стоит ли мне ожидать тогда 400 вместо 200 за версию 1337
?
Я подозреваю, что причиной моих проблем является использование EnableEndpointRouting = false
и app.UseMvc()
(сейчас я удалил оба). Я продолжу копаться в этом, но ваш пример, возможно, наставил меня на правильный путь! В настоящее время я получаю код 404 для всех вызовов, но вижу, что маршруты зарегистрированы с помощью IActionDescriptorCollectionProvider
.
Как только я заменил app.UseMvc()
на app.UseRouting()
, app.UseEndpoints(endpoints => endpoints.MapControllers())
, а также обновил свой вызов AddMvc
, чтобы использовать все в IApiVersioningBuilder
(согласно вашему примеру) вместо AddMvc()
из коллекции сервисов, я смог получить ожидаемое поведение.
@Синтаксис да, IApiVersioningBuilder.AddMvc
отличается и делает что-то другое, чем IMvcServiceExtensions.AddMvc
. Наследие IRouteBuilder
все еще поддерживается, но я бы не рекомендовал его. Есть некоторые крайние случаи, когда что-то может работать не так, как вы ожидаете. Начиная с ASP.NET 2.2
, руководство заключалось в использовании маршрутизации конечных точек. Полное объяснение немного длинное, но похоже, что вы что-то изменили и все работает.
Привет @Syntax, ты имеешь в виду, что если вы используете другую необъявленную версию, она также выдаст ответ вместо 404? Какая у вас версия targetframework? Как настроить версию API в автозагрузке или программе?