无法调用版本化的API控制器方法

时间:2013-11-22 10:10:34

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

我有一个版本化的webapi项目,包含4个版本控制器。

问题背景:

目前我有一个自定义路由方法类 - 显示在底部 - 根据URL中传递的查询'version'字符串参数选择要选择的控制器 - 例如,访问V2(版本2)'ProductV2Controller'并获取URL的所有产品对象:

http://localhost:1487/api/product/?v=2/

我的路线定义如下:

config.Routes.MapHttpRoute(
           name: "NewProduct",
           routeTemplate: "api/product/{id}",
           defaults: new { controller = "product", id = RouteParameter.Optional }
       );

        //*****Custom route selector*****
       config.Services.Replace(typeof(IHttpControllerSelector), new Selector.ControllerSelector(config));

工作地点:

如果我在名为'ProductController'的简单非版本控制器中调用'GetProductByName'方法,这很好,并根据传递的名称返回DTO对象。这是通过以下查询字符串实现的:

http://localhost:1487/api/product/Name=Table

问题:

当我尝试对版本控制器执行相同操作时,再次使用查询字符串指定控制器版本和使用以下URL指定“Name”参数的查询字符串:

http://localhost:1487/api/product/?v=2/?Name=Table

应该受到影响的方法:

public Product GetProductByName(string name)
    {
        return V2Ops.GetProductByName(name);
    }

这被忽略并返回简单的'ProductController''GET'所有方法。

这就是我的'ControllerSelector'类返回所选版本控制器URL的方式:

productV2/?Name=Table

这对我来说似乎是正确的, 它是 选择正确的控制器,但我无法解决为什么它不会路由到'GetProductByName'方法!

更新 - 添加了'ControllerSelector'类:

public class ControllerSelector : DefaultHttpControllerSelector
{
    private HttpConfiguration _config;

    //Send config object to base constructor
    public ControllerSelector(HttpConfiguration config):base(config)
    {
        _config = config;
    }

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        string name;

        //Get the list of the Controllers i.e product/productV1
        var controllerList = GetControllerMapping();

        //Get the selected route api data.
        var routeData = request.GetRouteData();

        //Pull the selected routes controller name and convert it to a string.
        string controllerName = routeData.Values["controller"].ToString();

        HttpControllerDescriptor descriptor;

        //Check the controller list contains the controller implementation.
        if (controllerList.TryGetValue(controllerName, out descriptor))
        {
            //Call the GetVersionFromQueryString method passing the request object. This gives the api version.
            var version = GetVersionFromQueryString(request);

            //Construt the controller version.
            //If the returned version object wasn't null, i.e the request had a versioned controller, set the controller string.
            if (version != null)
            {
                name = controllerName + "V" + version;
            }

            //Else, simply return the standard 'product' controller.
            else
            {
                name = controllerName;
            }

            HttpControllerDescriptor versionedDescriptor;

            //If the controller list contains the controller plus its route return it.
            if (controllerList.TryGetValue(name, out versionedDescriptor))
            {
                return versionedDescriptor;
            }

            return descriptor;
        }

        return null;
    }

    //Check the route request version method.
    private object GetVersionFromQueryString(HttpRequestMessage request)
    {
        //Check the URL query 
        var query = HttpUtility.ParseQueryString(request.RequestUri.Query);

        //Set the version parameter to version set at the URL.
        var version = query["v"];

        //If version is not null i.e a versioned controller has been called, return the version.
        if(version != null)
        {
            return version;
        }

        //else return null so the standard controller is called.
        return null;
    }
}

2 个答案:

答案 0 :(得分:1)

您尝试使用的网址中包含两个单独的查询字符串;这不是HTTP URL的合法语法。您在自定义路由逻辑中获得的查询参数几乎肯定是错误的。特别是,我强烈怀疑你甚至没有在任何地方看到一个名为“Name”的参数,因为它没有被正确地格式化为“第二个”参数。这可以解释为什么您的路由逻辑无法确定发送请求的位置。

您应该将所有参数都滚动到一个查询字符串中并从那里解析出来;如果需要将一些(但不是全部)发送到最终目标控制器,您始终可以从剩余参数中重建新的查询字符串。 e.g:

Incoming: http://localhost:1487/api/product/?v=2&Name=Table
Outgoing: http://localhost:1487/api/productV2/?Name=Table

或其他任何你需要的东西。

答案 1 :(得分:0)

看起来我已经设法对此进行排序。

这是一个简单的代码是正确的情况,但URL不是:

我之前使用的方法,即带有两个查询字符串的错误:

http://localhost:1487/api/product/?v=2/?Name=Table

这是正确的格式。一个查询:

http://localhost:1487/api/product/?v=2&Name=Table

这现在完美无缺。