带有查询字符串的 RouteUrl - 带有重复键

RouteUrl with querystring - with duplicate keys

我使用此扩展创建一个 RouteUrl,并附加了当前查询字符串。这适用于两次或多次不具有相同键的查询字符串。

public static string CurrentQueryStringRouteUrl(this UrlHelper url, string routeName, RouteValueDictionary routeValues)
{
    var context = url.RequestContext;
    var combinedRouteValues = new RouteValueDictionary();
    var queryString = context.HttpContext.Request.QueryString;

    foreach (var key in queryString.AllKeys.Where(key => key != null))
    {
        combinedRouteValues[key] = queryString[key];
    }

    if (routeValues != null)
    {
        foreach (var routeValue in routeValues)
        {
            combinedRouteValues[routeValue.Key] = routeValue.Value;
        }
    }

    return url.RouteUrl(routeName, combinedRouteValues);
}

当存在同名的查询字符串键时,例如?id=1&id=2&id=3,这个用上面的方法转换成?id=1,2,3。有什么办法可以避免这种情况吗?我希望保留原始查询字符串,因为我将这些值绑定到模型列表中。

我知道我可以创建一个自定义模型绑定器来将逗号分隔的字符串绑定到 string[](或本例中的 int[]),但我希望尽可能避免这种情况的一致性.

你可以这样做,但这是肮脏的 hack:

string _rawstring = context.HttpContext.Request.RawUrl;
int _f;
_f = _rawstring.IndexOf('?');
string _resultString = _rawstring.SubString(_f, _rawstring.Length);

在这里您可以找到有关该问题的有用信息:How to deal with more than one value per key in ASP.NET MVC 3?

我采用了稍微不同的方法,因为我确实需要在查询字符串中使用单独的重复键。我使用计数器更改密钥,然后在呈现 url 字符串后,恢复原始参数名称。我需要这个用于 GridMvc 的 grid_filter 查询,但您可以根据您的目的调整它。

/// <summary>
/// Allows you to create or extend a collection of route values to use in a url action
/// </summary>
public class RouteValueBuilder
{
    readonly RouteValueDictionary routeValues;
    private int gridFilterCounter = 0;

    public RouteValueBuilder(object existingRouteValues = null)
    {
        routeValues = existingRouteValues as RouteValueDictionary ?? new RouteValueDictionary(existingRouteValues);
    }

    public void Add(string field, object value)
    {
        if (field == "grid_filter" && routeValues.ContainsKey(field))
        {
            // Because we can't add duplicate keys and GridMvc doesn't support joined comma format for query strings,
            // we briefly rename each new filter, and then the Finalise method must be called after the url
            // string is rendered to restore the grid_filter names back to normal.
            gridFilterCounter++;
            routeValues.Add(field + gridFilterCounter, value);
        }
        else if (routeValues.ContainsKey(field))
        {
            // Since duplicate key names are not supported, the concatenated comma approach can be used
            routeValues[field] += "," + value;
        }
        else
        {
            routeValues.Add(field, value);
        }
    }

    public RouteValueDictionary Get()
    {
        return routeValues;
    }

    /// <summary>
    /// Cleans up the final string url, fixing workarounds done during the building process.
    /// This must be called after the final url string is rendered.
    /// </summary>
    public static string Finalise(string url)
    {
        // Restores grid_filter parameters to their correct naming.  See comments on Add method.
        for (var i = 0; i < 100; i++)
        {
            url = url.Replace("grid_filter" + i, "grid_filter");
        }

        return url;
    }
}

用法:

var builder = new RouteValueBuilder();
builder.Add("grid_filter", "value1");
builder.Add("grid_filter", "value2");
string url = Html.Action("Index", "Home", builder.Get());
url = RouteValueBuilder.Finalise(url);

编辑:请注意,逗号连接方法实际上在 class 中不起作用,因为它已被编码,但对复制的支持是此示例的主要接受者。