我有一个版本化的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'方法!
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;
}
}
答案 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
这现在完美无缺。