Именование конечных точек RESTful API

Я работаю над бэкэндом asp.net core 2.2, предоставляющим RESTful API.

Текущая реализация работает нормально (дополнительный код удален для ясности):

namespace Sppd.TeamTuner.Controllers
{
    [Authorize]
    [ApiController]
    [Route("[controller]")]
    public class UsersController : ControllerBase
    {
        private readonly ITeamTunerUserService _userService;
        private readonly ITokenProvider _tokenProvider;
        private readonly IAuthorizationService _authorizationService;
        private readonly IMapper _mapper;

        public UsersController(ITeamTunerUserService userService, ITokenProvider tokenProvider, IAuthorizationService authorizationService, IMapper mapper)
        {
            _userService = userService;
            _tokenProvider = tokenProvider;
            _authorizationService = authorizationService;
            _mapper = mapper;
        }

        [HttpGet]
        public async Task<IActionResult> GetByUserId(Guid userId)
        {
            // TODO: secure this call
            var user = _userService.GetByIdAsync(userId);
            return Ok(_mapper.Map<UserDto>(await user));
        }
    }
}

Метод единого API отлично работает с URL https://localhost:5001/Users?userId=4AF29C4A-282A-4FB8-8691-9D44398A97F2.

Теперь я хотел бы добавить второй метод:

[HttpGet]
public async Task<IActionResult> GetByTeamId(Guid teamId)
{
    // TODO: secure this call
    var users = _userService.GetByTeamIdAsync(teamId);
    return Ok(_mapper.Map<IEnumerable<UserDto>>(await users));
}

Это приведет к URL-адресу https://localhost:5001/Users?teamId=4AF29C4A-282A-4FB8-8691-9D44398A97F2 (обратите внимание, что параметром является teamId, а не userId по сравнению с первым вызовом).

При тестировании с помощью SwaggerUI страница не загружается, и отображается следующее исключение:

An unhandled exception has occurred while executing the request.
System.NotSupportedException: HTTP method "GET" & path "Users" overloaded by actions - Sppd.TeamTuner.Controllers.UsersController.GetByUserId (Sppd.TeamTuner),Sppd.TeamTuner.Controllers.UsersController.GetByTeamId (Sppd.TeamTuner). Actions require unique method/path combination for Swagger 2.0. Use ConflictingActionsResolver as a workaround
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.CreatePathItem(IEnumerable`1 apiDescriptions, ISchemaRegistry schemaRegistry)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.CreatePathItems(IEnumerable`1 apiDescriptions, ISchemaRegistry schemaRegistry)
   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath, String[] schemes)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

Вопросы:
- Это проблема SwaggerUI? То есть контроллер будет работать иначе?
- Должны ли пользователи или контроллер Teams предоставлять метод для получения всех пользователей группы? Запрашиваем по id команды и возвращаем пользователей.
- Если он остается в контроллере пользователей, как лучше всего иметь уникальные конечные точки?

Я бы поместил его в контроллер Teams и получил его, используя маршрут, например /teams/1/users, где 1 — идентификатор команды. Это больше "отдых" :). Но если вы хотите сохранить его в контроллере пользователей, аннотируйте действие команды с помощью [Route("action-name/{teamId}")] и получите к нему доступ, используя Users/action-name/{teamId}

IMujagic 02.04.2019 14:20

@Ismar Спасибо, я использую ваше первое предложение (даже если мои идентификаторы, которые являются GUID, не слишком хорошо выглядят в URL-адресе: локальный: 5001/Teams/B699ED14-F672-46F5-885E-460EE8381802/нас

Philippe 02.04.2019 14:54

Да, это URL-адрес, который вы видите в журнале или некоторых инструментах, но важно то, что ваше определение является дружественным к REST, по крайней мере, это мое мнение. Ваше определение маршрута действия, вероятно, что-то вроде «{teamId}/users», и это совершенно нормально.

IMujagic 02.04.2019 15:02

Именно, я использую "{teamId}/users". Я полностью согласен с тем, что вы сказали. Поскольку я новичок в разработке такого API, я предпочитаю спрашивать заранее, чтобы правильно установить базу. Кстати, если вы хотите опубликовать ответ, я отмечу его как решение.

Philippe 02.04.2019 15:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
856
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

you need to provide action name in your url.which is unique path

нравится это https://localhost:5001/Пользователи/GetByTeamId?teamId=4AF29C4A-282A-4FB8-8691-9D44398A97F2

Попробуйте добавить действие в свой атрибут маршрута, не забудьте добавить имя действия в свой URL-адрес.

namespace Sppd.TeamTuner.Controllers
{
   [Authorize]
   [ApiController]
   [Route("[controller]/[Action]")]
 public class UsersController : ControllerBase
 {
     private readonly ITeamTunerUserService _userService;
     private readonly ITokenProvider _tokenProvider;
     private readonly IAuthorizationService _authorizationService;
     private readonly IMapper _mapper;

     public UsersController(ITeamTunerUserService userService, ITokenProvider tokenProvider, IAuthorizationService authorizationService, IMapper mapper)
    {
        _userService = userService;
        _tokenProvider = tokenProvider;
        _authorizationService = authorizationService;
        _mapper = mapper;
    }

    [HttpGet]
    public async Task<IActionResult> GetByUserId(Guid userId)
    {
        // TODO: secure this call
        var user = _userService.GetByIdAsync(userId);
        return Ok(_mapper.Map<UserDto>(await user));
    }
  }
} 

вы можете попробовать Attribute Routing, вот что я имею в виду

[HttpGet("/{userId}")]
public async Task<IActionResult> GetByUserId(Guid userId)

[HttpGet("/{teamId}")]
public async Task<IActionResult> GetByTeamId(Guid teamId)

[HttpGet("/{teamId}")] определяет новый route с параметром teamId

Это было бы моим предпочтительным решением, но, к сожалению, оно не работает: Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchExceptio‌​n: «Запрос соответствует нескольким конечным точкам. Спички:

Philippe 02.04.2019 14:42

у вас не может быть такого, потому что во время выполнения будет два действия, получающих Guid, который идет после имени контроллера. Вы должны различать эти 2, используя жестко закодированные строковые значения в имени маршрута действия.

IMujagic 02.04.2019 15:09
Ответ принят как подходящий

Я бы поместил его в контроллер Teams и получил бы его, используя маршрут, например /teams/1/users, где 1 — идентификатор команды. Это больше "отдых" :). Вам нужно будет аннотировать действие контроллера Teams с помощью [Route("{teamId}/users")].

Однако, если вы хотите сохранить его в контроллере пользователей, аннотируйте действия команды с помощью [Route("some-action-name/{teamId}")] и получите к нему доступ с помощью users/some-action-name/{teamId}

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

Похожие вопросы

Не удается разрешить другой веб-API, используя его общедоступный домен
Как получить доступ к следующему и предыдущему элементам в итерации foreach через IEnumerable?
Явное включение сборки в процесс компоновщика Xamarin.iOS
Как создать пакет NuGet из проектов библиотеки классов и предварительно скомпилированных представлений?
При разборе схемы json с несколькими ссылками в свойстве anyOf в выходных данных возвращается только одна ссылка (последняя)
Как назначить бинарный оператор делегату
Программное добавление диаграммы в программу, но без отображения вывода диаграммы
Как проверить, является ли номер версии версией между двумя номерами версий
Окно C# .NET WPF Mousedown/Keydown не срабатывает для 4-цветных кнопок на выключенном телевизоре Пульт дистанционного управления, подключенный через инфракрасный приемник USB
Создание потока кодирования RTSP/RTMP h264 из растровых изображений