web-dev-qa-db-fra.com

Comment acheminer une URL avec une chaîne de requête dans ASP.NET MVC?

J'essaie de configurer un itinéraire personnalisé dans MVC pour extraire une URL d'un autre système au format suivant:

../ABC/ABC01?Key=123&Group=456

Le 01 après le deuxième ABC est un numéro d’étape qui changera et les paramètres Clé et Groupe changeront. Je dois diriger ceci vers une action dans un contrôleur avec la clé de numéro d'étape et le groupe en tant que paramètres. J'ai tenté le code suivant, mais une exception est générée:

Code:

routes.MapRoute(
    "OpenCase", 
    "ABC/ABC{stepNo}?Key={key}&Group={group}",
    new {controller = "ABC1", action = "OpenCase"}
);

Exception:

`The route URL cannot start with a '/' or '~' character and it cannot contain a '?' character.`
32
Jason Underhill

Vous ne pouvez pas inclure la chaîne de requête dans la route. Essayez avec un itinéraire comme celui-ci:

routes.MapRoute("OpenCase", "ABC/ABC{stepNo}",
   new { controller = "ABC1", action = "OpenCase" });

Ensuite, sur votre contrôleur, ajoutez une méthode comme celle-ci:

public class ABC1 : Controller
{
    public ActionResult OpenCase(string stepno, string key, string group)
    {
        // do stuff here
        return View();
    }        
}

ASP.NET MVC mappera automatiquement les paramètres de chaîne de requête aux paramètres de la méthode dans le contrôleur. 

39
Hector Correa

Lors de la définition des itinéraires, vous ne pouvez pas utiliser un / au début de l'itinéraire:

routes.MapRoute("OpenCase",
    "/ABC/{controller}/{key}/{group}", <-- Bad; see / at beginning
    new { controller = "", action = "OpenCase" },
    new { key = @"\d+", group = @"\d+" }
    );

routes.MapRoute("OpenCase",
    "ABC/{controller}/{key}/{group}", <-- Good; No /  at beginning
    new { controller = "", action = "OpenCase" },
    new { key = @"\d+", group = @"\d+" }
    );

Essaye ça:

routes.MapRoute("OpenCase",
    "ABC/{controller}/{key}/{group}",
    new { controller = "", action = "OpenCase" },
    new { key = @"\d+", group = @"\d+" }
    );

Ensuite, votre action devrait ressembler à ceci:

public ActionResult OpenCase(int key, int group)
{
    //do stuff here
}

On dirait que vous assemblez la stepNo et le "ABC" pour obtenir un contrôleur au format ABC1. C'est pourquoi j'ai remplacé cette section de l'URL par {controller}

Etant donné que vous avez également un itinéraire définissant la "clé" et le "groupe", l'itinéraire ci-dessus capturera également votre URL initiale et l'enverra à l'action.

4
George Stocker

Il n'y a aucune raison d'utiliser le routage basé sur la chaîne de requête dans le nouveau projet ASP.NET MVC. Cela peut être utile pour les anciens projets convertis à partir d'un projet ASP.NET classique et pour lesquels vous souhaitez conserver les URL.

Une solution peut être le routage par attribut.

Une autre solution peut consister à écrire un routage personnalisé en dérivant de RouteBase:

public class MyOldClassicAspRouting : RouteBase
{

  public override RouteData GetRouteData(HttpContextBase httpContext)
  {
    if (httpContext.Request.Headers == null) //for unittest
      return null;

    var queryString = httpContext.Request.QueryString;

    //add your logic here based on querystring
    RouteData routeData = new RouteData(this, new MvcRouteHandler());
    routeData.Values.Add("controller", "...");
    routeData.Values.Add("action", "...");
  }

  public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
  {
     //Implement your formating Url formating here
     return null;
  }
}

Et enregistrez votre classe de routage personnalisée

public static void RegisterRoutes(RouteCollection routes)
{
  ...

  routes.Add(new MyOldClassicAspRouting ());
}
1
Tomas Kubes

Les arguments de chaîne de requête sont généralement spécifiques à ce contrôleur et à cette logique d'application spécifique. 

Il sera donc préférable que cela ne soit pas écrit dans les règles de route, qui sont générales.

Vous pouvez intégrer la détection de chaîne de requête à un argument d'action de la manière suivante.

Je pense que c'est mieux d'avoir un contrôleur pour gérer StepNo.

public class ABC : Controller
{
    public ActionResult OpenCase(OpenCaseArguments arg)
    {
        // do stuff here
        // use arg.StepNo, arg.Key and arg.Group as You need
        return View();
    }        
}

public class OpenCaseArguments
{
    private string _id;
    public string id
    {
        get
        {
            return _id;
        }

        set
        {
            _id = value; // keep original value;
            ParseQueryString(value);
        }
    }

    public string  StepNo { get; set; }
    public string Key { get; set; }
    public string Group { get; set; }

    private void ParseQueryString(string qs)
    {
        var n = qs.IndexOf('?');
        if (n < 0) return;
        StepNo = qs.Substring(0, n); // extract the first part eg. {stepNo}
        NameValueCollection parms = HttpUtility.ParseQueryString(qs.Substring(n + 1));
        if (parms.Get("Key") != null) Key = parms.Get("Key");
        if (parms.Get("Group") != null) Group = parms.Get("Group");
    }

}

ModelBinder attribue une valeur {id} au champ id de OpenCaseArguments. La méthode set gère la logique de division de chaîne de requête.

Et continuez le routage de cette façon. Remarque le routage obtient votre chaîne de requête en argument id.

routes.MapRoute(
    "OpenCase", 
    "ABC/OpenCase/{id}",
    new {controller = "ABC", action = "OpenCase"}
);

J'ai utilisé cette méthode pour obtenir plusieurs champs valeur clé sur l'action du contrôleur.

0
RFex