Управление версиями ASP, UrlSegmentApiVersionReader, маршруты соответствуют бессмысленным номерам версий

Я использую библиотеку управления версиями 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).

Привет @Syntax, ты имеешь в виду, что если вы используете другую необъявленную версию, она также выдаст ответ вместо 404? Какая у вас версия targetframework? Как настроить версию API в автозагрузке или программе?

Rena 26.06.2024 08:41

Привет @Rena, Спасибо за твой вклад/идеи. Я обновил билет, добавив дополнительную информацию, надеюсь, это поможет вам и другим!

Syntax 26.06.2024 15:59

Привет @Syntax, не могли бы вы поделиться файлом проекта? Или даже лучше поделиться минимальным репозиторием, который мог бы воспроизвести вашу проблему. Протестировав ваш общий код, он работает нормально.

Rena 27.06.2024 03:42

Когда вы говорите, что все работает нормально, вы имеете в виду, что получаете 404 NotFound по запросу с номером версии, отличным от версии 1?

Syntax 27.06.2024 06:30

Привет @Syntax, ок, я понял. Вы не хотите получать сообщение об ошибке 400, например: The HTTP resource that matches the request URI 'https://localhost:5001/v3/WeatherForecast/version' does not support the API version '3'.. Только хочу, чтобы ошибка 404 не найдена.

Rena 27.06.2024 07:09

Если вы используете платформу до .net 6, это кажется невозможным. Если вы используете .net 6 или более позднюю версию .net 6, вы можете установить options.UnsupportedApiVersionStatusCode = 404;. Кроме того, так и должно быть [ApiVersion(1.0)]. Использование [ApiVersion("1") не может работать нормально.

Rena 28.06.2024 04:19

Извините за медленный ответ, как показано выше, когда я запрашиваю бессмысленные версии, я получаю 200 (как показано в результатах Curl выше). Я был бы рад получить 400, именно 200 ответов с кодом статуса побудили меня опубликовать этот вопрос, если честно.

Syntax 28.06.2024 13:42
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
114
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Сократить ответ

Если вы хотите вернуть ответ о недопустимой версии с кодом 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").

Объяснение, почему невозможно установить код состояния 404 до версии .NET 6

Вы можете настроить ответ об ошибке, настроив 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 Martinez 28.06.2024 01:21

Привет @Chris Мартинес, да. Но вы можете видеть это использование services.xxxx. Кажется, что целевой фреймворк выпущен до версии .NET 6, которая не будет использовать новый пакет Asp.Versioning.Mvc.ApiExplorer. Новый пакет используется за пределами .NET 6, а регистрация службы за пределами .NET 6 будет builder.Services.xxx.

Rena 28.06.2024 04:02

Заметки для потомков; Я использую .Net 8, у меня есть зависимость Asp.Versioning.Mvc.ApiExplorer v 8.1.0. UnsupportedApiVersionStatusCode выглядит именно так, как мне нужно, если бы я хотел преобразовать 400 в 404 (что, честно говоря, меня меньше беспокоит). Больше всего меня беспокоит то, что GET для неподдерживаемых версий выдает ответ с кодом состояния 200. ре. атрибут версии, оба доступны (двойной и строковый), я предпочитаю использовать строку, потому что лично мне не нужна версия 1.0, и я рад увеличивать версии как целые числа.

Syntax 28.06.2024 13:46

Как оказалось, использование [Version(1.0)] нормально и правильно отображает карты, когда проблемы с конфигурацией были исправлены, спасибо за предложение и отзыв!

Syntax 29.06.2024 02:16

@Rena раньше 6.0 (~ 2,5 года назад) это был способ установить/изменить код состояния HTTP для ошибок управления версиями API. Вы не можете точно определить, какой пакет используется, всего лишь по services, поскольку вы могли бы просто сначала получить значение с помощью var services = builder.Services;. Поскольку все старые пакеты устарели, я обычно не ожидаю, что с ними будут связаны новые вопросы, но они могли бы быть. Для меня подарками были using Asp.Versioning; и .AddApiVersioning().AddApiExplorer(), что означает IApiVerisoningBuilder в 6.0+.

Chris Martinez 29.06.2024 19:28

Чтобы добавить ценность этому ответу, если вы используете - теперь устаревшие - Microsoft.AspNetCore.Mvc.Versioning.* пакеты, этот ответ - правильный способ преобразовать ответ об ошибке.

Chris Martinez 29.06.2024 19:30
Ответ принят как подходящий

Что произойдет, если версия 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?

Syntax 28.06.2024 13:41

Я подозреваю, что причиной моих проблем является использование EnableEndpointRouting = false и app.UseMvc() (сейчас я удалил оба). Я продолжу копаться в этом, но ваш пример, возможно, наставил меня на правильный путь! В настоящее время я получаю код 404 для всех вызовов, но вижу, что маршруты зарегистрированы с помощью IActionDescriptorCollectionProvider.

Syntax 28.06.2024 17:57

Как только я заменил app.UseMvc() на app.UseRouting(), app.UseEndpoints(endpoints => endpoints.MapControllers()), а также обновил свой вызов AddMvc, чтобы использовать все в IApiVersioningBuilder (согласно вашему примеру) вместо AddMvc() из коллекции сервисов, я смог получить ожидаемое поведение.

Syntax 29.06.2024 02:14

@Синтаксис да, IApiVersioningBuilder.AddMvc отличается и делает что-то другое, чем IMvcServiceExtensions.AddMvc. Наследие IRouteBuilder все еще поддерживается, но я бы не рекомендовал его. Есть некоторые крайние случаи, когда что-то может работать не так, как вы ожидаете. Начиная с ASP.NET 2.2, руководство заключалось в использовании маршрутизации конечных точек. Полное объяснение немного длинное, но похоже, что вы что-то изменили и все работает.

Chris Martinez 29.06.2024 19:20

Другие вопросы по теме