Пользовательские маршруты с ASP.NET MVC для фасетного поиска [от QueryString до Route] - PullRequest
2 голосов
/ 25 февраля 2012

Я реализую функцию граненого поиска, где пользователь может фильтровать и детализировать 4 свойства моей модели: City, Type, Purpose и Value.

У меня есть раздел просмотра с такими гранями:

enter image description here

Каждая строка, отображаемая на изображении выше, является интерактивной, чтобы пользователь мог выполнить детализацию и выполнить фильтрацию ...

Я делаю это с помощью строк запроса, которые я передаю с помощью пользовательского вспомогательного метода ActionLink:

 @Html.ActionLinkWithQueryString(linkText, "Filter",
                                 new { facet2 = Model.Types.Key, value2 = fv.Range });

Этот пользовательский помощник сохраняет предыдущие фильтры (параметры строки запроса) и объединяет их с новыми значениями маршрута, присутствующими в других ссылках действий. Я получаю такой результат, когда пользователь применил 3 фильтра:

http://leniel-pc:8083/realty/filter?facet1=City&value1=Volta%20Redonda&
facet2=Type&value2=6&facet3=Purpose&value3=3

Это работает, но я хотел бы узнать о лучшем / более чистом способе сделать это, используя маршруты. Порядок параметров может меняться в зависимости от примененных пользователем фильтров. Я имею в виду нечто подобное:

http://leniel-pc:8083/realty/filter // returns ALL rows

http://leniel-pc:8083/realty/filter/city/rio-de-janeiro/type/6/value/50000-100000

http://leniel-pc:8083/realty/filter/city/volta-redonda/type/6/purpose/3

http://leniel-pc:8083/realty/filter/type/7/purpose/1

http://leniel-pc:8083/realty/filter/purpose/3/type/4

http://leniel-pc:8083/realty/filter/type/8/city/carangola

Возможно ли это? Есть идеи?

Ответы [ 2 ]

7 голосов
/ 26 февраля 2012

Возможно ли это? Есть идеи?

Я бы оставил параметры строки запроса для фильтрации.

Но если вы хотите получить URL-адреса, которые вы задали в своем вопросе, я рассмотрю 2 возможных метода.

Для обоих подходов, которые я приведу здесь, я предполагаю, что у вас уже есть модель представления:

public class FilterViewModel
{
    public string Key { get; set; }
    public string Value { get; set; }
}

и контроллер:

public class RealtyController : Controller
{
    public ActionResult Filter(IEnumerable<FilterViewModel> filters)
    {
        ... do the filtering ...
    }
}

Первый вариант - написать пользовательский механизм связывания модели, который будет связан с типом IEnumerable<FilterViewModel>:

public class FilterViewModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var filtersValue = bindingContext.ValueProvider.GetValue("pathInfo");
        if (filtersValue == null || string.IsNullOrEmpty(filtersValue.AttemptedValue))
        {
            return Enumerable.Empty<FilterViewModel>();
        }

        var filters = filtersValue.AttemptedValue;
        var tokens = filters.Split('/');
        if (tokens.Length % 2 != 0)
        {
            throw new Exception("Invalid filter format");
        }

        var result = new List<FilterViewModel>();
        for (int i = 0; i < tokens.Length - 1; i += 2)
        {
            var key = tokens[i];
            var value = tokens[i + 1];
            result.Add(new FilterViewModel
            {
                Key = tokens[i],
                Value = tokens[i + 1]
            });
        }

        return result;
    }
}

который будет зарегистрирован в Application_Start:

ModelBinders.Binders.Add(typeof(IEnumerable<FilterViewModel>), new FilterViewModelBinder());

и у вас также будет фильтр маршрута:

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

    routes.MapRoute(
        "Filter",
        "realty/filter/{*pathInfo}",
        new { controller = "Realty", action = "Filter" }
    );

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

Вторая возможность - написать собственный маршрут

public class FilterRoute : Route
{
    public FilterRoute()
        : base(
            "realty/filter/{*pathInfo}", 
            new RouteValueDictionary(new 
            { 
                controller = "realty", action = "filter" 
            }), 
            new MvcRouteHandler()
        )
    {
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var rd = base.GetRouteData(httpContext);
        if (rd == null)
        {
            return null;
        }

        var filters = rd.Values["pathInfo"] as string;
        if (string.IsNullOrEmpty(filters))
        {
            return rd;
        }

        var tokens = filters.Split('/');
        if (tokens.Length % 2 != 0)
        {
            throw new Exception("Invalid filter format");
        }

        var index = 0;
        for (int i = 0; i < tokens.Length - 1; i += 2)
        {
            var key = tokens[i];
            var value = tokens[i + 1];
            rd.Values[string.Format("filters[{0}].key", index)] = key;
            rd.Values[string.Format("filters[{0}].value", index)] = value;
            index++;
        }

        return rd;
    }
}

, который будет зарегистрирован в вашем RegisterRoutes методе:

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

    routes.Add("Filter", new FilterRoute());

    routes.MapRoute(
        "Default",
        "{controller}/{action}/{id}",
        new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}
1 голос
/ 25 февраля 2012

На мой взгляд (и это довольно субъективно) ваш первоначальный подход кажется нормальным.Я думаю, что критерии поиска принадлежат строке запроса, поскольку они представляют собой подмножество ресурсов, которые вы пытаетесь получить.

Ваши URL не имеют особого смысла с точки зрения логической иерархии ресурсов.

Я бы, вероятно, переименовал метод "filter" search, но с фильтрами, являющимися переменными строки запроса.Кроме того, необходимо ли определять фасеты в строке запроса - разве вы не можете достичь того же результата, называя простоту фасета, например,?

...