Question

J'ai un site MVC ASP.NET qui utilise des vues fortement typés. Dans mon cas, une action de contrôleur pourrait ressembler à ceci:

public ActionResult List(MyStrongType data)

Lors de la présentation la page (vue), la réponse va générer une URL qui ressemble à quelque chose comme ça (oui, je sais le routage pourrait générer une URL plus agréable):

http://localhost/Ad/List?F.ShowF=0&ALS.CP=30&ALS.L=0&ALS.OB=0&ALS.ST=0&S=&LS.L1=&LS.L2=&CS.C1=32&CS.C2=34&CS.C3=&ALS.ST=0

Si je soumets à nouveau la page, je peux voir que l'objet de données dans l'action est correctement réglée (selon l'URL) (liant par défaut).

Le problème est: dire que je suis d'ajouter des boutons de page (pour modifier la page) pour obtenir une liste sur mon sitepage, la liste sera contrôlée par des paramètres tels que le filtre, sortorder, quantité de pages par page et ainsi de suite (contrôlé par le querystring). Tout d'abord, je dois inclure tous les paramètres de la requête en cours dans l'URL, et je dois mettre à jour le paramètre de page sans altérer les autres paramètres de la requête. Comment puis-je genereate cette URL de la vue / "HTML helper"?

Je pourrais bien sûr manipuler la chaîne URL manuellement, mais cela implique beaucoup de travail et il sera difficile de tenir à jour si un itinéraire est modifié, il doit y avoir un moyen plus facile? Comme une sorte de collection querystring qui peut être modifié sur le côté service (comme ASP.NET Request.QueryString)?

J'espère ne pas impliquer la route, mais je poster celui que je suis jusqu'à présent de toute façon:

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );

    routes.MapRoute(
        "TreeEditing",
        "{controller}/{action}/{name}/{id}",
        new { controller = "MyCategory", action = "Add", name = string.Empty, id = -1 }
    );

BestRegards

Edit 1: Il est possible de définir les paramètres de la requête comme celle-ci (en vue):

<%= url.Action(new {controller="search", action="result", query="Beverages", Page=2})%>

Mais cela ne génère une URL comme celui-ci (avec la route par défaut):

/search/result?query=Beverages&page=2

Le reste des paramètres est manquant que vous pouvez voir.

Je pourrais d'ajouter bien sûr, chaque paramètre connu dans cette URL action, mais si un paramètre de requête est ajouté ou modifié, il y aura beaucoup de travail pour garder tout à jour.

J'ai lu l'article ASP.NET MVC Framework (partie 2): URL de routage , mais comment puis-je trouver une réponse à mon problème?

Était-ce utile?

La solution

It sounds to me like the problem you have is that you want to be able to easily persist query string values from the current request and render them into the URLs of links in your view. One solution would be to create an HtmlHelper method that returns the existing query string with some changes. I created an extension method for the HtmlHelper class that takes an object and merges its property names and values with the query string from the current request, and returns the modified querystring. It looks like this:

public static class StackOverflowExtensions
{
    public static string UpdateCurrentQueryString(this HtmlHelper helper, object parameters)
    {
        var newQueryStringNameValueCollection = new NameValueCollection(HttpContext.Current.Request.QueryString);
        foreach (var propertyInfo in parameters.GetType().GetProperties(BindingFlags.Public))
        {
            newQueryStringNameValueCollection[propertyInfo.Name] = propertyInfo.GetValue(parameters, null).ToString();
        }

        return ToQueryString(newQueryStringNameValueCollection);
    }

    private static string ToQueryString(NameValueCollection nvc)
    {
        return "?" + string.Join("&", Array.ConvertAll(nvc.AllKeys, key => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(nvc[key]))));
    }
}

It will loop through the query string values from the current request and merge in the properties defined on the object you passed in. So your view code might look like this:

<a href='/SomeController/SomeAction<%=Html.GetCurrentQueryStringWithReplacements(new {page = "2", parameter2 = "someValue"})%>'>Some Link</a>

This is basically saying "keep the query string from the current request, but change the page and parameter2 values, or create them if they didn't exist." Note that if your current request has a "page" query string parameter, this method will overwrite the value from the current request with the one you explicitly pass in from the view. In this case, if your querystring was:

?parameter1=abc&page=1

It would become:

?parameter1=abc&page=2&parameter2=someValue

EDIT: The above implementation will probably not work with the dictionary lookup of querystring parameter names you described. Here is an implementation and that uses a dictionary instead of an object:

    public static string UpdateCurrentQueryString(this HtmlHelper helper, Dictionary<string, string> newParameters)
    {
        var newQueryStringNameValueCollection = new NameValueCollection(HttpContext.Current.Request.QueryString);
        foreach (var parameter in newParameters)
        {
            newQueryStringNameValueCollection[parameter.Key] = parameter.Value;
        }

        return ToQueryString(newQueryStringNameValueCollection);
    }

Your view would call the function by doing an inline initialization of a dictionary and passing it to the helper function like this:

<a href='/SomeController/SomeAction<%=Html.GetCurrentQueryStringWithReplacements(new Dictionary<string,string>() {
{ QuerystringHandler.Instance.KnownQueryParameters[QuaryParameters.PageNr], "2" },
{ QuerystringHandler.Instance.KnownQueryParameters[QuaryParameters.AnotherParam], "1234" }})%>'>Some Link</a>

Autres conseils

I did JUST what you need!

I created an HTML helper for this. The helper takes the same parameters as a normal helper. Yet, it keeps the current values from the URL. I made it both for the URL helper as a ActionLink helper.

This replaces the: Url.Action()

<a href='<%= Html.UrlwParams("TeamStart","Inschrijvingen", new {modID=item.Mod_ID}) %>'  title="Selecteer">
    <img src="<%= Url.Content("~/img/arrow_right.png") %>" alt="Selecteer" width="16" /></a>

and this replaces the Html.ActionLink()

<%: Html.ActionLinkwParams("Tekst of url", "Action", new {test="yes"}) %>

Here is the helper:

using System;
using System.Web.Mvc;
using System.Web.Routing;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Web.Mvc.Html;

namespace MVC2_NASTEST.Helpers {
    public static class ActionLinkwParamsExtensions {
        public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller, object extraRVs, object htmlAttributes) {

            NameValueCollection c = helper.ViewContext.RequestContext.HttpContext.Request.QueryString;

            RouteValueDictionary r = new RouteValueDictionary();
            foreach (string s in c.AllKeys) {
                r.Add(s, c[s]);
            }

            RouteValueDictionary htmlAtts = new RouteValueDictionary(htmlAttributes);

            RouteValueDictionary extra = new RouteValueDictionary(extraRVs);

            RouteValueDictionary m = RouteValues.MergeRouteValues(r, extra);

            //return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, linktext, action, controller, m, htmlAtts);
            return helper.ActionLink(linktext, action, controller, m, htmlAtts);
        }

        public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action) {
            return ActionLinkwParams(helper, linktext, action, null, null, null);
        }

        public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller) {
            return ActionLinkwParams(helper, linktext, action, controller, null, null);
        }

        public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, object extraRVs) {
            return ActionLinkwParams(helper, linktext, action, null, extraRVs, null);
        }

        public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller, object extraRVs) {
            return ActionLinkwParams(helper, linktext, action, controller, extraRVs, null);
        }

        public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, object extraRVs, object htmlAttributes) {
            return ActionLinkwParams(helper, linktext, action, null, extraRVs, htmlAttributes);
        }
    }

    public static class UrlwParamsExtensions {
        public static string UrlwParams(this HtmlHelper helper, string action, string controller, object extraRVs) {
            NameValueCollection c = helper.ViewContext.RequestContext.HttpContext.Request.QueryString;

            RouteValueDictionary r = RouteValues.optionalParamters(c);

            RouteValueDictionary extra = new RouteValueDictionary(extraRVs);

            RouteValueDictionary m = RouteValues.MergeRouteValues(r, extra);

            string s = UrlHelper.GenerateUrl("", action, controller, m, helper.RouteCollection, helper.ViewContext.RequestContext, false);
            return s;
        }

        public static string UrlwParams(this HtmlHelper helper, string action) {
            return UrlwParams(helper, action, null, null);
        }

        public static string UrlwParams(this HtmlHelper helper, string action, string controller) {
            return UrlwParams(helper, action, controller, null);
        }

        public static string UrlwParams(this HtmlHelper helper, string action, object extraRVs) {
            return UrlwParams(helper, action, null, extraRVs);
        }
    }
}

How does it work?

The calls are the same as for the Html.ActionLink() so you simple can replace those.

The method does the following:

It takes all the optional parameters from the current URL and places them in a RouteValueDictionary. It also places the htmlattributes in a dictionary. Then it takes the extra routevalues which you specified manually and places those in a RouteValueDictionary as well.

The key then is to merge the ones from the URL and the ones manually specified.

This happens in the RouteValues class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Collections.Specialized;
using System.Web.Mvc;

namespace MVC2_NASTEST {
    public static class RouteValues {

        public static RouteValueDictionary optionalParamters() {
            return optionalParamters(HttpContext.Current.Request.QueryString);
        }

        public static RouteValueDictionary optionalParamters(NameValueCollection c) {
            RouteValueDictionary r = new RouteValueDictionary();
            foreach (string s in c.AllKeys) {
                r.Add(s, c[s]);
            }
            return r;
        }

        public static RouteValueDictionary MergeRouteValues(this RouteValueDictionary original, RouteValueDictionary newVals) {
            // Create a new dictionary containing implicit and auto-generated values
            RouteValueDictionary merged = new RouteValueDictionary(original);

            foreach (var f in newVals) {
                if (merged.ContainsKey(f.Key)) {
                    merged[f.Key] = f.Value;
                } else {
                    merged.Add(f.Key, f.Value);
                }
            }
            return merged;
        }

        public static RouteValueDictionary MergeRouteValues(this RouteValueDictionary original, object newVals) {
            return MergeRouteValues(original, new RouteValueDictionary(newVals));
        }
    }
}

This is all pretty straightforward. In the end, the actionlink is made with the merged routevalues. This code also lets you remove values from the URL.

Examples:

Your URL is localhost.com/controller/action?id=10&foo=bar. If in that page you place this code

 <%: Html.ActionLinkwParams("Tekst of url", "Action", new {test="yes"}) %>

the URL returned in that element will be localhost.com/controller/action?id=10&foo=bar&test=yes.

If you want to remove an item, you just set the item as an empty string. For example,

 <%: Html.ActionLinkwParams("Tekst of url", "Action", new {test="yes", foo=""}) %>

will return the URL in the <a> element: localhost.com/controller/action?id=10&test=yes

I'm guessing this is all you need?

If you have some additional questions, just ask.

Extra:

Sometimes you will want to keep your values inside your action too, when you will redirect to another Action. With my RouteValues class, this can be done very easily:

 public ActionResult Action(string something, int? somethingelse) {
                    return RedirectToAction("index", routeValues.optionalParamters(Request.QueryString));
 }

If you still want to add some optional parameters, no problem!

 public ActionResult Action(string something, int? somethingelse) {
                    return RedirectToAction("index", routeValues.optionalParamters(Request.QueryString).MergeRouteValues(new{somethingelse=somethingelse}));
 }

I think that covers pretty much everything you'll need.

If you want to set the query string in a link of a view:

Html.ActionLink("LinkName", "Action", "Controller", new { param1 = value1, param2 = value2 }, ...)

If you want to set it in the browser URL after a post back, just call Route* in an action like RouteToAction() and set the parameter key/value you want.

  • If you use the action public ActionResult List(MyStrongType data), you need to include all page settings (page index, ordering,...) as parameters to 'MyStrongType', and the data object will contain all infomation for the view.

  • In the view, if you need to generate a URL, using the approach of CallMeLaNN: Html.ActionLink("LinkName", "Action", "Controller", new { param1 = Model.value1, param2 = Model.param2, ... });. You need to manually set all the parameters here or create a helper to help you fill the URL.

  • You don't need to care about current parameters included in the address.

  • You can route: routes.MapRoute( "custome", "{controller}/{action}/", new { controller = "Home", action = "Index"} ); to generate all the parameters as a query string.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top