Thomas Bandt

Über mich | Kontakt | Archiv

ASP.NET 2.0 Themes und URL-Rewriting

Update: The solution for URL Rewriting with ASP.NET 2.0 is released: http://www.urlrewriting.net/!

ASP.NET 2.0 Themes sind eine nette Sache: man legt in ihnen einfach ein Stylesheet ab und definiert in einem Skin-File applikationsübergreifend für Controls die entsprechenden Styles. Die Engine selbst rendert dann alles notwendige selbst, d.h. man braucht sich auch nicht um das Einbinden der *.css-Dateien zu kümmern.

Hier liegt aber auch ein Problem:

ASP.NET verlinkt die Stylesheets immer ausgehend vom aktuellen Verzeichnis in dem man sich befindet. So wird zum Beispiel für website.de/Default.aspx die Adresse wie folgt angegeben:

"App_Themes/Default/web.css" text="text/css" rel="stylesheet" />

Soweit so gut. Benutzt man nun aber URL-Rewriting, kommt man in die Bredouille. Da die umgeschriebene URL aber zum Beispiel website.de/sprache/verzeichnis/default.aspx heißt und auf /website.de/Default.aspx?Sprache=sprache&Folder=verzeichnis gemappt wird, befindet man sich für ASP.NET natürlich im Applikations-Root website.de/, der Browser weiß davon aber natürlich nichts, und sucht das Stylesheet dann ausgehend von /sprache/verzeichnis. Folge: es kann kein Stylesheet gefunden werden.

Was kann man tun?

Der pragmatischste Weg dürfte es sein, in den Head-Bereich der HTML-Seite ein Base-Tag einzufügen. Dann sucht der Browser egal unter welcher URL sämtliche Medien und Stylesheets, die relativ verlinkt sind, unter der URL die in diesem Tag definiert ist. Das Ganze hatte bei mir übrigens nicht funktioniert, weil die Engine die Links zu den CSS-Dateien immer direkt nach dem Head-Tag renderte, und das Base-Tag nach hinten verschob.

Schöner wäre es natürlich, wenn man nicht mehr in den Output eingreifen müsste - denn das entspricht ja nicht unbedingt dem Prinzip und Ziel der Themes-Philosophie. Also habe ich versucht einen Weg zu finden, an die Themes-Klassen heranzukommen und dort die entsprechende Property für die URL's der CSS-Files zu manipulieren und die Pfade richtig zu mappen. Aber ohne Erfolg, an diese Klasse(n) ist kein Herankommen möglich. Alles mögliche kann man sich in .NET selbst bauen, ableiten und weiterbenutzen, aber das ausgerechnet nicht.

Als dritten Weg, den auch ich letztlich benutzt habe, kann man noch eine PageFilter-Klasse benutzen, und diese als HttpModule in die Applikation einbinden. Dieses Modul parst am Ende den kompletten HTML-Output, der nach dem Rendern durch ASP.NET zum Client geschickt wird, und bietet dabei Möglichkeiten den Clientcode zu manipulieren. Ich habe mich dabei dem fertigen Filter von Milan Negovan bedient, und ihn angepasst:

public override void Write(byte[] buffer, int offset, int count)
{
string strBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count);

// ---------------------------------
// Wait for the closing tag
// ---------------------------------
Regex eof = new Regex("", RegexOptions.IgnoreCase);

if (!eof.IsMatch(strBuffer))
{
responseHtml.Append(strBuffer);
}
else
{
responseHtml.Append(strBuffer);

string finalHtml = responseHtml.ToString();
Regex re = null;

// Css-Files auf die absolute Client-URL mappen
re = new Regex("", RegexOptions.IgnoreCase);
finalHtml = re.Replace (finalHtml, new MatchEvaluator (CssMatch));

// Write the formatted HTML back
byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(finalHtml);

responseStream.Write(data, 0, data.Length);
}

}

private string CssMatch(Match m)
{
return m.ToString().Replace(m.Groups[1].Value, ConfigurationManager.AppSettings["ApplicationRoot"] + m.Groups[1].Value);
}

Ich hatte zuerst auch versucht über Page.ResolveUrl() den Pfad automatisch generieren zu lassen, aber zu diesem Zeitpunkt ist es für einen Zugriff darauf scheinbar schon zu spät, sodass ich mir die Basis-URL (website.de/applicationsroot/) in der webc.config abgelegt habe.

Die Regular-Expression  findet sämtliche CSS-Links im o.g. Format, und ruft für diese dann die Methode CssMatch() auf.

Performance-Einbußen konnte ich im Übrigen nicht feststellen.

Kommentare

  1. Fabrice schrieb am Mittwoch, 11. Januar 2006 01:18:00 Uhr:

    I describe another solution here:
    http://weblogs.asp.net/fmarguerie/archive/2006/01/11/435022.aspx
  2. Fabrice schrieb am Mittwoch, 11. Januar 2006 12:14:00 Uhr:

    Scott Watermasysk and Wilco Bauwer wrote in comments to my post that a much more simpler solution exists. A new overload has been added to RewritePath to prevent "client path rebasing".
    See my post for the details.
    http://weblogs.asp.net/fmarguerie/archive/2006/01/11/435022.aspx


« Zurück  |  Weiter »