ASP.NET MVC custom routing for search

Oto mój scenariusz. Dla przykładu powiedzmy, że muszę zwrócić listę samochodów na podstawie kryteriów wyszukiwania. Chciałbym mieć jeden widok do wyświetlania wyników, ponieważ wynik będzie taki sam, ale potrzebuję kilku sposobów, aby się tam dostać. Na przykład, mogę mieć formularz z pole tekstowe do wyszukiwania według roku. Może mam inną osobną stronę, która zawiera hiperłącze do wszystkich czerwonych samochodów Toyoty. Jak poradzić sobie z wieloma scenariuszami w tym samym widoku i kontrolerze. Mój dylemat jest to, że wyszukiwanie może zawierać kilka opcji ... rok, marka, model, itp, Ale Nie wiem, gdzie je umieścić.

Jakie jest najlepsze podejście do tego? Czy powinienem zdefiniować parametry w routingu, czy iść z ciągami zapytań, itp?

Author: Papa Burgundy, 2008-12-19

3 answers

Opcja 1

Oczywiście zawsze możesz wybrać sposób / samochód / Szukaj/?vendor = Toyota&color = Red & model = Corola i myślę, że to będzie dobre dla Ciebie.

routes.MapRoute(
    "CarSearch",
    "car/search",
    new { controller = "car", action = "search" }
);

Możesz uzyskać params z Request.Params w akcji w tym przypadku.

Opcja 2

Lub można zdefiniować params w tabeli routingu, ale AFAIK będzie wymagane, aby zestaw reguł dla wszystkich możliwych kombinacji, ponieważ kolejność params ma znaczenie, na przykład:

        routes.MapRoute(
            "CarSearch1",
            "car/search/vendor/{vendor}/color/{color}/model/{model}",
            new {controller = "car", action = "search"}
        );

        routes.MapRoute(
            "CarSearch2",
            "car/search/color/{color}/vendor/{vendor}/model/{model}",
            new {controller = "car", action = "search"}
        );

        routes.MapRoute(
            "CarSearch3",
            "car/search/model/{model}/color/{color}/vendor/{vendor}",
            new {controller = "car", action = "search"}
        );

... na i tak dalej. To prawda, jeśli idziesz ze standardowym MvcRouteHandler.

Ale to był prosty sposób :)

Opcja 3

Trudnym, ale, jak sądzę, najbardziej eleganckim sposobem, jest wykonanie własnej implementacji IRouteHandler-da ci to znacznie większą elastyczność w zamówieniu params. Ale znowu, to trudny sposób, nie idź z nim, jeśli masz prostą aplikację. Tak więc, na przykład jak zrobić to w ten sposób (bardzo prosty przykład):

Dodaj nową trasę do listy trasy:

routes.Add
    (
        new Route
            (
                "car/search/{*data}",
                new RouteValueDictionary(new {controller = "car", action = "search", data = ""}),
                new MyRouteHandler()
            )
    );

Dodaj klasy, które zmienią standardowy łańcuch przetwarzania żądań:

class MyRouteHandler : IRouteHandler
{
    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return new MyHttpHandler(requestContext);
    }
}

class MyHttpHandler : MvcHandler
{
    public MyHttpHandler(RequestContext requestContext) : base(requestContext)
    {
    }

    protected override void ProcessRequest(HttpContextBase httpContext)
    {
        IController controller = new CarController();
        (controller as Controller).ActionInvoker = new MyActionInvoker();
        controller.Execute(RequestContext);
    }
}

class MyActionInvoker : ControllerActionInvoker
{
    protected override ActionResult InvokeActionMethod(MethodInfo methodInfo, IDictionary<string, object> parameters)
    {
        // if form of model/{model}/color/{color}/vendor/{vendor}
        var data = ControllerContext.RouteData.GetRequiredString("data");
        var tokens = data.Split('/');

        var searchParams = new Dictionary<string, string>();
        for (var i = 0; i < tokens.Length; i++)
        {
            searchParams.Add(tokens[i], tokens[++i]);
        }

        parameters["searchParams"] = searchParams;

        return base.InvokeActionMethod(methodInfo, parameters);
    }
}

In controller:

public ActionResult Search(IDictionary<string, string> searchParams)
{
    ViewData.Add
        (
            // output 'model = Corola, color = red, vendor = Toyota'
            "SearchParams",
            string.Join(", ", searchParams.Select(pair => pair.Key + " = " + pair.Value).ToArray())
        );
    return View();
}

I będzie działać z dowolnymi parametrami wyszukiwania:

/car/search/vendor/Toyota/color/red/model/Corola
/car/search/color/red/model/Corola/vendor/Toyota
/car/search/model/Corola/color/red/vendor/Toyota

Ale również nie zapomnij zrobić logikę generowania linków, ponieważ Html.ActionLink i Html.RenderLink nie da ci adresu url w ładnej formie / car / search/model/Corola/color/red/vendor / Toyota, więc musisz zrobić niestandardowy generator linków.

Więc, jeśli potrzebujesz naprawdę elastycznego routing - lepiej idź tą drogą:)

 78
Author: maxnk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-19 00:58:36

Każda metoda (akcja) na kontrolerze przyjmowałaby różne parametry, ale tworzyłaby tę samą kolekcję wyników wyszukiwania. Wtedy każdy z nich

return View("SearchResult", searchResultCollection);

Wszystkie używają tego samego widoku, SearchResult.aspx.

 0
Author: Matthew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-18 22:40:59

Coś w tym stylu powinno zrobić to, czego szukasz. Zauważ, że istnieją dwie różne metody akcji, ale obie zwracają wywołanie DisplayResults () - więc kończą używając tego samego widoku, z różnymi danymi ViewData.

public class SearchController : Controller {

    public ActionResult ByColor(Color[] colors) {
         List<Car> results = carRepository.FindByColor(colors);
         return(DisplayResults(result));
    }

    public ActionResult ByMake(string make) {
         List<Car> results = carRepository.FindByMake(make);
         return(DisplayResults(results));
    }

    private ActionResult DisplayResults(IList<Car> results) {

        // Here we explicitly return the view /Views/Search/Results.aspx
        // by specifying the view name in the call to View();
        return(View("Results", results));
    }
}
 0
Author: Dylan Beattie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-19 00:43:41