Asp.net-Mvc
有沒有辦法讓 RoutePrefix 以可選參數開頭?
我想通過以下 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" }); } }快樂編碼:)