Daten mit ASP.NET zum Client schicken, oder: Wieso eigentlich HttpHandler?
Einführung
Wenn per Browser eine Webseite abgerufen wird, sendet der Browser eine GET oder POST Anforderung an den Webserver zu einen bestimmten Identifier bzw. Adresse.
Der Webserver schickt dann - aufgrund der Infos die per Request hineingekommen sind - die entsprechenden Daten zurück.
Bei einer Webseite werden diese Daten im Textformat verschickt.
Der Contentype ist dann entweder text/html bei normalen Html Dokumenten oder application/xhtml+xml bei XHtml Dokumenten.
Sobald die Seite im Browser angekommen ist, geht der Parser den Text durch.
Falls es dort noch weitere verlinkte Dateien gibt (Html / Javascript- / CSS-Includes, Bilder) lädt der Browser die genannten Ressourcen nach (Auch per GET oder POST Anforderung an den Webserver) und stellt diese innerhalb der Webseite dar.
Es fällt aber auf, das diese Daten, bspw. ein Bild nicht direkt im Html Quellcode steht, wo also sind diese Daten und weshalb nicht im Code der Webseite?
Diese Daten befinden sich je nach Situation entweder im Arbeitsspeicher oder auf der Festplatte bei den temporären Internetdateien.
Diese Daten werden also getrennt gehalten. Das macht auch auf verschiedene Arten mehr als Sinn.
- Ist so der Quelltext übersichtlich und nicht durch irgendwelche binäre Zeichenfolgen verwüstet
- Werden diese externen Ressourcen je nach Einstellung im Browsercache gehalten und müssen nicht bei jeder Anforderung zusätzlich geladen werden
- Sind diese externen Daten auch phyisisch vom restlichen Code getrennt
Eine ASPX Seite ist nichts anderes als eine Textdatei mit einer speziellen Endung, trifft der Webserver auf diese Endung, wird die besagte Datei von der ASP.NET Engine verarbeitet.
Diese Engine macht aus der ASPX Seite, dem deklerativen Teil und etwaigen externen Ressourcen schlussendlich das Endergebnis, das der Webserver dann zum Browser sendet.
Was ist ein HttpHandler?
Ein Handler kann sich als Teil des Prozesses vorgestellt werden, der bei einem Request abgearbeitet wird.
Wenn ein Request eintrifft, wird anhand von Mappings entschieden, welcher Handler den Request abarbeitet.
In ASP.NET gibt es schon mehrere bestehende Handler, der bekannteste von ihnen ist der Page Handler der auf alle Dateianforderungen mit der Endung *.aspx reagiert.
Webservices *.asmx gehen auch durch einen spezifischen Handler. Sowie bspw. WebResource.axd.
Die Klasse "Page", die bzw. deren Ableitungen für jede ASPX Seite benutzt wird, stellt den oben genannten Handler dar.
Sie implementiert das Interface IHttpHandler:
public class Page : TemplateControl, IHttpHandler { }
Und was macht jetzt ein Handler?
Ein Handler bearbeitet einen Request der ihm zugeteilt wurde.
Bei einer Anforderung auf den Handler selbst (Handler.ashx) oder auf einen Pfad der auf den Handler gemappt ist.
Im Falle des HttpHandlers Klasse Page stellt dieser Handler ein Grundgerüst für Webseite bereit.
Und wieso noch mehr Handler?
Nicht jeder Request geht an eine Webseite, es gehen viele Requests an andere Median wie bspw. ein Bild.
Wenn ein Request auf eine Datei geht die auf dem Webserver liegt, wird diese vom Webserver direkt ausgeliefert.
Es gibt jedoch Situationen in denen bspw. Bilder dynamisch zum Client geschickt werden sollen.
Und genau für solche Situationen kann ein HttpHandler geschrieben werden.
Im Unterschied zum Page HttpHandler ist eine Klasse die nur IHttpHandler implenentiert nur ein Grundgerüst.
Ein einfacher HttpHandler
public class HelloWorld : IHttpHandler
{ public void ProcessRequest(HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable {
get { return false; }
}
}
Die Eigenschaft IsReusable gibt an ob der Handler für einen weiteren Request wiederverwendet werden soll.
In der Methode ProcessRequest wird - wie der Name sagt - der Prozess abgearbeitet.
Über das Argument context kann auf den aktuellen Request, Response, etc... zugegriffen werden.
Wie schon weiter oben geschrieben gibt es verschiedene Content Typen die dem Client mitteilen um welchen Typ Datei es sich handelt.
Mit context.Response.ContentType kann der gewünschte ContentType angegeben werden. Bei einem jpg Bild wäre das bspw. image/jpeg.
Eine Liste der häufigsten Content Typen stellt SelfHtml bereit.
Im nächsten Schritt erfolgt die Ausgabe per Response Objekt.
Bei Text wird die Methode .Write benutzt, bei einer binären Datei (zip, exe, dll) .WriteFile oder .BinaryWrite.
Wie wird ein HttpHandler aktiv?
Ein HttpHandler kann - sowie auch eine ASPX Datei - über seinen Dateipfad abgerufen werden.
Oder per Mapping auf einen bestimmten Pfad, Endung und Übertragungstyp (GET / POST) das bspw. in der web.config erstellt werden kann.
In unserem Beispiel sieht das Mapping in der web.config im <system.web> Zweig so aus:
<httpHandlers>
<add verb="*" path="*.genial" type="HttpHandlerTest.Genial" />
</httpHandlers>
Wenn eine Ressource mit der Endung .genial angefragt wird, verarbeitet der Handler Genial den Request und gibt die Response zurück.
Im Beispiel machen wir nichts anderes als die Url abzufragen, zu splitten und eine formatierte Ausgabe der Url zu tätigen.
public class Genial : IHttpHandler
{ public void ProcessRequest(HttpContext context) {
context.Response.ContentType = "text/plain"; string value = context.Request.Url.ToString().Split('/')[3];
value = value.Replace(".", " ");
context.Response.Write(value);
}
public bool IsReusable {
get { return false; }
}
}
Bei einer Eingabe der Url: http://localhost:<Port>/das.ist.genial kommt folgende Ausgabe zurück:
das ist genial
Ist es das nicht? :-)
Ein weiterer Anwendungsfall gefällig?
In einer Anwendung sollen Downloads zur Verfügung gestellt werden.
Dies sollte (vielfach leider nicht der Fall) mit einem HttpHandler erledigt werden.
Ein Link auf eine Datei könnte folgendermassen lauten: localhost/ImageHandler.ashx?path=images/meinBild.jpg
Das funktioniert zwar, sieht aber nicht wirklich schön aus.
Mit einem HttpHandler, dem entsprechendem Mapping und UrlRewrite könnte das so aussehen: localhost/download/meinBild.jpg
Ich beleuchte hier nur die Rolle des HttpHandlers.
Es besteht die HttpHandler Klasse "Download" die einen Download ausliefert und auf folgendes Mapping reagiert:
<add verb="*" path="/download/*" type="Knowledgebase.Download"/>
Der Handler reagiert auf alles was nach dem Pfad "/download/" folgt.
Also bspw. auf einen solchen Pfad: localhost/download/23_meineDatei.zip
Genauso ist es auch möglich einen "catch all" HttpHandler zu schreiben, der auf alle Anforderungen reagiert.
Das wäre bspw. eine Möglichkeit für einen Log-Handler.
Wie man sieht, gibt es viele Gründe die dafür sprechen HttpHandler zu verwenden sowie auch viele Einsatzzwecke.
Wieso sollte eine Datei oder ein Bild nicht per ASPX Seite versendet werden?
Es ist möglich Grafiken oder Dateien per ASPX Seite an den Client zu schicken, jedoch bringt das Nachteile und Fallstricke mit sich.
Der HttpHandler für die ASPX Seiten, also die Klasse Page stellt ein Grundgerüst zur Verfügung um eine Webseite zu rendern.
Auch wenn noch kein Inhalt auf der Webseite steht, wird ein Grundgerüst als Text zum Client geschickt.
Wenn jetzt also ein Bild zum Client geschickt werden soll, muss sichergestellt werden das wirklich auch nur das Bild geschickt wird.
Ansonsten kann es zu einem solchen ungemütlichen Gemisch beim Client kommen:
[...]
iQZ�RE!QZ�RE!QZ�RE4�(�S) �B(��L�4�LQEj�G��
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "<A href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd</A>">
<html xmlns="<A href="http://www.w3.org/1999/xhtml">http://www.w3.org/1999/xhtml</A>" >
<head><title>
[...]
Um das zu verhindern muss ganz am Anfang ein Response.Clear() und am Ende ein Response.End() abgesetzt werden.
Der Code dazwischen ist der selbe wie auch bei einem normalen HttpHandler.
Eine ASPX Seite sollte aber als Prinzip nicht zum senden von etwas anderem als Webseiten "missbraucht" werden.
Und wie rufe ich jetzt ein Bild über einen HttpHandler ab?
Es besteht jetzt bspw. ein HttpHandler Namens "Thumbnail.ashx", über GET oder POST Parameter die einer Anfrage mitgegeben werden, gibt der Handler entsprechend ein Thumbnail Bild zurück.
Auf einer ASPX Seite wird ein Image Control oder ein normaler <img>-Tag mit der Adresse des Handlers (bspw. localhost/Thumbnail.ashx?image=me.jpg) ausgestattet.
Wenn die Seite im Browser geparst wird, wird die Information - also die binären Daten - vom Handler gelesen und auf der Seite dargestellt.
Das ganze findet in einem eigenen Request / Response statt und hat mit der Auslieferung der Webseite an sich nur indirekt zu tun.
Unterschiedliches Verhalten der Webserver im Bezug auf HttpHandler, oder: Hilfe! Mein HttpHandler wird nicht aufgerufen.
Beim IIS 5.0 / 5.1 / 6.0 wird nicht standardmässig jeder Request über die ASP.NET Engine abgehandelt,
das führt dann dazu, das der Handler ggf. nicht angesprungen wird.
Beim IIS 7 / WebDev Server ist das Verhalten genau anders rum, dort wird immer jeder request durch die ASP.NET Engine abgehandelt.
Entweder muss man eine schon auf die ASP.NET Engine registrierte Endung nehmen (aspx / ashx / axd), deine gewünschte Endung selber im IIS registrieren (Handler Mapping) oder aber eine Application Wildcard einrichten.
Mit einer Wildcard läuft jeder Request - wie standardmässig beim IIS7 - immer über die ASP.NET Engine.
Wie man eine Application Wildcard einrichtet ist der Dokumentation von urlrewritingnet zu entnehmen:
- http://www.urlrewriting.net/154/de/dokumentation.html
Ich hoffe mit diesem Artikel ein wenig Licht ins Dunkle gebracht zu haben und danke euch fürs lesen.
Natürlich freue ich mich über jeden einzelnen Kommentar!
Weiterführende Links:
Änderungen / Korrekturen:
22.07.09 - Besonderheiten der Webserver im Bezug auf HttpHandler hinzugefügt.