Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Asp.net WebForm Web API版本处理使用命名空间和持久化旧API_C#_Asp.net_Asp.net Web Api2 - Fatal编程技术网

C# Asp.net WebForm Web API版本处理使用命名空间和持久化旧API

C# Asp.net WebForm Web API版本处理使用命名空间和持久化旧API,c#,asp.net,asp.net-web-api2,C#,Asp.net,Asp.net Web Api2,我有一个包含Web Api v2的asp.net Web表单项目。我没有考虑API版本控制,并用下面的简单路由添加我的整个API: RouteTable.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}" ); 但是,现在,我需要创建版本控制,因为我有很多更改,不想让使用旧api的老客户失望。因此,我创建了一个名为APIv2的新文件夹,并在那里创建了我的控制器(与旧api同名)。问题是,我怎样才能

我有一个包含Web Api v2的asp.net Web表单项目。我没有考虑API版本控制,并用下面的简单路由添加我的整个API:

RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}"
);
但是,现在,我需要创建版本控制,因为我有很多更改,不想让使用旧api的老客户失望。因此,我创建了一个名为APIv2的新文件夹,并在那里创建了我的控制器(与旧api同名)。问题是,我怎样才能走这样的路线:

MyWebsite/API/Item => For ItemController OF OLD API
MyWebsite/APIv2/Item => For ItemController Of New API (Version 2)
我读过很多帖子,但没有一篇适合我!我还创建了NamespaceHttpControllerSelector,但它也不起作用

请给我举一些如何处理这个问题的例子

附言:当我为新api创建简单的路由时,它说找到了重复的控制器!(尽管我使用的是不同的名称空间)

编辑(添加我的完整代码):

以及名称空间HttpControllerSelector:

public class NamespaceHttpControllerSelector : DefaultHttpControllerSelector
{
    private const string ControllerKey = "controller";
    private readonly HttpConfiguration _configuration;
    private readonly Lazy<IEnumerable<Type>> _duplicateControllerTypes;

    public NamespaceHttpControllerSelector(HttpConfiguration configuration) : base(configuration)
    {
        _configuration = configuration;
        _duplicateControllerTypes = new Lazy<IEnumerable<Type>>(GetDuplicateControllerTypes);
    }

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
    {
        var routeData = request.GetRouteData();
        if (routeData == null || routeData.Route == null || routeData.Route.DataTokens == null || routeData.Route.DataTokens["Namespaces"] == null) 
            return base.SelectController(request);

        // Look up controller in route data
        object controllerName;
        routeData.Values.TryGetValue(ControllerKey, out controllerName);
        var controllerNameAsString = controllerName as string;
        if (controllerNameAsString == null) 
            return base.SelectController(request);

        //get the currently cached default controllers - this will not contain duplicate controllers found so if
        // this controller is found in the underlying cache we don't need to do anything
        var map = base.GetControllerMapping();
        if (map.ContainsKey(controllerNameAsString)) 
            return base.SelectController(request);

        //the cache does not contain this controller because it's most likely a duplicate, 
        // so we need to sort this out ourselves and we can only do that if the namespace token
        // is formatted correctly.
        var namespaces = routeData.Route.DataTokens["Namespaces"] as IEnumerable<string>;
        if (namespaces == null)
            return base.SelectController(request);

        //see if this is in our cache
        var found = _duplicateControllerTypes.Value
            .Where(x => string.Equals(x.Name, controllerNameAsString + ControllerSuffix, StringComparison.OrdinalIgnoreCase))
            .FirstOrDefault(x => namespaces.Contains(x.Namespace));

        if (found == null)
            return base.SelectController(request);

        return new HttpControllerDescriptor(_configuration, controllerNameAsString, found);
    }

    private IEnumerable<Type> GetDuplicateControllerTypes()
    {
        var assembliesResolver = _configuration.Services.GetAssembliesResolver();
        var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
        var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);

        //we have all controller types, so just store the ones with duplicate class names - we don't
        // want to cache too much and the underlying selector caches everything else

        var duplicates = controllerTypes.GroupBy(x => x.Name)
            .Where(x => x.Count() > 1)
            .SelectMany(x => x)
            .ToArray();

        return duplicates;
    }

}
公共类名称空间HttpControllerSelector:DefaultHttpControllerSelector
{
private const string ControllerKey=“controller”;
私有只读HttpConfiguration\u配置;
私有只读惰性_duplicateControllerTypes;
公共名称空间HttpControllerSelector(HttpConfiguration配置):基本(配置)
{
_配置=配置;
_duplicateControllerTypes=新延迟(GetDuplicateControllerTypes);
}
公共覆盖HttpControllerDescriptor SelectController(HttpRequestMessage请求)
{
var routeData=request.GetRouteData();
如果(RoutedData==null | | RoutedData.Route==null | | | RoutedData.Route.DataTokens==null | | | RoutedData.Route.DataTokens[“命名空间”]==null)
返回基地。选择控制器(请求);
//在路由数据中查找控制器
对象控制器名称;
RoutedData.Values.TryGetValue(控制器键,外部控制器名);
var controllerNameAsString=作为字符串的controllerName;
if(controllerNameAsString==null)
返回基地。选择控制器(请求);
//获取当前缓存的默认控制器-这将不包含找到的重复控制器,因此如果
//该控制器位于底层缓存中,我们不需要执行任何操作
var map=base.GetControllerMapping();
if(映射ContainsKey(controllerNameAsString))
返回基地。选择控制器(请求);
//缓存不包含此控制器,因为它很可能是重复的,
//因此,我们需要自己解决这个问题,只有在名称空间标记
//格式正确。
var namespaces=routeData.Route.DataTokens[“namespaces”]作为IEnumerable;
if(名称空间==null)
返回基地。选择控制器(请求);
//看看这是否在我们的缓存中
var found=\u duplicateControllerTypes.Value
其中(x=>string.Equals(x.Name,controllerNameAsString+ControllerSuffix,StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(x=>namespaces.Contains(x.Namespace));
if(found==null)
返回基地。选择控制器(请求);
返回新的HttpControllerDescriptor(_配置,controllerNameAsString,找到);
}
private IEnumerable GetDuplicateControllerTypes()
{
var assembliesrolver=_configuration.Services.GetAssembliesResolver();
var controllersrolver=_configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes=controllersrolver.GetControllerTypes(assembliesrolver);
//我们有所有的控制器类型,所以只需存储具有重复类名的控制器-我们没有
//如果要缓存过多,则基础选择器会缓存其他所有内容
var duplicates=controllerTypes.GroupBy(x=>x.Name)
.Where(x=>x.Count()>1)
.SelectMany(x=>x)
.ToArray();
返回副本;
}
}

您可以使用Web Api属性路由

public ItemControllerV2 : ApiController
{
    [Route("v2/item/{id:int}")]
    public Item Get(int id)
    {
        ....
    }
}
还要记住在Web Api配置中启用属性路由

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        //rest of your Web Api configuration
    }
 }
有关属性路由的详细信息,您可以在此处找到:


编辑

若正如您在注释中所写,控制器中有许多方法,并且您不想向每个可以使用的方法添加属性
RoutePrefix
attribute:

[RoutePrefix("v2/item")]
public ItemControllerV2 : ApiController
{
}
另外,我没有WebApiConfig文件,我应该在哪里创建它

它与方法调用的位置相同:

RouteTable.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}"
);
因此,在此必须添加以下方法调用以启用属性路由:

GlobalConfiguration.Configure(config => 
{
    config.MapHttpAttributeRoutes();

    RouteTable.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}"
    );
});

仅添加
名称空间HttpControllerSelector
是不够的,您必须为其提供数据

这条线

var namespaces = routeData.Route.DataTokens["Namespaces"] as IEnumerable<string>;
并按如下方式更改您的路线:

RouteTable.Routes.MapHttpRoute(
  name: "VersionedApi",
  routeTemplate: "api/v2/{controller}",
  defaults: new {@namespace = "your namespace for v2 controllers"}
);

RouteTable.Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}",
  defaults: new {@namespace = "your namespace for v1 controllers"}
);

谢谢你的回答,但是,有很多方法,我应该为所有的方法添加它吗?有没有办法把所有的东西都分开?另外,我没有WebApiConfig文件,我应该在哪里创建它?再次感谢,实际上我没有WebApiConfig文件,我在我的Global.asax(Application\u Start method)中添加了路由部分!我懂了。尤其是将所有配置直接放在
应用程序\u Start
方法中并没有错。但是,如果您的项目增长,它可以从将各种元素配置分离到单独的类中获益。您可以在
Application\u Start
方法中进一步使用这些类,但正因为如此,它变得更易于维护。Application\u Start中没有“config”可供使用!我添加了这个GlobalConfiguration.Configuration.MapHttpAttribute路由();在会话中启动(在应用程序中启动会导致错误),但没有任何更改!你是对的,我错了,我以为你使用默认模板来配置WebApi。我更正了启用属性路由的调用。无论如何,您应该将其放入
应用程序\u Start
中。在
应用程序_Start
中使用时出现了什么错误?阅读了吗?你提到
名字
//the cache does not contain this controller because it's most likely a duplicate, 
// so we need to sort this out ourselves and we can only do that if the namespace token
// is formatted correctly.
var namespaces = routeData.Route.DataTokens["Namespaces"] as IEnumerable<string>;
if (namespaces == null)
  return base.SelectController(request);

//see if this is in our cache
var found = _duplicateControllerTypes.Value
  .Where(x => string.Equals(x.Name, controllerNameAsString + ControllerSuffix, StringComparison.OrdinalIgnoreCase))
  .FirstOrDefault(x => namespaces.Contains(x.Namespace));
var @namespace = routeData.Values["namespace"] as string;
if (@namespace == null)
  return base.SelectController(request);

//see if this is in our cache
var found = _duplicateControllerTypes.Value
  .Where(x => string.Equals(x.Name, controllerNameAsString + ControllerSuffix, StringComparison.OrdinalIgnoreCase))
  .FirstOrDefault(x => x.Namespace == @namespace);
RouteTable.Routes.MapHttpRoute(
  name: "VersionedApi",
  routeTemplate: "api/v2/{controller}",
  defaults: new {@namespace = "your namespace for v2 controllers"}
);

RouteTable.Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}",
  defaults: new {@namespace = "your namespace for v1 controllers"}
);