【问题标题】:ASP.net Web Api VersioningASP.net Web API 版本控制
【发布时间】:2018-02-21 00:14:25
【问题描述】:

我有 ASP.net Web Api 项目,我决定是时候支持版本控制了。我正在使用官方的 Microsoft Nuget 来支持版本控制(更多信息here),我决定按命名空间进行版本控制(例如here)。

很遗憾,我无法让代码正常工作。如果我这样调用我的方法:

http://localhost:7291/api/Saved/GetNumberOfSavedWorkoutsForUser?api-version=2.0

我得到错误:

Multiple types were found that match the controller named 'Saved'. This can happen if the route that services this request ('api/{controller}/{action}/{id}') found multiple controllers defined with the same name but differing namespaces, which is not supported.

如果我这样称呼它:http://localhost:7291/v2/Saved/GetNumberOfSavedWorkoutsForUser

我收到错误 404:

The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

我不确定我做错了什么。这是我的代码:

Startup.cs

public void Configuration(IAppBuilder app)
    {
        var configuration = new HttpConfiguration();
        var httpServer = new HttpServer(configuration);


        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

        // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
        configuration.AddApiVersioning(o => 
        {
            o.AssumeDefaultVersionWhenUnspecified = true;
            o.ReportApiVersions = true; 
            o.DefaultApiVersion = ApiVersion.Default;
        });


        configuration.Routes.MapHttpRoute(
            "VersionedUrl",
            "v{apiVersion}/{controller}/{action}/{id}",
            defaults: null,
            constraints: new { apiVersion = new ApiVersionRouteConstraint() });

        configuration.Routes.MapHttpRoute(
            "VersionedQueryString",
            "api/{controller}/{action}/{id}",
            defaults: null);


        app.UseWebApi(httpServer);

        ConfigureAuth(app);
    }

保存的控制器 (v1)

namespace Master.Infrastructure.Api.Controllers
{

    [Authorize]
    [RoutePrefix("api/Saved")]
    [ApiVersion("1.0")]
    public class SavedController : ApiController
    {

        private readonly IUserService _userService;

        public SavedController(IUserService userService)
        {
            _userService = userService;

        }


        [HttpGet]
        [ActionName("GetNumberOfSavedWorkouts")]
        public async Task<NumberOfSavedWorkouts> GetNumberOfSavedWorkouts()
        {
            var numOfSavedWorkouts = new NumberOfSavedWorkouts
            {
                CurrentNumberOfSavedWorkouts =
                    await _userService.GetNumberOfSavedWorkoutsForUserAsync(User.Identity.GetUserId())
            };

            return numOfSavedWorkouts;
        }

    }
}

保存的控制器 (v2)

namespace Master.Infrastructure.Api.V2.Controllers
{
    [Authorize]
    [ApiVersion("2.0")]
    [RoutePrefix("v{version:apiVersion}/Saved")]
    public class SavedController : ApiController
    {

        private readonly ISavedWorkoutService _savedWorkoutService;


        public SavedController(ISavedWorkoutService savedWorkoutService)
        {       
            _savedWorkoutService = savedWorkoutService;
        }


        [ActionName("GetNumberOfSavedWorkoutsForUser")]
        public async Task<IHttpActionResult> GetNumberOfSavedWorkoutsForUser()
        {
            var cnt = await _savedWorkoutService.CountNumberOfSavedWorkoutsForUser(User.Identity.GetUserId());

            return Ok(cnt);
        }
    }
}

【问题讨论】:

    标签: c# asp.net-mvc asp.net-web-api api-versioning


    【解决方案1】:

    您的路线不正确。我强烈不鼓励你混合路由样式,除非你真的需要。排除故障可能非常困难。

    这里发生了几件事:

    1. 您有通过查询字符串和 URL 段进行版本控制的配置,您想要哪一种?我只会选择一个。默认值和我的建议是使用查询字符串方法。
    2. 您的基于约定的路由与基于属性的路由不同
    3. 由于您定义了 RoutePrefixAttribute,看来您更喜欢属性路由样式。我会删除所有基于约定的路由(例如:configuration.Routes.MapHttpRoute)。

    在您的约定中,路由模板:

    v{apiVersion}/{controller}/{action}/{id}

    但在你的属性中是:

    api/Saved

    这些都不符合您的预期路线:

    http://localhost:7291/api/Saved/GetNumberOfSavedWorkoutsForUser http://localhost:7291/v2/Saved/GetNumberOfSavedWorkoutsForUser

    对于使用路由属性的查询字符串方法,应该是这样的:

    configuration.AddApiVersioning(o => o.ReportApiVersions = true);
    
    namespace Master.Infrastructure.Api.Controllers
    {
        [Authorize]
        [ApiVersion("1.0")]
        [RoutePrefix("api/Saved")]
        public class SavedController : ApiController
        {
           private readonly IUserService _userService;
           public SavedController(IUserService userService) => _userService = userService;
    
           [HttpGet]
           [Route("GetNumberOfSavedWorkouts")]
           public async Task<IHttpActionResult> GetNumberOfSavedWorkouts()
           {
                var userId = User.Identity.GetUserId();
                var count = await _userService.GetNumberOfSavedWorkoutsForUserAsync(userId);
                return Ok(new NumberOfSavedWorkouts(){ CurrentNumberOfSavedWorkouts = count });
           }
        }
    }
    
    namespace Master.Infrastructure.Api.V2.Controllers
    {
        [Authorize]
        [ApiVersion("2.0")]
        [RoutePrefix("api/Saved")]
        public class SavedController : ApiController
        {
           private readonly ISavedWorkoutService _savedWorkoutService;
           public SavedController(ISavedWorkoutService savedWorkoutService) => _savedWorkoutService = savedWorkoutService;
    
           [HttpGet]
           [Route("GetNumberOfSavedWorkoutsForUser")]
           public async Task<IHttpActionResult> GetNumberOfSavedWorkoutsForUser()
           {
                var userId = User.Identity.GetUserId();
                var count = await _savedWorkoutService.CountNumberOfSavedWorkoutsForUser(userId);
                return Ok(count);
           }
        }
    }
    

    以下应该可以工作:

    http://localhost:7291/api/Saved/GetNumberOfSavedWorkouts?api-version=1.0 http://localhost:7291/api/Saved/GetNumberOfSavedWorkoutsForUser?api-version=2.0

    希望对你有所帮助。s

    【讨论】:

    • 刚刚意识到我又忘记了一件事。您还需要在配置中使用基于属性的路由:configuration.MapHttpAttributeRoutes()
    猜你喜欢
    • 2017-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 2016-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多