Datastyrede routes i ASP.NET MVC

I disse tider med større og større fokus på SEO-venlighed, kan det være rart, hvis man kan basere sine url'er på data fra eksempelvis sin database.

Man kunne have en produktdatabase, hvor produktet har en unik (url-venlig) streng, som kan anvendes til at finde produktet. I et setup uden nogen specielle regler, kan dette være en udfordring at få gjort stømlinet. Med en routedefinition som denne:

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

hvor produkt-controlleren hedder ProduktController, action-metoden til at vise et produkt hedder Vis og produktets urlnavn ligger i id-parameteren, kunne url'en til et produkt se således ud:

/produkt/vis/stor-tallerken-med-sort-kant

Det kan være fint nok ift. SEO, men det ville jo være rart, hvis man kunne slippe for de lidt overflødige controller- og action-tekster, så url'en kom til at se således ud i stedet:

/stor-tallerken-med-sort-kant

Dette kan opnåes med en RouteConstraint og en wildcard-urlparameter. Route-mapping kunne derfor se således ud:

routes.MapRoute(
    name: "Default",
    url: "{id}",
    defaults: new { controller = "Produkt", action = "Vis" },
    constraints: new { id = new ProduktRouteConstraint() }
);

Nu kaldes ProduktRouteConstraint's Match-metode hver gang routing kaldes. Denne Match-metode skal så håndtere et opslag i produktdatabasen for at se, om den angivne id refererer til et eksisterende produkt. Match-metoden returnerer en bool, der indikerer om den aktuelle route skal anvendes eller ej, dvs. om produktet findes i dette tilfælde. En implementering kunne se således ud:

public class ProduktRouteConstraint : IRouteConstraint
{
    public bool Match(
            HttpContextBase httpContext,
            Route route,
            string parameterName,
            RouteValueDictionary values,
            RouteDirection routeDirection
        )
    {
        var svc = new ProduktService();
        var id = values["id"].ToString();
        return svc.FindesProduktMedUrlnavn(id);
    }
}

Match-metoden fyrer op under en produktservice-klasse, der, via metoden FindesProduktMedUrlnavn, kan svare på, om produktet med det angivne navn (fra url'en) findes eller ej.

Hvis url'en ikke er et match, fortsætter routing-systemet videre til næste regel (som det altid har gjort). Derfor skal denne regel helst ikke være den sidste regel i rækken af routing-regler. 

En ting man skal være opmærksom på med denne metode er, at routing-systemet aktiveres hver gang der foretages et request til sitet. Derfor skal routingconstraints helst være så optimerede som muligt, da sitet ellers vil virke langsomt. Det vil med andre ord være en god idé, at overveje caching af grundlaget for disse specielle routingregler, i dette eksempel produktets urlnavne.

Comment