Я работаю над бэкэндом 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 команды и возвращаем пользователей.
- Если он остается в контроллере пользователей, как лучше всего иметь уникальные конечные точки?
@Ismar Спасибо, я использую ваше первое предложение (даже если мои идентификаторы, которые являются GUID, не слишком хорошо выглядят в URL-адресе: локальный: 5001/Teams/B699ED14-F672-46F5-885E-460EE8381802/нас
Да, это URL-адрес, который вы видите в журнале или некоторых инструментах, но важно то, что ваше определение является дружественным к REST, по крайней мере, это мое мнение. Ваше определение маршрута действия, вероятно, что-то вроде «{teamId}/users», и это совершенно нормально.
Именно, я использую "{teamId}/users". Я полностью согласен с тем, что вы сказали. Поскольку я новичок в разработке такого API, я предпочитаю спрашивать заранее, чтобы правильно установить базу. Кстати, если вы хотите опубликовать ответ, я отмечу его как решение.





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.AmbiguousMatchException: «Запрос соответствует нескольким конечным точкам. Спички:
у вас не может быть такого, потому что во время выполнения будет два действия, получающих Guid, который идет после имени контроллера. Вы должны различать эти 2, используя жестко закодированные строковые значения в имени маршрута действия.
Я бы поместил его в контроллер Teams и получил бы его, используя маршрут, например /teams/1/users, где 1 — идентификатор команды. Это больше "отдых" :). Вам нужно будет аннотировать действие контроллера Teams с помощью [Route("{teamId}/users")].
Однако, если вы хотите сохранить его в контроллере пользователей, аннотируйте действия команды с помощью [Route("some-action-name/{teamId}")] и получите к нему доступ с помощью users/some-action-name/{teamId}
Я бы поместил его в контроллер Teams и получил его, используя маршрут, например /teams/1/users, где 1 — идентификатор команды. Это больше "отдых" :). Но если вы хотите сохранить его в контроллере пользователей, аннотируйте действие команды с помощью [Route("action-name/{teamId}")] и получите к нему доступ, используя Users/action-name/{teamId}