isNumeric – c# oder csharp

Ich weiß nicht wie oft ich schon da stand, und mir mal wieder die Frage gestellt habe, wie unter c# das äquivalent für die in vb verfügbare Funktion isnumeric lautet.

Und immer wieder benötige ich einiges an Zeit um wieder mal herauszufinden dass es kein direktes äquivalent gibt, aber es gibt halt doch eine ganz einfache Lösung.

Man implementiert sich eine solche Funktion mal schnell selbst. Es gibt dazu eine ganze Reihe von ansätzen, so nach dem Motto; viele Wege führen nach Rom.

Ich möchte nun hier meinen persönlichen Favorit dokumentieren, so dass ich Ihn nicht wieder vergesse, ganz nach dem Motto, was du mal geschrieben hast, vergisst du nicht mehr so schnell.

Und hier der Ersatz für die aus vb bekannte isnumeric Funktion:

public static bool IsNumeric(object Expression)   
{   
    bool isNum;   
    double retNum;  
 
    isNum = Double.TryParse(Convert.ToString(Expression),       System.Globalization.NumberStyles.Any, System.Globalization.NumberFormatInfo.InvariantInfo, out retNum);   

    return isNum;   
}  

Windows Dienste – Alternative Installations Möglichkeiten

Beim durchstöbern des Web bin ich auf den Beitrag „Install a Windows Service in a smart way instead of using Windows Installer MSI package“ gestoßen. Dieser Beitrag beschreibt einen alternativen Weg wie man Windows Dienste ohne einen eigens dafür erstellte Setup Routine installieren kann.


Der Artikel ist in Englisch und wurde auf http://www.codeproject.com/ veröffentlicht.

ASP.NET Redirect – Aber bitte Suchmaschinenfreundlich

Wenn Sie die URL zu Ihrer Webseite ändern müssen, sollten Sie bereits vorher die richtigen Maßnahmen ergreifen, dass ein Internet Surfer der auf diese Seite zugreifen möchte nicht den häßlichen Fehler 404 (Datei nicht gefunden) angezeigt bekommt.

Eine durchaus übliche Lösung hierfür ist es ein meta refresh hierfür zu verwenden.

Auf der URL eines Seite anzulegen welche als einzigen Inhalt den nachfolgenden meta refresh enthält.

meta http-equiv="refresh" content="0; URL=http://www.Domain.de/" />

Der oben dargestellte meta refresh leitet ohne Zeitverzögerung den Internet Surfer auf die im Parameter URL stehende neue Webseite um.

So weit so Gut, aber !!

Es gibt 3 Gründe warum man dies nicht machen sollte:
  1. Sie werden diese Umleitung ewig bestehen lassen müssen, da der Internet Surfer nie wirklich etwas von Ihrer neuen Internetseite erfährt.
  2. Suchmaschinen bewerten solche Umleitungen häufig als SPAM und streichen wenn Sie Pech haben, all Ihre Seiten aus Ihrer Datenbank.
  3. Das Thema Ranking spielt hier eine Rolle. Ich Denke jeder kennt das PageRank von Google. Also nehmen wir einmal an Sie hatten auf der alten URL einen PR von 5, im besten Fall akzeptiert die Suchmaschine Ihre Umleitung und behält Ihren Eintrag in Ihrer Datenbank. Das Ranking der Seite liegt aber weiterhin auf der alten URL und wird nicht zu einem eventuelle Ranking der neuen Seite hinzugezogen.

Was geschieht bei einem Redirect egal ob über meta-equiv oder über Source Code „Context.Response.Redirect“.

Die Seite wird umgeleitet und im Response Header wird ein Status Code 302 zurückgeben.

Dieser Status kommt sagt aus das die URL gefunden wurde. Gefunden bedeutet aber, Sie musste gesucht werden, nicht Gut !!

Wenn ein URL Zugriff einwandfrei verläuft dann sollte ein Status Code 200 zurückgegeben werden.

So jetzt haben wir was von Status 200 und 302 gehört, aber wie können wir es erreichen dass wir durch unseren Redirect die Suchmaschinen nicht böße machen und dass die neue URL als Quelle der Information im Internet bekannt wird.

Die Lösung heißt: Permanente Weiterleitung.

Eine Permanente Weiterleitung ist im ersten Moment auch nur ein Redirect, aber der zurückgegeben Status Code ist 301 (URL wurde verschoben). Diese Information nutzen die meisten Suchmaschinen um Ihre Einträge auf die neue URL in Ihren Datenbanken zu aktualisieren. Das hat den Vorteil. dass nach einige Zeit die Informationen über die neue URL direkt in den Suchmaschinen zur Verfügung stehen, und ganz wichtig, das auf der alten liegende PageRank wird auf die neue URL übernommen.

Wie kann ich jetzt eine Permanente Umleitung erstellen:

Mit einem meta-equiv leider nicht (nicht in ASP.NET)

Aber für was haben wir denn ein intelligentes Framework auf dem unser Web läuft.

Zu diesem Zweck erstellen wir einfach eine ASPX Seite die dem alten URL Namen entspricht.

In diese ASPX Datei schreiben wir folgenden Inline Code:

    <%@ Page Language="vb" AutoEventWireup="false"%>
    <SCRIPT runat="server">
    Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Context.Response.Status = "301 Moved Permanently"
    Context.Response.AddHeader("Location", "<http://www.schelian.de/Default.aspx/tabid/189>")
    End Sub
    </SCRIPT>

Wenn Sie nun die Datei auf dem Server gespeichert haben wird eine permanente Suchmaschinenfreundliche Umleitung vorgenommen.

Hier ein Beispiel http://www.schelian.de/MindManager.aspx

Dieser Link wird auf den URL http://www.schelian.de/default.aspx/tabid/155 umgeleitet.

Und nun viel Spaß beim suchmaschinenfreundlichen Umleiten von Seiten!

WinForms Cursor Handling – C#

Hier ein kurzer Source Code Ausschnitt zur Cursor Behandlung in MDI Forms.

// Cursor var definieren um Cursor zu sichern
 Cursor oldCursor;

// aktuellen Cursor Zustand speichern
oldCursor = this.MdiParent.Cursor;

// Neuen Cursor setzen 
this.MdiParent.Cursor = Cursors.WaitCursor;

// Hier Aktionen ausführen 
------

// Cursor wieder zurück setzen
this.MdiParent.Cursor = oldCursor;

VS2005 :: Web Services Debuggen

Nachdem ich selbst das Problem am Anfang hatte und auch immer wieder von Freunden, Bekannten und Kollegen auf das Thema Debuggen von Web Services unter Visual Studion 2005 angesprochen werde, hier nun ein kleiner Blog Eintrag der beschreibt wie man dabei am besten vorgeht.

Hier zuerst noch einmal das Szenario:

Ich möchte einen Web Service erstellen und während der Entwicklung auf einfachem Wege in der Lage sein die einzelnen Web Methoden debuggen zu können.

Also einfach ein zweites Projekt (Windows Form) in der Projektmappe erstellt, referenz auf den Web Service in der Projektmappe hinzufügen, als Startprojekt festlegen, und fertig.

Anmerkung zum Szenario:

So sollte es gehen, leider geht es aber „noch“ nicht“

Und hier nun die Lösung:

Zuerst einmal genau so vorgehen wie es im Szenario beschrieben ist und dann folgende Einstellungen zusätzlich vornehmen:

In den Projektmappen Eigenschaften in den Allgemeinen Eigenschaften Startprojekt auswählen.

Dort die Option mehrere Startobjekte auswählen und bei den beiden Projekten (der Web Service und das Windows Forms Projekt) die Aktion auf Starten stellen.

Das ganze noch kurz mit OK bestätigen und fertig.

Wenn ich nun in der Windows Form eine Web Methode verwende, kann ich innerhalb der web methode einfach mit F9 einen Haltepunkt (Breakpoint) setzen.

Hoffe das die (schnelle und kurze) Beschreibung vielen Helfen wird.

Windows Service – OnStart mal etwas anders !

Nachdem ich in einem Projekt mit dem Problem kämpfen musste, das einer der für dieses Projekt entwickelten Windows Dienste durch eine aufwendige Initialisierung bis zu einer Minute in der OnStart Methode verweilt hat bis er endlich den Dienst als gestartet anzeigt habe ich mir überlegt wie man diese Problem anders lösen könnte.

Eigentlich war das auch kein wirklich großes Problem, aber da ich auch erst einmal gezielt darüber Nachdenken musste um auf die Lösung zu kommen, denke ich dass vielleicht auch andere erst einmal auf das Problem aufmerksam werden müssen um dann einen anderen Ansatz zum Start der Windows Dienste zu verwenden.

Im Nachfolgenden Beschreibe ich eine einfach Lösung wie man einen Windows Dienst mit Hilfe eines Timers dazu bringt, sofort zu starten, unabhängig davon, ob aufwendige Initialisierungsprozesse für den Dienst durchgeführt werden müssen oder nicht.

Schauen wir und doch einfach mal wie der normale Startvorgang eines Dienstes aussieht:

Hier ein Beispiel wie es normalerweise aussehen kann, wobei osc irgendeine Klasse ist deren Methoden nach deren Initialisierung vom Service verwendet werden:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.ServiceProcess;

using System.Text;

using System.Timers; 
 


namespace Namespace.Service

{

      publicpartialclassMeinService : ServiceBase

      {

            MeineKlasse osc;

            public MeinService()

            {

                  InitializeComponent();

                  Osc = new Osc();

            } 


            protectedoverridevoid OnStart(string[] args)

            {

                  Osc.Init();

                  Osc.StartAll();

            } 


            protectedoverridevoid OnStop()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnContinue()

            {

                  osc.StartAll();

            } 


            protectedoverridevoid OnPause()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnShutdown()

            {

                  osc.StopAll();

                  osc.Dispose();

                  osc = null; 


            }

      } 

Wenn nun davon ausgehen dass Osc.Init() und Osc.StartAll() Methoden sind die lange dauern können, dann befindet sich dieser Dienst in der Gesamt Zeit in welcher diese beiden Methoden ausgeführt werden im Dienst- Status StartPending. Dies kann in bestimmten Situationen aber mehr als unerwünscht sein. Und für diesen Fall habe ich folgende Änderungen vorgenommen, die dazu führen, dass der Service sofort nach dem Start wirklich in den Status Running übergeht.

Nachfolgend nun die Lösung für das Problem:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.ServiceProcess;

using System.Text;

using System.Timers ; 
 


namespace Namespace.Service

{

      publicpartialclassMeinService : ServiceBase

      {

            Timer startTimer = newTimer();

            MeineKlasse osc;

            public MeinService()

            {

                  InitializeComponent();

                  startTimer.Interval = 1000;        // 1 seconds 

                  startTimer.Elapsed += newElapsedEventHandler(startTimer_Elapsed);

                  startTimer.Enabled = false;

            } 


            void startTimer_Elapsed(object sender, ElapsedEventArgs e)

            {

                  startTimer.Enabled = false;

                  osc = MeineKlasse.loadActiveSystems();

                  osc.StartAll();

            } 


            protectedoverridevoid OnStart(string[] args)

            {

                  startTimer.Enabled = true;

            } 


            protectedoverridevoid OnStop()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnContinue()

            {

                  osc.StartAll();

            } 


            protectedoverridevoid OnPause()

            {

                  osc.StopAll();

            } 


            protectedoverridevoid OnShutdown()

            {

                  osc.StopAll();

                  osc.Dispose();

                  osc = null; 


            }

      }

} 

Ich habe die Änderungen in Fettschrift dargestellt damit man diese gleich auf Anhieb erkennen kann.
Und die Erklärung ist eigentlich auch selbstredend.
Ich verwende also einen Timer, der vom OnStart Event eine Sekunde nach dem der Service gestartet wurde die eigentlichen Funktionen des Service aktiviert.

Manchmal ist einfach, einfach Einfach !

DNN User Controls Programmierung VB versus C#

Bei der Programmierung von DotNetNuke Modulen gibt es ja wie allseits bekannt die freie Wahl der .NET Programmiersprachen.

Die beiden beliebtesten Sprachen zur Programmierung von DotNetNuke Modulen sind wohl VB.NET und C#.

Bei der Programmierung von User Controls sind jedoch obwohl beides .NET Programmiersprachen sind die Sprachspezifischen Eigenschaften wie zum Beispiel der Unterschied zwischen Groß und Kleinschreibung bei C# im Gegensatz zu VB.NET zu berücksichtigen. Das dies aber nicht die einzigen Unterschiede sind wird spätestens dann ganz deutlich wenn man versucht ein in VB.NET erstelltes User Control als Vorlage für ein C# User Control zu verwenden.

Schauen wir uns das doch einmal am Beispiel des Moduls Event, das ja Bestandteil des Core Produktes DotNetNuke ist, einmal etwas näher an.

Hier der Auszug aus der Events.ascx (mit VB.NET Codebehind)

<TR>

<TD id=colIcon vAlign=top align=center width='<%# DataBinder.Eval(Container.DataItem,"MaxWidth") %>' rowSpan=3 runat="server">

<asp:Image id=imgIcon runat="server" Visible='<%# FormatImage(DataBinder.Eval(Container.DataItem,"IconFile")) <> "" %>'

ImageUrl='<%# FormatImage(DataBinder.Eval(Container.DataItem,"IconFile")) %>' AlternateText='<%# DataBinder.Eval(Container.DataItem,"AltText") %>'>

</asp:Image></TD>

<TD>

<asp:HyperLink id=editLink runat="server" Visible="<%# IsEditable %>" NavigateUrl='<%# EditURL("ItemID",DataBinder.Eval(Container.DataItem,"ItemID")) %>'>

<asp:Image id="editLinkImage" ImageUrl="~/images/edit.gif" Visible="<%# IsEditable %>" AlternateText="Edit" runat="server" resourcekey="Edit"/>

</asp:HyperLink>

<asp:Label id=lblTitle text='<%# DataBinder.Eval(Container.DataItem,"Title") %>' Cssclass="SubHead" Runat="server">

</asp:Label></TD>

</TR>

Unsere besondere Aufmerksamkeit müssen wir dabei vor allem auf die Funktionen DataBinder.Eval() und EditURL() lenken.

Schauen wir uns Diese Syntax am folgenden Beispiel etwas näher an:

Visible='<%# FormatImage(DataBinder.Eval(Container.DataItem,"IconFile")) <> "" %>'

Mit dieser Zeile wird (in VB.NET ) erreicht dass nur wenn ein IconFile vorhanden ist das Control sichtbar ist, sonst wird der Wert Visible auf false gesetzt

Wenn wir eine solche Zeile in einem User Control einsetzen, welches mit C# zusammenarbeiten soll, dann ergeben sich gleich mehrere Problem (Fehlermeldungen während der Programmausführung, nicht beim übersetzen)

Als erstes wird in C# der Ausdruck nicht als If Ausdruck ausgewertet, da er nicht in () eingeschlossen ist.

Also muss das ganze auf jeden Fall schon mal so aussehen:

Visible='<%# (FormatImage(DataBinder.Eval(Container.DataItem,"IconFile")) <> "") %>'

Zu beachten hierbei sind die Klammern am Anfang und Ende der Auswertung

(FormatImage(DataBinder.Eval(Container.DataItem,"IconFile")) <> "")

Jetzt würden aber noch immer Laufzeitfehler entstehen, da C# bei der Typ Auswertung wesentlich kleinlicher (Gott sei Dank) ist, als dies VB.NET ist.

Das nächste Problem liegt in der Behandlung eines Stringvergleiches. in VB.NET ist der Vergleich string1 <> string2 ein gültiger Vergleich. In C# nicht, also müssen wir einen gültigen Vergleich für C# nehmen, der sieht dann so aus: string1 != string2.

Danach muss unser Ausdruck wie folgt aussehen:

Visible='<%# (FormatImage(DataBinder.Eval(Container.DataItem,"IconFile")) !="") %>'

Wenn Sie nun Denken, dass es nun funktioniert, muss ich Sie leider enttäuschen.

DataBinder.Eval() gibt als Datentyp ein abstraktes Objekt zurück, nun versuchen Sie mal einen Vergleich eines Objekt Datentyps mit „“ einem String, das wird C# nicht zulassen.

Also bleibt uns nichts weiter übrig, als das Objekt zu einem String umzuwandeln.

Die Umwandlung können wir wie folgt vornehmen:

DataBinder.Eval(Container.DataItem,"IconFile").ToString()

Somit sieht unser Ausdruck nun wie folgt aus:

Visible='<%# (FormatImage(DataBinder.Eval(Container.DataItem,"IconFile").ToString()) !="") %>'

Der nun hier dargestellte Ausdruck ist nun C# konform und wird keinen Laufzeitfehler mehr erzeugen.

Schauen wir uns nun noch ein zweites Problem unseres Beispiels an:

<asp:HyperLink id=editLink runat="server" Visible="<%# IsEditable %>" NavigateUrl='<%# EditURL("ItemID",DataBinder.Eval(Container.DataItem,"ItemID")) %>'>

Der hier dargestellte Ausdruck ist der typische Code für den Hyperlink, der aus dem Anzeige Control das Edit Control aufruft.

Der nicht C# konforme Teil dieses Ausdrucks ist nachfolgend dargestellt:

NavigateUrl='<%# EditURL("ItemID",DataBinder.Eval(Container.DataItem,"ItemID")) %>'

Wenn wir das bisherige aus diesem Artikel anwenden, dann wissen wir dass wir das aus dem DataBinder.Eval() zurückgegeben Objekt in einem String umwandeln müssen.

So das unser Ausdruck wie folgt aussieht:

NavigateUrl='<%# EditURL("ItemID",DataBinder.Eval(Container.DataItem,"ItemID").ToString()) %>'

Wie ich am Anfang schon bemerkt habe berücksichtigt C# im Gegenteil zu VB.NET die Groß und Kleinschreibung, und somit haben wir mit dem Aufruf der Funktion EditURL ein Problem, da diese in Wirklichkeit eigentlich EditUrl heißt.

Also ändern wir den Ausdruck wie folgt ab:

NavigateUrl='<%# EditUrl("ItemID",DataBinder.Eval(Container.DataItem,"ItemID").ToString()) %>'

Mit dem Wissen dieser notwendigen Änderungen wenn Sie ein User Control in C# verwenden möchten, was eigentlich für VB.NET konzipiert war, können Sie nun die notwendigen Änderungen in User Controls vornehmen so dass diese dann in einem C# DotNetNuke Module verwendet werden können.

Spider Friendly URL – ASP.NET URL Rewriting

Hintergrund:

Dynamisch erstellte Web Seiten haben häufig mit Parameter gespickte URL’s. Manche Suchmaschinen können diese Parameter behafteten URL’s nicht oder nur schlecht auswerten.

Beispiele von URL’s:

Hier eine typische URL einer DotNetNuke Webseite:

http://www.dnnportal.de/default.aspx&tabid=179&type=art&site=40&parentid=48

Mit dem hier besprochenen Ansatz könnte die URL wie folgt aussehen http://www.dnnportal.de/Default.aspx/type/art/site/40/tabid/179/parentid/48

Mir ist auch schon aufgefallen das Webseiten von exakt gleichem Inhalt mit URL’s ohne Parameter häufig weiter oben in den Suchmaschinen angesiedelt sind, als die gleiche Seite mit parametrisierten URL’s.

Leider ist es nicht ohne weiteres Möglich diese Art von SFU (Spider Friendly URL’s) aus einem Programm welches nicht von vornherein dafür vorgesehen ist zu erzeugen. Und wenn, dann doch meist, nur mit vielen Kompromissen und in vielen Fällen auch nur mit Änderungen im Quellcode.

Die Aufgabenstellung lautet also eine Möglichkeit zu schaffen, SFU URL’s ohne oder mit nur geringen Änderungen des Quellcode’s anwendungsdurchgängig zu implementieren.

Die Idee

Die Idee zur Erstellung eines Programms welches Suchmaschinen freundliche URL’s (Spider Friendly URL’s) erstellt ist im Zusammenhang mit der Suchmaschinenoptimierung für DotNetNuke Portale entstanden.

Nachdem ich mir viele Ansätze für solch eine Lösung im Internet angesehen hatte und mit den Ergebnissen nicht zufrieden war (Es waren meist irgendwie keine durchgängigen Lösungen), bin ich auf einen Beitrag von Scott Van Vliet gestoßen, der mir dann als Basis für die Erstellung dieser Lösung gedient hat.

Der Beitrag von Scott Van Vliet basiert im groben darauf dass der vom Webserver an den Client gesendete Response gefiltert wird und bei dieser Filterung die dynamischen URL’s in statische URL’s ausgetauscht werden Und das bei einer Anforderung von einem Client die an den Server gesendete Statische URL wieder für die Internet Verwendung in die ursprüngliche Dynamische URL umgesetzt wird.

Wie Sie sich Denken können ist dies ein vorhaben, dass seine Tücken haben soll.

Aber ich kann Ihnen schon hier verraten, es funktioniert.

Eine fertige Lösung. welche die Konzepte dieses Beitrags umsetzt können Sie auf DNNPortal im Download Bereich herunterladen.

Für registrierte Benutzer liegt auch der komplette Source Code als Download bereit.

Ich habe dann auf Basis dieses Lösungsansatzes dieses Modul HPS.Utilities.SFU erstellt.

Grundsätzliche Lösung

In diesem Kapitel möchte ich den Grundsätzlichen Weg beschreiben, welcher für diese Lösung beschritten wird.

Normale Webanforderung

Schauen wir uns doch zuerst einmal an wie der normale Aufruf einer Web Seite funktioniert.

graphic

Der Client sendet eine Request an den Server. Der Request besteht aus einer angeforderten URL. Nehmen wir als Beispiel die folgende URL:

http://www.dnnportal.de/default.aspx&tabid=163.

In diesem Fall bedeutet das, der Client fordert die Homepage der Web Anwendung www.dnnportal.de an.

Der Server empfängt nun diese Anforderung, die Webanwendung (wir sprechen ja über ASP.NET) löst die Parameter auf und schickt als Response die gewünschte Web Seite als Stream zum Browser.

Soweit zur normalen Anforderung einer Webseite eines Client vom Server.

Ansatzpunkte zur Umsetzung

Betrachten wir uns das ganze mal etwas näher was wir erreichen wollen:

graphic

Gewünscht wäre, der Client Request lautet http://www.dnnportal.de/default.aspx/tabid/163, den Request den der Server bekommt sollte aber http://www.dnnportal.de/default.aspx?tabid=163 lauten.

In diesem Fall würde der Server den Request des Client verstehen und im den Response (also die gewünschte Webseite) an ihn zurücksenden.

Schauen wir uns doch mal an was der Server an den Client sendet.

Es sendet ihm den Inhalt der Webseite http://www.dnnportal.de/default.aspx?tabid=163 in diesem Inhalt (der Content) können sich Links auf andere Seiten oder auf Java Skript oder sonstiges mit meist absoluten Pfadangaben enthalten, die immer auf das Wurzelverzeichnis der aktuellen Web Anwendung (also unserer Dynamischen Web Anwendung) verweist.

Somit erhält der Client Links die im Format http://www.dnnportal.de/default.aspx?tabid=163 gehalten sind.

Eigentlich wollen wir ja aber das der Client die URL Informationen im Format http://www.dnnportal.de/default.aspx/tabid/163 erhält.

Wenn wir uns die obige Abbildung anschauen dann ist dort bereits der Ansatz der Lösung zu erkennen.

Wenn wir die beiden Rechtecke Response vom Server und Request vom Client nicht als Beschreibung sondern als die Möglichkeit sehen, dort die ein und ausgehenden Stream (Anforderungen und Antworten) so zu manipulieren, dass Sie unser gewünschte Resultat ergeben.

Der Oberbegriff für unsere Lösung heißt also HTTPHandler.

Wir erzeugen also ein Klassenmodul welches als HTTPHandler später über die Web.Config aktiviert wird.

Also erzeugen wir eine Klasse (Diese Klasse muss von der Klasse System.Web.IHttpModule abgeleitet sein.):

public class SfuHttpModule : System.Web.IHttpModule
{

}
Request (Eingehende Streams)

Um den eingehenden Stream abzufangen und zu ändern bevor wir Ihn an den Webserver weiterleiten gibt es die Möglichkeit einen Event der ausgelöst wird wenn eine Anforderung zum Server geschickt wird auf eine eigene Event Methode umzulenken.

Dies geschieht auf folgende Art und Weise.

Im Init Event unseres HTTPHandler weisen wir einen neue Event Methode zu:

public void Init(HttpApplication application)
{
     application.BeginRequest +=new EventHandler(Application_BeginRequest);
}

Das bedeutet immer wenn ein Request an den Server gesendet wird, wird durch die Event Methode Application_Begin_Request unseres HTTPHandler aufgerufen.

An dieser Stelle behandle ich die dort durchgeführten Aktionen rein generisch, nähere Erläuterungen was in der Methode genau geschieht folgt später in diesem Artikel.

public void Application_BeginRequest(object sender, EventArgs e)
{
     // Hier wird jetzt eine Funktion eingefügt, welche die eingehende statische URL wieder               //in die ursprüngliche dynamische URL umwandelt.
}
Response (Ausgehende Streams)

Um den Inhalt der an den Client übertragen wird zu manipulieren, ist es notwendig den vom Server ausgehenden Stream abzufangen und zu manipulieren.

Um es gleich vorweg zu sagen, dies ist die wesentlich größere Herausforderung. Hierbei handelt es sich ja nicht nur um eine URL die abgefangen und manipuliert werden muss, sondern um den gesamten Inhalt der Webseite die vom Server an den Client übertragen wird.

Aber das es dafür Lösungsmöglichkeiten gibt sehen wir ja in diesem Artikel.

Wir gehen also wie folgt vor und registrieren einen weiteren Event (hier fett dargestellt) in unserer Init Methode des HTTPModules:

public void Init(HttpApplication application)
{
     application.BeginRequest +=new EventHandler Application_BeginRequest);

     application.PostRequestHandlerExecute += new EventHandler(application_PostRequestHandlerExecute);
}

Das hinzufügen des Events application_PostRequestHandlerExecute hat zur Folge dass jedesmal wenn der Server einen Response an eine Client sendet unsere Event Methode aufgerufen wird bevor der Client die Daten des Response erhält.

An dieser Stelle auch nur die generische Beschreibung was in dieser Methode geschieht.

private void application_PostRequestHandlerExecute(object sender, EventArgs e)
{
// Daten vom Server empfangen und manipulierte Daten dann zum Client senden
}

Dieses beschriebene Ansinnen ist etwas umfangreicher als es hier dargestellt ist und wird später näher erläutert.

Zusammenfassung

Wenn wir also die eingehenden und ausgehenden Streams abfangen und manipulieren können, sollte es möglich sein, die gewünschten Anforderungen zu erfüllen.

Im nächsten Abschnitt werden wir auf die einzelnen Funktionen die wir implementieren müssen näher eingehen.

HTTPHandler im Detail

Nun wird es ernst, in diesem Abschnitt werden nun die einzelnen Funktionen beschrieben die notwendig sind um die ein und ausgehenden Streams abzufangen und zu manipulieren.

Beginnen wir mit dem einfacheren Teil.

Beginn_Request

Schauen wir uns zuerst einmal an, wie die Event Methode nach unserer Implementierung aussieht und welche Funktionalitäten darin versteckt sind:

public voidApplication_BeginRequest(objectsender, EventArgs e)
{
       HttpContext context = ((HttpApplication)sender).Context;
       context.RewritePath(SfuUtil.FromSfuUrl(context.Request.Path) );
}

In einfachen Worten beschrieben, wird in der Methode die Funktion context.RewritePath() aufgerufen um die eingehende URL im statischen Format durch die ursprüngliche dynamische URL umzuschreiben, so dass der Server uns die zu dieser URL gehörigen Informationen zurückliefern kann.

Wir verwenden hierzu die Funktion FromSfuUrl der Klasse SfuUtil. Diese Funktion der Klasse wandelt einfach die statische in die dynamische URL um.

Um diesen Bericht nicht unnötig aufzublasen möchte ich keine detaillierte Erläuterung der Klasse SfuUtil vornehmen. Die Klasse ist im Source Code enthalten und bei Fragen können diese gerne per Email an mich gesendet werden.

PostRequestHandlerExecute

Nun zum Interessanteren Teil der Lösung.

private void application_PostRequestHandlerExecute(object sender, EventArgs e)
{
     HttpApplication application = (HttpApplication)sender;
     string _querystring = application.Context.Request.QueryString.ToString();
     application.Context.Response.Filter = new RequestFilter(application.Context.Response.Filter, application.User);
}

Wie sie sehen wird im PostRequestHandlerExecute ein weiterer Event registriert. Ein Context.Response.Filter. Dieser Event wird immer dann ausgelöst wenn der Server Daten zum Client senden möchte.

Das was nun folgt ist der eigentliche Höhepunkt dieser Anwendung.

RequestFilter

Diese Klasse enthält nun die Funktionalität den ausgehende Stream vom Client zum Server abzufangen und zu manipulieren. Aber sehen Sie selbst:

Die Klasse muss von Stream abgeleitet werden, diese wiederum erfordert dass eine Anzahl von Methoden überschrieben werden müssen, da sonst die Implementierung der Klasse nicht vorgenommen werden kann.

Erforderliche Überschreibungen:

Nachfolgende werden die Methoden aufgeführt die zwingend Überschrieben werden müssen, damit unsere Klasse von der Stream Klasse abgeleitet werden kann.

public override bool CanRead
{

     get

     {

          return true;

     }

}

public override bool CanSeek
{

     get

     {

          return true;

     }

}

public override bool CanWrite
{

     get

     {

          return true;

     }

}

public override long Length
{

     get

     {

          return 0;

     }

}

public override long Position
{

     get

     {

          return _position;

     }

     set

     {

          _position = value;

     }

}

public override long Seek(long offset, SeekOrigin origin)
{

     return _sink.Seek(offset,origin);

}

public override void SetLength(long value)
{

     _sink.SetLength(value);

}

public override void Close()
{

     _sink.Close ();

}

public override void Flush()
{

     _sink.Flush();

}

public override int Read(byte[] buffer, int offset, int count)
{

     return _sink.Read(buffer, offset, count);

}
Write Methode (Hier ist das Herzstück unseres Content Filter)
public override void Write(byte[] buffer, int offset, int count)

{

     string sBuffer = Encoding.Default.GetString(buffer, offset, count);

     sBuffer = _tempBuffer + sBuffer.Trim();

     if (buffer.Length != count)         

     {

          int idx = sBuffer.LastIndexOf(">");

          _tempBuffer = sBuffer.Substring(idx + 1);

          sBuffer = sBuffer.Substring(0,idx+1);

     }

     MatchCollection hrefMatches = Regex.Matches(sBuffer, RegexPattern.HrefPattern, RegexOptions.IgnoreCase);

     HttpContext Context = HttpContext.Current;

     if ((hrefMatches.Count > 0))

     {

          try

          {

               foreach (Match match in hrefMatches)

               {

                    string href = match.Groups[match.Groups.Count - 2].Value;

                    if (href.IndexOf(Context.Request.Headers["Host"]) > 0)

                         href = href.Substring(href.IndexOf(Context.Request.Headers["Host"])+ Context.Request.Headers["Host"].Length );

                    if (Regex.IsMatch(href, RegexPattern.AspxPattern) &&

                         !Regex.IsMatch(match.Value, RegexPattern.ImgPattern) &&

                         !Regex.IsMatch(match.Value, RegexPattern.CssPattern) &&

                         !Regex.IsMatch(match.Value, RegexPattern.ScriptPattern))

                    {

                         href = href.Replace(href, SfuUtil.ToSfuUrl(href));

                    }

                    if (!Regex.IsMatch(href, RegexPattern.HttpProtocolPattern) &&

                         !Regex.IsMatch(href, RegexPattern.MailToPattern,RegexOptions.IgnoreCase) &&

                         !Regex.IsMatch(href, RegexPattern.AnchorPattern) &&

                         !Regex.IsMatch(href, RegexPattern.JavascriptHtmlStatementPattern))

                    {

                         if (!Regex.IsMatch(href, RegexPattern.AbsolutePathPattern))

                         {

                              href = Regex.Match(Context.Request.Path, RegexPattern.CurrentPathPattern).Groups[1].Value + href;

                         }

                         sBuffer = sBuffer.Replace(match.Value, match.Value.Replace(match.Groups[match.Groups.Count - 2].Value, href));

                    }

               }

          }

          catch (Exception ex)

          {

               System.Diagnostics.Debug.WriteLine(ex.Message);

          }

     }

     byte[] bufferNew = Encoding.Default.GetBytes(sBuffer);

     _sink.Write(bufferNew, 0, bufferNew.Length);

}

Nachdem wir den HTTPHandler erzeugt und die Events wie in diesem Artikel beschrieben haben registriert haben, wird die Methode Write immer dann ausgeführt wenn der Server etwas an den Client senden möchte. Wir müssen in dieser Methode jetzt selbst dafür sorgen dass der Client eine Antwort von unserem Server erhält. Wenn wir an dieser Stelle nichts senden, bekommt der Client keinen Response.

Nachdem ich mit dieser Methode gearbeitet habe sind mir fast unbegrenzte Möglichkeiten für den Einsatz eingefallen, aber bleiben wir nun erst einmal bei unserem Thema und schauen uns die Methode näher an.

public override void Write(byte[] buffer, int offset, int count)

Die Methode erhält als Parameter im Parameter buffer den Response der vom Server auf den Request des Client zurück gesendet werden soll.

Diesen Buffer können wir nun lesen, manipulieren und anschließend an den Client als Server Response senden (Der Client bekommt davon nichts mit)

Kommen wir aber gleich zu einem Problem, was wenn nicht gelöst zu einem echten Problem werden kann.

Der Server sendet maximal 25 KByte (diese Zahl ist nicht genau, konnte keine genaue Definition finden) auf einmal an den Client. Das bedeutet wenn größere Webseiten Inhalten an den Client gesendet werden, so wird diese Methode mehrfach aufgerufen und jeweils ein Teil des Webseite an den Client gesendet. Dies kann aber in unserem Fall (den wir später noch näher erläutern) des Stringvergleiches dazu führen, dass wir nur einen Teil des gesamten String während eines Aufrufs der Methode Write enthalten haben.

Um dies zu berücksichtigen habe ich folgenden Code eingebaut:

int idx = sBuffer.LastIndexOf(">");
_tempBuffer = sBuffer.Substring(idx + 1);
sBuffer = sBuffer.Substring(0,idx+1);

Da der Filter dazu verwendet wird HTML Seiten an den Client zu übertragen suche ich einfach das letzte vorkommen eines abschließenden Tags.

Kopiere alles einschließlich des letzten Tags in die zu verarbeitende Puffervariable sBuffer. Alles was hinter dem letzten abschließenden Tag ist kopiere ich in einen temporären Zwischenpuffer _tempBuffer.

Dieser wird beim nächsten Aufruf an den Anfang von sBuffer kopiert und anschließen geleert.

Hierdurch wird sichergestellt dass bei den Stringvergleichen immer ein ganzer in einem Tag eingeschlossener String zur Verfügung steht und nicht nur ein Teil eines href oder ähnlichem im sBuffer verarbeitet wird.

Den Teil des Stringvergleiches werde ich an dieser Stelle auch nicht ausführlich beschreiben, es sei nur soviel das gesagt, dass alle vorkommen von href gesucht werden und mit der Funktion ToSfu der Klasse SfuUtil von dynamischen URL in statischen URL umgewandelt werden. Außerdem werden Tags wie src, img, java scripte etc im sBuffer gesucht und anstelle von relativen angaben wie Image\logo.gif gegen absolute angaben wie http://www.dnnportal.de/imager/logo.gif ausgetauscht.

Zum Schluss werden die Daten des sBuffer als Stream zum Client gesendet.

Klasse in der Übersicht

public class RequestFilter : Stream
{
    private Stream _sink;
    protected IPrincipal _user;
    private long _position;
    bool openTag, endTag;
    string _tempBuffer;
    public RequestFilter(Stream sink, IPrincipal user)
    {
        _sink = sink;
        _user = user;
        openTag = false;
        endTag = false;
        _tempBuffer = String.Empty;
    }
    public override bool CanRead
    {
        get
        {
            return true;
        }
    }
    public override bool CanSeek
    {
        get
        {
            return true;
        }
    }
    public override bool CanWrite
    {
        get
        {
            return true;
        }
    }
    public override long Length
    {
        get
        {
            return 0;
        }
    }
    public override long Position
    {
        get
        {
            return _position;
        }
        set
        {
            _position = value;
        }
    }
    public override long Seek(long offset, SeekOrigin origin)
    {
        return _sink.Seek(offset, origin);
    }
    public override void SetLength(long value)
    {
        _sink.SetLength(value);
    }
    public override void Close()
    {
        _sink.Close();
    }
    public override void Flush()
    {
        _sink.Flush();
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        return _sink.Read(buffer, offset, count);
    }
    public override void Write(byte[] buffer, int offset, int count)
    {
        string sBuffer = Encoding.Default.GetString(buffer, offset, count);
        sBuffer = _tempBuffer + sBuffer.Trim();
        if (buffer.Length != count)
        {
            int idx = sBuffer.LastIndexOf(">");
            _tempBuffer = sBuffer.Substring(idx + 1);
            sBuffer = sBuffer.Substring(0, idx + 1);
        }
        MatchCollection hrefMatches = Regex.Matches(sBuffer, RegexPattern.HrefPattern, RegexOptions.IgnoreCase);
        HttpContext Context = HttpContext.Current;
        if ((hrefMatches.Count > 0))
        {
            try
            {
                foreach (Match match in hrefMatches)
                {
                    string href = match.Groups[match.Groups.Count - 2].Value;
                    if (href.IndexOf(Context.Request.Headers["Host"]) > 0)
                        href = href.Substring(href.IndexOf(Context.Request.Headers["Host"]) + Context.Request.Headers["Host"].Length);
                    if (Regex.IsMatch(href, RegexPattern.AspxPattern) &&
                    !Regex.IsMatch(match.Value, RegexPattern.ImgPattern) &&
                    !Regex.IsMatch(match.Value, RegexPattern.CssPattern) &&
                    !Regex.IsMatch(match.Value, RegexPattern.ScriptPattern))
                    {
                        href = href.Replace(href, SfuUtil.ToSfuUrl(href));
                    }
                    if (!Regex.IsMatch(href, RegexPattern.HttpProtocolPattern) &&
                    !Regex.IsMatch(href, RegexPattern.MailToPattern, RegexOptions.IgnoreCase) &&
                    !Regex.IsMatch(href, RegexPattern.AnchorPattern) &&
                    !Regex.IsMatch(href, RegexPattern.JavascriptHtmlStatementPattern))
                    {
                        if (!Regex.IsMatch(href, RegexPattern.AbsolutePathPattern))
                        {
                            href = Regex.Match(Context.Request.Path, RegexPattern.CurrentPathPattern).Groups[1].Value + href;
                        }
                        sBuffer = sBuffer.Replace(match.Value, match.Value.Replace(match.Groups[match.Groups.Count - 2].Value, href));
                    }
                }
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }
        byte[] bufferNew = Encoding.Default.GetBytes(sBuffer);
        _sink.Write(bufferNew, 0, bufferNew.Length);
    }
}
Schlussbemerkungen

Die in diesem Artikel beschriebenen Klassen sind nicht vollständig und somit nicht lauffähig.

Außerdem gibt es bei dieser Verarbeitung noch einige andere Aspekte die gesondert betrachtet werden müssen.

Einen kompletten lauffähigen HTTPHandler der genau auf diesem Artikel basiert kann hier herunterladen.

Für registriert Mitglieder steht hier auch der Quellcode zum Download zur Verfügung.

Für Anregungen, Kritik oder Verbesserungsvorschläge bitte einfach Kommentare hinterlassen.

Der Autor: Hans-Peter Schelian

Enum Werte aus Konfigurationsdatei auslesen und in Enum Typ umwandeln

Um in einer App.config Datei Enum Werte wieder in eine Variable des Augzählungstypes einzulesen kann folgende Funktion verwendet werden.

Enum.Parse()

Beispiel:

In diesem Beispiel lesen wir den NotifyFilter eines FileSystemWatcher ein:

Der Key in der App.config sieht wie folgt aus:

<add key="fdwNotiFyFilter" value="FileName, DirectoryName, LastWrite" />

Und hier die Vewendung:

fdw.NotifyFilter = (NotifyFilters)Enum.Parse(typeof(NotifyFilters), ConfigurationSettings.AppSettings["NotifyFilter"]);