asp.net mvc - Is there a way to have a RoutePrefix that starts with an optional parameter? -
i want reach bikes controller these url's:
/bikes // (default path us) /ca/bikes // (path canada)
one way of achieving using multiple route attributes per action:
[route("bikes")] [route("{country}/bikes")] public actionresult index()
to keep dry i'd prefer use routeprefix, multiple route prefixes not allowed:
[routeprefix("bikes")] [routeprefix("{country}/bikes")] // <-- error: duplicate 'routeprefix' attribute public class bikescontroller : basecontroller [route("")] public actionresult index()
i've tried using route prefix:
[routeprefix("{country}/bikes")] public class bikescontroller : basecontroller
result: /ca/bikes works, /bikes 404s.
i've tried making country optional:
[routeprefix("{country?}/bikes")] public class bikescontroller : basecontroller
same result: /ca/bikes works, /bikes 404s.
i've tried giving country default value:
[routeprefix("{country=us}/bikes")] public class bikescontroller : basecontroller
same result: /ca/bikes works, /bikes 404s.
is there way achieve objective using attribute routing? (and yes, know can stuff registering routes in routeconfig.cs, that's not i'm looking here).
i'm using microsoft.aspnet.mvc 5.2.2.
fyi: these simplified examples - actual code has irouteconstraint {country} values, like:
[route("{country:countrycode}/bikes")]
i bit late party, have working solution problem. please find detailed blog post on issue here
i writing down summary below
you need create 2 files given below
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 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 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; } } }
once done, open webapiconfig.cs , add below given line
config.maphttpattributeroutes(new _3btechtalkmultipleprefixdirectrouteprovider());
that's it, can add multiple route prefix in controller. example below
[_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" }); } }
i have uploaded working solution here
happy coding :)
Comments
Post a Comment