Asp.net-Mvc

有沒有辦法讓 RoutePrefix 以可選參數開頭?

  • May 7, 2017

我想通過以下 URL 訪問 Bikes 控制器:

/bikes     // (default path for US)
/ca/bikes  // (path for Canada)

實現這一目標的一種方法是每個操作使用多個路由屬性:

[Route("bikes")]
[Route("{country}/bikes")]
public ActionResult Index()

為了保持乾燥,我更喜歡使用 RoutePrefix,但不允許使用多個 Route Prefix:

[RoutePrefix("bikes")]
[RoutePrefix("{country}/bikes")] // <-- Error: Duplicate 'RoutePrefix' attribute    
public class BikesController : BaseController

   [Route("")]
   public ActionResult Index()

我試過只使用這個路由前綴:

[RoutePrefix("{country}/bikes")]
public class BikesController : BaseController

結果:/ca/bikes 有效,/bikes 404s。

我嘗試將國家/地區設為可選:

[RoutePrefix("{country?}/bikes")]
public class BikesController : BaseController

結果相同:/ca/bikes 有效,/bikes 404s。

我試過給國家一個預設值:

[RoutePrefix("{country=us}/bikes")]
public class BikesController : BaseController

結果相同:/ca/bikes 有效,/bikes 404s。

還有其他方法可以使用屬性路由來實現我的目標嗎? (是的,我知道我可以通過在 RouteConfig.cs 中註冊路由來做這些事情,但這不是我在這裡尋找的東西)。

我正在使用 Microsoft.AspNet.Mvc 5.2.2。

僅供參考:這些是簡化的範例 - 實際程式碼具有 {country} 值的 IRouteConstraint,例如:

[Route("{country:countrycode}/bikes")]

我參加聚會有點晚了,但我有一個解決這個問題的有效解決方案。請在此處找到有關此問題的詳細部落格文章

我在下面寫下總結

您需要創建 2 個文件,如下所示



   using System;
   using System.Collections.Generic;
   using System.Collections.ObjectModel;
   using System.Web.Http.Controllers;
   using System.Web.Http.Routing;

   namespace _3bTechTalk.MultipleRoutePrefixAttributes {
    public class _3bTechTalkMultiplePrefixDirectRouteProvider: DefaultDirectRouteProvider {
     protected override IReadOnlyList  GetActionDirectRoutes(HttpActionDescriptor actionDescriptor, IReadOnlyList  factories, IInlineConstraintResolver constraintResolver) {
      return CreateRouteEntries(GetRoutePrefixes(actionDescriptor.ControllerDescriptor), factories, new [] {
       actionDescriptor
      }, constraintResolver, true);
     }

     protected override IReadOnlyList  GetControllerDirectRoutes(HttpControllerDescriptor controllerDescriptor, IReadOnlyList  actionDescriptors, IReadOnlyList  factories, IInlineConstraintResolver constraintResolver) {
      return CreateRouteEntries(GetRoutePrefixes(controllerDescriptor), factories, actionDescriptors, constraintResolver, false);
     }

     private IEnumerable  GetRoutePrefixes(HttpControllerDescriptor controllerDescriptor) {
      Collection  attributes = controllerDescriptor.GetCustomAttributes  (false);
      if (attributes == null)
       return new string[] {
        null
       };

      var prefixes = new List  ();
      foreach(var attribute in attributes) {
       if (attribute == null)
        continue;

       string prefix = attribute.Prefix;
       if (prefix == null)
        throw new InvalidOperationException("Prefix can not be null. Controller: " + controllerDescriptor.ControllerType.FullName);
       if (prefix.EndsWith("/", StringComparison.Ordinal))
        throw new InvalidOperationException("Invalid prefix" + prefix + " in " + controllerDescriptor.ControllerName);

       prefixes.Add(prefix);
      }

      if (prefixes.Count == 0)
       prefixes.Add(null);

      return prefixes;
     }


     private IReadOnlyList  CreateRouteEntries(IEnumerable  prefixes, IReadOnlyCollection  factories, IReadOnlyCollection  actions, IInlineConstraintResolver constraintResolver, bool targetIsAction) {
      var entries = new List  ();

      foreach(var prefix in prefixes) {
       foreach(IDirectRouteFactory factory in factories) {
        RouteEntry entry = CreateRouteEntry(prefix, factory, actions, constraintResolver, targetIsAction);
        entries.Add(entry);
       }
      }

      return entries;
     }


     private static RouteEntry CreateRouteEntry(string prefix, IDirectRouteFactory factory, IReadOnlyCollection  actions, IInlineConstraintResolver constraintResolver, bool targetIsAction) {
      DirectRouteFactoryContext context = new DirectRouteFactoryContext(prefix, actions, constraintResolver, targetIsAction);
      RouteEntry entry = factory.CreateRoute(context);
      ValidateRouteEntry(entry);

      return entry;
     }


     private static void ValidateRouteEntry(RouteEntry routeEntry) {
      if (routeEntry == null)
       throw new ArgumentNullException("routeEntry");

      var route = routeEntry.Route;
      if (route.Handler != null)
       throw new InvalidOperationException("Direct route handler is not supported");
     }
    }
   }


   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Web;
   using System.Web.Http;

   namespace _3bTechTalk.MultipleRoutePrefixAttributes
   {
       [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
       public class _3bTechTalkRoutePrefix : RoutePrefixAttribute
       {
           public int Order { get; set; }

           public _3bTechTalkRoutePrefix(string prefix) : this(prefix, 0) { }

           public _3bTechTalkRoutePrefix(string prefix, int order) : base(prefix)
           {
               Order = order;
           }        
       }
   }

完成後,打開WebApiConfig.cs並將其添加到給定行下方


config.MapHttpAttributeRoutes(new _3bTechTalkMultiplePrefixDirectRouteProvider());

就是這樣,現在您可以在控制器中添加多個路由前綴。下面的例子



   [_3bTechTalkRoutePrefix("api/Car", Order = 1)]
   [_3bTechTalkRoutePrefix("{CountryCode}/api/Car", Order = 2)]
   public class CarController: ApiController {
    [Route("Get")]
    public IHttpActionResult Get() {
     return Ok(new {
      Id = 1, Name = "Honda Accord"
     });
    }
   }

我在這裡上傳了一個可行的解決方案

快樂編碼:)

引用自:https://stackoverflow.com/questions/31053697