2014-07-24 5 views
5

Ho bisogno di mappare due variabili che potrebbero contenere barre, su un controller, nella mia applicazione ASP MVC. Vediamo questo con un esempio.MVC: Come gestire slahs in URL int il primo parametro di percorso

enter image description here

  • Repository e percorso sarà parametri con codifica URL.
  • Il deposito può avere 0 barre o 1 barra al massimo (rep o rep/modulo)
  • Il percorso può avere un numero arbitrario di barre.

Per esempio questi sono URL validi:

http://mysite/rep/Items 
http://mysite/rep/module/Items/foo/bar/file.c 

Qualcuno potrebbe dare alcuni suggerimenti su come definire questo percorso?

+0

Deve essere quell'URL esatto, o quei parametri possono essere codificati tramite URL? (Sto indovinando il primo, dal momento che quest'ultimo è un * molto * più facile, ma vale la pena chiedere esplicitamente.) – David

+0

@David: Vedi le mie modifiche –

+0

Se non puoi codificare l'URL, l'unica cosa che puoi fare è prendere il l'intero URL come stringa e analizzalo tu stesso. – DavidG

risposta

2

Infine, con sede nella risposta di Darin Dimitrov, ho implementato il seguente itinerario personalizzato, che risolve il mio problema:

public class RepositoryRoute : Route 
{ 
    public RepositoryRoute(string name, string url, object defaults) 
     : base(url, new RouteValueDictionary(defaults), new MvcRouteHandler()) 
    { 
     string moduleUrl = url.Replace(
      REPOSITORY_PARAMETER, REPOSITORY_PARAMETER + MODULE_PARAMETER); 
     mModuleRoute = new Route(
      moduleUrl, new RouteValueDictionary(defaults), new MvcRouteHandler()); 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     RouteData rd = mModuleRoute.GetRouteData(httpContext); 

     if (rd == null) 
      return base.GetRouteData(httpContext); 

     if (!rd.Values.ContainsKey(MODULE)) 
      return rd; 

     // set repository as repo/submodule format 
     // if a submodule is present in the URL 
     string repository = string.Format("{0}/{1}", 
      rd.Values[REPOSITORY], 
      rd.Values[MODULE]); 

     rd.Values.Remove(MODULE); 
     rd.Values[REPOSITORY] = repository; 

     return rd; 
    } 

    Route mModuleRoute; 

    const string REPOSITORY = "repository"; 
    const string MODULE = "module"; 

    const string REPOSITORY_PARAMETER = "{" + REPOSITORY + "}/"; // {repository}/ 
    const string MODULE_PARAMETER = "{" + MODULE + "}/"; // {module}/ 
} 

che è registrato nel seguente modo:

 routes.Add(new RepositoryRoute(
         "Items", 
         "{repository}/Items/{*path}", 
         new { controller = "Items", action = "Index", path = "/" } 
     )); 

Gli usi di percorso una route interna, che definisce un parametro del modulo, e se viene trovata, la concatro al repository e la rimuovo. Quindi il repository o il repository/modulo di mapping è trasparente.

-1

Non è possibile eseguire correttamente questa mappatura, a causa della natura di questo problema. Cercare di ottenere una matita e carta seguente URL per repository e percorso:

http://mysite/rep/Items/Items/Items/Items/Items/Items/Items/Items/Items 

Ci sono molteplici mappature:

1) Repository = Oggetti path = Voci/Voci/Voci/Voci/Voci/Voci/Articoli

2) Repository = Voci/articoli path = Voci/items/articoli/articoli/items/Articoli

e così via ....

Quindi, o si dovrebbe

  1. parametri passare come stringa di query
  2. Definire percorsi multipli per ogni formato di repository (e aggiungere parti al nome completo repository in metodo di controllo)
+0

The Items è una costante (è il nome del controller) in modo da poter discretizzare: tutti i blocchi a sinistra del nome del controller sono il repository e tutti pezzi a destra del nome del controller, è il percorso. –

0

Se non puoi vivere con parametri di stile "vecchio stile" e codifica URL, penso che l'unico modo per ottenerlo sia come questo. Si noti che questo non è testato, ma dovrebbe funzionare fondamentalmente. Inoltre ho messo il nome del controller all'inizio e il separatore Items ora è sostanzialmente privo di significato oltre a fungere da delimitatore.

controller

Creare un controller con un unico metodo senza parametri:

public class GetRepo : Controller 
{ 
    public ActionResult Index() 
    { 
     //TBC 
     return View(); 
    } 
} 

Routing

Assicurarsi di routing è impostato in modo da consentire http://www.example.com/GetRepo/nulla al percorso per la vostra metodo dell'indice

Si noti che la parte GetRepo è importante in quanto altrimenti cosa succede se l'URL è www.example.com/blah/repo/items/other/stuff e si ha un controller chiamato blah?

The Magic

Ora è decostruire l'URL manualmente utilizzando Request.Url.AbsolutePath.

var urlPath = Request.Url.AbsolutePath; 

//Split the path by '/', drop the first entry as it's the action method 
var parts = urlPath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries) 
    .Skip(1).ToList(); 

//Find where "Items" separator appears: 
var posOfItems = parts.IndexOf("Items"); 

//Everything before separator is the repo: 
var repo = string.Join("/", parts.Take(posOfItems)); 

//Everything after separator is the path: 
var path = string.Join("/", parts.Skip(posOfItems + 1)); 

//Now do something with repo/path variables 
+0

Grazie per la risposta. Questa soluzione non è valida per me, perché dobbiamo mantenere la compatibilità con i vecchi URL. Il vecchio URL supporta solo i repository (senza moduli). Ho solo bisogno che se si aggiunge un modulo al repository (repo/modulo), deve funzionare allo stesso modo. Sto rompendo la testa per capire come farlo. –

+0

beh, potresti riuscire a farla franca purché tu abbia un singolo controller e nient'altro nella soluzione che potrebbe entrare in conflitto. – DavidG

+0

O almeno si è sicuri che un percorso di repository non sarà in conflitto con il nome di un controller. – DavidG

2

Sembra un percorso personalizzato potrebbe tagliare la senape:

public class MyRoute: Route 
{ 
    public MyRoute() 
     : base("{*catchall}", new MvcRouteHandler()) 
    { 
    } 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     var rd = base.GetRouteData(httpContext); 
     if (rd == null) 
     { 
      // we do not have a match for {*catchall}, although this is very 
      // unlikely to ever happen :-) 
      return null; 
     } 

     var segments = httpContext.Request.Url.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); 
     if (segments.Length < 4) 
     { 
      // we do not have the minimum number of segments 
      // in the url to have a match 
      return null; 
     } 

     if (!string.Equals("items", segments[1], StringComparison.InvariantCultureIgnoreCase) && 
      !string.Equals("items", segments[2], StringComparison.InvariantCultureIgnoreCase)) 
     { 
      // we couldn't find "items" at the expected position in the url 
      return null; 
     } 

     // at this stage we know that we have a match and can start processing 

     // Feel free to find a faster and more readable split here 
     string repository = string.Join("/", segments.TakeWhile(segment => !string.Equals("items", segment, StringComparison.InvariantCultureIgnoreCase))); 
     string path = string.Join("/", segments.Reverse().TakeWhile(segment => !string.Equals("items", segment, StringComparison.InvariantCultureIgnoreCase)).Reverse()); 

     rd.Values["controller"] = "items"; 
     rd.Values["action"] = "index"; 
     rd.Values["repository"] = repository; 
     rd.Values["path"] = path; 
     return rd; 
    } 
} 

che potrebbe essere registrato prima che i percorsi standard:

public static void RegisterRoutes(RouteCollection routes) 
{ 
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

    routes.Add("myRoute", new MyRoute()); 

    routes.MapRoute(
     "Default", // Route name 
     "{controller}/{action}/{id}", // URL with parameters 
     new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults 
    ); 
} 

E se avete intenzione di mettere stringhe arbitrarie nella parte del percorso dei tuoi URL, spero tu sia a conoscenza dello Zombie Operating Systems che potrebbe sorprenderti.

+0

"myRoute" non sarebbe diventato semplicemente il percorso predefinito? Prenderebbe ogni singolo URL. –

+0

Ah. Scusate. Ho appena notato il 'return null' nel caso in cui si determini che non corrisponde al catch di tutte le route che stai cercando di analizzare. –

+0

@Darin Dimitrov: Grazie per la risposta. Sospetto che questa soluzione non sia generica. Potrebbe essere possibile implementarlo per QUALSIASI controller e QUALSIASI azione? –