【问题标题】:RESTful API endpoint namingRESTful API 端点命名
【发布时间】:2019-04-02 21:12:42
【问题描述】:

我正在开发一个公开 RESTful API 的 asp.net core 2.2 后端。

当前的实现运行良好(为清楚起见删除了额外的代码):

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 问题吗?这意味着控制器会以其他方式工作吗?
- 用户或团队控制器是否应该公开获取所有团队用户的方法?我们按团队 ID 查询并返回用户。
- 如果它保留在用户控制器中,拥有唯一端点的“最佳”方式是什么?

【问题讨论】:

  • 我会将它放在 Teams 控制器中,并使用 /teams/1/users 之类的路由接收它 - 其中 1 是团队 ID。这更像是“休息”:)。但是,如果您想将其保留在用户控制器中,则使用 [Route("action-name/{teamId}")] 注释团队操作并使用 Users/action-name/{teamId} 访问它
  • @Ismar 谢谢,我正在使用您的第一个提议(即使我的 ID,即 GUID,在 URL 中看起来不太好:localhost:5001/Teams/B699ED14-F672-46F5-885E-460EE8381802/users
  • 是的,这是您在日志或某些工具中看到的 URL,但重要的是您的定义是 REST 友好的,至少这是我的看法。您的操作路线定义可能类似于“{teamId}/users”,这很好。
  • 准确地说,我使用的是“{teamId}/users”。我完全同意你所说的。由于我对设计这样的 API 还很陌生,所以我更喜欢尽早询问以设置正确的基础。顺便说一句,如果您想发布答案,我会将其标记为解决方案。

标签: c# rest asp.net-core swagger


【解决方案1】:

我会将它放在Teams 控制器中,并使用/teams/1/users 之类的路由接收它 - 其中 1 是团队 ID。这更像是“休息”:)。您需要使用 [Route("{teamId}/users")]. 注释您的 Teams 控制器操作

但是,如果您想将其保留在用户控制器中,请使用 [Route("some-action-name/{teamId}")] 注释团队操作并使用 users/some-action-name/{teamId} 访问它

【讨论】:

    【解决方案2】:
    you need to provide action name in your url.which is unique path
    

    喜欢这个https://localhost:5001/Users/GetByTeamId?teamId=4AF29C4A-282A-4FB8-8691-9D44398A97F2

    【讨论】:

      【解决方案3】:

      你可以试试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。您必须通过在操作路线名称中使用硬编码字符串值来区分这两个。
      【解决方案4】:

      尝试在你的路由属性中添加动作,不要忘记在你的 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));
          }
        }
      } 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-25
        • 2021-07-01
        相关资源
        最近更新 更多