1

私は基本的に、私はこの記事に続く、versionned APIを作るしようとしています:私はのように私のWEBAPIためのいくつかのルートを設定しているhttps://blogs.msdn.microsoft.com/webdev/2013/03/07/asp-net-web-api-using-namespaces-to-version-web-apis/どのバージョンのWeb APIを名前空間を使用する - 〜/ API/V1 /製品

をこれは:

namespace WebApiApplication { 
public static class WebApiConfig { 
    public static void Register(HttpConfiguration config) { 
     // Web API configuration and services 

     // Web API routes 
     config.MapHttpAttributeRoutes(); 

     config.Routes.MapHttpRoute(
      name: "DefaultApi", 
      routeTemplate: "api/{version}/{controller}", 
      defaults: new { version = "v2" } 
     ); 

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

     config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config)); 
    } 
}} 

私のプロジェクトには、「v1」と「v2」という2つのフォルダが含まれているControllersフォルダがあります。

〜/コントローラ/ V1/ProductsController.cs

namespace WebApiApplication.Controllers.v1 { 
[RoutePrefix("api/v1/products")] 
public class ProductsController : ApiController { 
    Product[] products = new Product[] { 
     new Product { UserReference = 1, Name = "product1" }, 
     new Product { UserReference = 2, Name = "product2" } 
    }; 

    [HttpGet] 
    [Route("")] 
    public IEnumerable<Product> GetAllProducts() { 
     return products; 
    } 

    [HttpGet] 
    [Route("{userReference}")] 
    public IHttpActionResult GetProducts(int userReference) { 
     var res = products.Where(t => t.UserReference == userReference); 

     if (res == null) 
      return NotFound(); 

     return Ok(res); 
    } 
}} 

〜/コントローラ/ V2/ProductsController.cs

namespace WebApiApplication.Controllers.v2 { 
[RoutePrefix("api/v2/products")] 
public class ProductsController : ApiController { 
    Product[] products = new Product[] { 
     new Product { UserReference = "a", Name = "product1" }, 
     new Product { UserReference = "b", Name = "product2" } 
    }; 

    [HttpGet] 
    [Route("")] 
    public IEnumerable<Product> GetAllProducts() { 
     return products; 
    } 

    [HttpGet] 
    [Route("{userReference}")] 
    public IHttpActionResult GetProducts(string userReference) { 
     var res = products.Where(t => t.UserReference == userReference); 

     if (res == null) 
      return NotFound(); 

     return Ok(res); 
    } 
}} 

バージョン間の唯一の違いは、次のとおりです。コントローラは、このようになります。 UserReferenceはV2の文字列になります。

私は両方のバージョンで同じコントローラ名を持っているので、私は要求されたコントローラを見つけるために、現在の「IHttpControllerSelector」を上書きする必要があります。

〜/ NamespaceHttpControllerSelector.cs

public class NamespaceHttpControllerSelector : IHttpControllerSelector { 
    private const string VERSION_KEY = "version"; 
    private const string CONTROLLER_KEY = "controller"; 

    private readonly HttpConfiguration _configuration; 
    private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; 
    private readonly HashSet<string> _duplicates; 

    public NamespaceHttpControllerSelector(HttpConfiguration configuration) { 
     _configuration = configuration; 
     _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase); 
     _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); 
    } 

    public HttpControllerDescriptor SelectController(HttpRequestMessage request) { 
     IHttpRouteData routeData = request.GetRouteData(); 

     if (routeData == null) 
      throw new HttpResponseException(HttpStatusCode.NotFound); 

     string versionName = GetRouteVariable<string>(routeData, VERSION_KEY); // Here, I am always getting null 

     if (versionName == null) 
      throw new HttpResponseException(HttpStatusCode.NotFound); 

     string controllerName = GetRouteVariable<string>(routeData, CONTROLLER_KEY); 

     if (controllerName == null) 
      throw new HttpResponseException(HttpStatusCode.NotFound); 

     // Find a matching controller. 
     string key = string.Format(CultureInfo.InvariantCulture, "{1}.{2}", versionName, controllerName); 

     HttpControllerDescriptor controllerDescriptor; 
     if (_controllers.Value.TryGetValue(key, out controllerDescriptor)) { 
      return controllerDescriptor; 
     } 
     else if (_duplicates.Contains(key)) { 
      throw new HttpResponseException(
       request.CreateErrorResponse(
        HttpStatusCode.InternalServerError, 
        "Multiple controllers were found that match this request.")); 
     } 
     else { 
      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 
    } 

    public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() { 
     return _controllers.Value; 
    } 

    private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() { 
     var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); 

     // Create a lookup table where key is "namespace.controller". The value of "namespace" is the last 
     // segment of the full namespace. For example: 
     // MyApplication.Controllers.V1.ProductsController => "V1.Products" 
     IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver(); 
     IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); 

     ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); 

     foreach (Type t in controllerTypes) { 
      var segments = t.Namespace.Split(Type.Delimiter); 

      // For the dictionary key, strip "Controller" from the end of the type name. 
      // This matches the behavior of DefaultHttpControllerSelector. 
      var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); 

      var key = string.Format(
       CultureInfo.InvariantCulture, 
       "{0}.{1}", 
       segments[segments.Length - 1], 
       controllerName); 

      // Check for duplicate keys. 
      if (dictionary.Keys.Contains(key)) { 
       _duplicates.Add(key); 
      } 
      else { 
       dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t); 
      } 
     } 

     // Remove any duplicates from the dictionary, because these create ambiguous matches. 
     // For example, "Foo.V1.ProductsController" and "Bar.V1.ProductsController" both map to "v1.products". 
     foreach (string s in _duplicates) { 
      dictionary.Remove(s); 
     } 

     return dictionary; 
    } 

    private static T GetRouteVariable<T>(IHttpRouteData routeData, string name) { 
     object result = null; 

     if (routeData.Values.TryGetValue(name, out result)) 
      return (T) result; 

     return default(T); 
    } 
} 

を問題があります"SelectController"メソッドで。 「versionName = GetRouteVariable(routeData、VERSION_KEY);」を実行しているとき、私は常にversionNameに「null」を取得しています。 (私が "http://localhost:27039/api/v1/products/1"と呼ぶとき)。

私が代わりにURLに要求されたバージョンを持っている必要があります...

私のGlobal.asax.cs:

namespace WebApiApplication { 
public class WebApiApplication : System.Web.HttpApplication { 
    protected void Application_Start() { 
     GlobalConfiguration.Configure(WebApiConfig.Register); 
    } 
}} 
+0

これは単なるテストにすぎないことは知っていますが、コントローラを1つだけ作成して「api/v1」に公開することをお勧めします。次に、新しいバージョンをお持ちの場合は、バージョンコントロールに新しいブランチを作成し、それを 'api/v2'に公開することができます。たとえば、1つのバグを修正する必要があるとしましょう。同じアプリの2つのバージョンを表す1つのアプリであるため、戦略2ではバージョン2も公開する必要があります。 –

+0

2つのバージョンの唯一の違いは、Product UserReferenceがV2の文字列になることです –

答えて

0

ここでは、バージョンの制約を使用してはるかに優れたソリューションです。ここ

public class ApiVersionConstraint : IHttpRouteConstraint 
    { 
     public string AllowedVersion { get; private set; } 

     public ApiVersionConstraint(string allowedVersion) 
     { 
      AllowedVersion = allowedVersion.ToLowerInvariant(); 
     } 

     public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection) 
     { 
      object value; 
      if (values.TryGetValue(parameterName, out value) && value != null) 
      { 
       return AllowedVersion.Equals(value.ToString().ToLowerInvariant()); 
      } 
      return false; 
     } 
    } 

がRoutePrefixAttributeがV1とV2

public class ApiVersion1RoutePrefixAttribute : RoutePrefixAttribute 
    { 
     private const string RouteBase = "api/{apiVersion:apiVersionConstraint(v1)}"; 
     private const string PrefixRouteBase = RouteBase + "/"; 

     public ApiVersion1RoutePrefixAttribute(string routePrefix) 
      : base(string.IsNullOrWhiteSpace(routePrefix) ? RouteBase : PrefixRouteBase + routePrefix) 
     { 
     } 
    } 


public class ApiVersion2RoutePrefixAttribute : RoutePrefixAttribute 
     { 
      private const string RouteBase = "api/{apiVersion:apiVersionConstraint(v2)}"; 
      private const string PrefixRouteBase = RouteBase + "/"; 

      public ApiVersion1RoutePrefixAttribute(string routePrefix) 
       : base(string.IsNullOrWhiteSpace(routePrefix) ? RouteBase : PrefixRouteBase + routePrefix) 
      { 
      } 
     } 

の両方のためのものである。ここでNamespaceControllerSelectorです。ここで

public class NamespaceHttpControllerSelector : IHttpControllerSelector 
    { 
     private readonly HttpConfiguration _configuration; 
     private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers; 

     public NamespaceHttpControllerSelector(HttpConfiguration config) 
     { 
      _configuration = config; 
      _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary); 
     } 

     public HttpControllerDescriptor SelectController(HttpRequestMessage request) 
     { 
      var routeData = request.GetRouteData(); 
      if (routeData == null) 
      { 
       throw new HttpResponseException(HttpStatusCode.NotFound); 
      } 

      var controllerName = GetControllerName(routeData); 
      if (controllerName == null) 
      { 
       throw new HttpResponseException(HttpStatusCode.NotFound); 
      } 

      var namespaceName = GetVersion(routeData); 
      if (namespaceName == null) 
      { 
       throw new HttpResponseException(HttpStatusCode.NotFound); 
      } 

      var controllerKey = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", namespaceName, controllerName); 

      HttpControllerDescriptor controllerDescriptor; 
      if (_controllers.Value.TryGetValue(controllerKey, out controllerDescriptor)) 
      { 
       return controllerDescriptor; 
      } 

      throw new HttpResponseException(HttpStatusCode.NotFound); 
     } 

     public IDictionary<string, HttpControllerDescriptor> GetControllerMapping() 
     { 
      return _controllers.Value; 
     } 

     private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary() 
     { 
      var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase); 

      var assembliesResolver = _configuration.Services.GetAssembliesResolver(); 
      var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); 

      var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); 

      foreach (var controllerType in controllerTypes) 
      { 
       var segments = controllerType.Namespace.Split(Type.Delimiter); 

       var controllerName = controllerType.Name.Remove(controllerType.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length); 

       var controllerKey = string.Format(CultureInfo.InvariantCulture, "{0}.{1}", segments[segments.Length - 1], controllerName); 

       if (!dictionary.Keys.Contains(controllerKey)) 
       { 
        dictionary[controllerKey] = new HttpControllerDescriptor(_configuration, controllerType.Name, controllerType); 
       } 
      } 

      return dictionary; 
     } 

     private string GetControllerName(IHttpRouteData routeData) 
     { 
      var subroute = routeData.GetSubRoutes().FirstOrDefault(); 
      if (subroute == null) return null; 

      var dataTokenValue = subroute.Route.DataTokens.First().Value; 
      if (dataTokenValue == null) return null; 

      var controllerName = ((HttpActionDescriptor[])dataTokenValue).First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty); 
      return controllerName; 
     } 

     private string GetVersion(IHttpRouteData routeData) 
     { 
      var subRouteData = routeData.GetSubRoutes().FirstOrDefault(); 

      if (subRouteData == null) return null; 


      // Modified to allow for . versioning, eg v1.1 etc 

      var apiVersion = GetRouteVariable<string>(subRouteData, "apiVersion"); 

      var apiVersionNamespace = apiVersion.Replace('.', '_'); 

      return apiVersionNamespace; 
     } 

     private T GetRouteVariable<T>(IHttpRouteData routeData, string name) 
     { 
      object result; 
      if (routeData.Values.TryGetValue(name, out result)) 
      { 
       return (T)result; 
      } 
      return default(T); 
     } 
    } 

HttpConfigurationです: -

public static class WebApiConfig 
    { 
     public static void Register(HttpConfiguration config) 
     { 


      var constraintsResolver = new DefaultInlineConstraintResolver(); 

      constraintsResolver.ConstraintMap.Add("apiVersionConstraint", typeof(ApiVersionConstraint)); 

      config.MapHttpAttributeRoutes(constraintsResolver); 

      config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config)); 



     } 
    } 

そして最後に、ここにあなたのコントローラがある: -

[ApiVersion1RoutePrefix("products")] 
public class ProductsController : ApiController { 
    Product[] products = new Product[] { 
     new Product { UserReference = "a", Name = "product1" }, 
     new Product { UserReference = "b", Name = "product2" } 
    }; 

    [HttpGet] 
    [Route("")] 
    public IEnumerable<Product> GetAllProducts() { 
     return products; 
    } 

    [HttpGet] 
    [Route("{userReference}")] 
    public IHttpActionResult GetProducts(string userReference) { 
     var res = products.Where(t => t.UserReference == userReference); 

     if (res == null) 
      return NotFound(); 

     return Ok(res); 
    } 
} 

あなたはちょうど

を使用v2の使用したい場合には

[ApiVersion2RoutePrefix( "products")]

0

Mike Wassonのオリジナルソースを変更した人はほとんどありません。私はASP.NET Web API 2 book by Jamie Kurtz, Brian Wortmanで使用されているものが好きです。

可動部分が多すぎるので、a sample project at GitHubを作成しました。私は個人的に私のプロジェクトのいくつかで同様のコードを使用していました。

RouteConfigファイルでカスタムルートを削除します。その後

config.Routes.MapHttpRoute(
    name: "DefaultApi", 
    routeTemplate: "api/{version}/{controller}", 
    defaults: new { version = "v2" } 
); 

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

、あなたが次にApiVersionConstraint

public class ApiVersionConstraint : IHttpRouteConstraint 
{ 
    public ApiVersionConstraint(string allowedVersion) 
    { 
     AllowedVersion = allowedVersion.ToLowerInvariant(); 
    } 

    public string AllowedVersion { get; private set; } 

    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, 
     IDictionary<string, object> values, HttpRouteDirection routeDirection) 
    { 
     object value; 
     if (values.TryGetValue(parameterName, out value) && value != null) 
     { 
      return AllowedVersion.Equals(value.ToString().ToLowerInvariant()); 
     } 
     return false; 
    } 
} 

を追加し、あなたには、コントローラにRoutePrefixを置き、あなたが行われています。

[RoutePrefix("api/{apiVersion:apiVersionConstraint(v1)}/values")] 
public class ValuesController : ApiController 
{ 
    // GET api/v1/values 
    [Route("")] 
    public IEnumerable<string> Get() 
    { 
     return new string[] { "v1-value1", "v1-value2" }; 
    } 

    // GET api/v1/values/5 
    [Route("{id}")] 
    public string Get(int id) 
    { 
     return "v1-value-" + id; 
    } 
} 
関連する問題