Auf der MSDN-Website wurde in 2003-05 ein Add-In vorgestellt, das das bekannte HTML Tidy in Visual Studio .NET integriert. Der Nutzen dieses Ansatzes hält sich meiner Meinung nach in Grenzen, zumal das Add-In nur mit ASCII-Zeichen sicher umgehen kann und Tidy von Web-Controls nicht den „fertigen“ HTML-Code zu sehen bekommt.
Wesentlich sinnvoller erscheint es mir, Tidy den HTML-Code bearbeiten zu lassen, den ein Browser sehen würde. Das kann ein Filter leisten. Insbesondere kann Tidy HTML in XHTML konvertieren.
Filter sind im .NET-Framework von der Klasse
System.IO.Stream
abgeleitet. Meine Klasse
TidyFilter
implementiert einen Filter, der einen Byte-Strom an
Tidy weiterreicht:
Option Strict On
Option Explicit On
Imports System.IO
Public Class TidyFilter : Inherits Stream
Const TIDY_EXE As String = "tidy.exe"
Const TIDY_CFG As String = "tidy.cfg"
Private _sink As Stream
Private _position As Long
Private mstr As New MemoryStream
#Region "trivial"
Public Sub New(ByVal sink As Stream)
_sink = sink
End Sub
Public Overrides ReadOnly Property CanRead() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanSeek() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property CanWrite() As Boolean
Get
Return True
End Get
End Property
Public Overrides ReadOnly Property Length() As Long
Get
Return 0
End Get
End Property
Public Overrides Property Position() As Long
Get
Return _position
End Get
Set(ByVal Value As Long)
_position = Value
End Set
End Property
Public Overrides Function Seek(ByVal offset As Long, ByVal direction As System.IO.SeekOrigin) As Long
Return _sink.Seek(offset, direction)
End Function
Public Overrides Sub SetLength(ByVal length As Long)
_sink.SetLength(length)
End Sub
Public Overrides Sub Flush()
_sink.Flush()
End Sub
Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
_sink.Read(buffer, offset, count)
End Function
#End Region
Public Overrides Sub Close()
Dim path As String = System.Web.HttpContext.Current.Request.PhysicalApplicationPath
Try
'Prepare process.
Dim psi As New System.Diagnostics.ProcessStartInfo
psi.FileName = path & TIDY_EXE
psi.Arguments = "-config " & path & TIDY_CFG
psi.WorkingDirectory = path
psi.CreateNoWindow = True
psi.UseShellExecute = False
psi.RedirectStandardInput = True
psi.RedirectStandardOutput = True
psi.RedirectStandardError = False
'Create process.
Dim p As New System.Diagnostics.Process
p.StartInfo = psi
If p.Start() Then
'Feed Tidy.
mstr.WriteTo(p.StandardInput.BaseStream)
p.StandardInput.Close()
'Write Tidy output into the original response stream.
Const BUFFER_SIZE As Integer = 4096
Dim buffer(BUFFER_SIZE) As Byte
Dim count As Integer
Do
count = p.StandardOutput.BaseStream.Read(buffer, 0, BUFFER_SIZE)
_sink.Write(buffer, 0, count)
Loop Until count = 0
p.StandardOutput.Close()
Else
Throw New Exception
End If
Catch ex As Exception
'Something went wrong, output original source.
mstr.WriteTo(_sink)
Finally
_sink.Close()
End Try
End Sub
Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
mstr.Write(buffer, offset, count)
End Sub
End Class
Sie benötigen eine ausführbare
.exe-Datei sowie eine Konfigurationsdatei
tidy.cfg
. Die Konfigurationsoptionen sind ausführlich
dokumentiert, meine eigene Konfigurationsdatei sieht derzeit so aus:
input-xml: no
output-xhtml: yes
doctype: strict
add-xml-decl: no
output-bom: no
char-encoding: utf8
markup: yes
wrap: 0
numeric-entities: yes
quote-nbsp: no
newline: CRLF
force-output: yes
tidy-mark: no
tidy.exe
muß in einem Verzeichnis abgelegt werden, das Ausführberechtigungen für ausführbare Dateien besitzt. Möglicherweise müssen Sie die Datei deshalb ins
cgi-bin
-Verzeichnis verschieben. Den Pfad zu den beiden Dateien müssen Sie relativ zum Stammverzeichnis Ihrer ASP.NET-Applikation angeben:
Const TIDY_EXE As String = "cgi-bin\tidy.exe"
Const TIDY_CFG As String = "tidy.cfg"
Ich verwende hier die „klassische“ .exe-Datei. Es gibt zwar einen einen COM-Wrapper für TidyLib, das COM-Objekt müßte jedoch auf dem Server registriert werden, was die Portabilität einschränken würde. Die Performance ist dennoch zufriedenstellend.
Erstellen Sie einfach eine neue Klasse und fügen Sie den obigen Quellcode ein.
vbc.exe
Der Quellcode wird mit folgendem Befehl kompiliert:
vbc.exe /target:library /r:System.dll,System.Web.dll /imports:System TidyFilter.vb
Dieser Aufruf erzeugt eine Datei
TidyFilter.dll
. Diese müssen Sie in das
\bin
-Verzeichnis Ihrer ASP.NET-Applikation kopieren. Um den Filter zu verwenden, müssen Sie die
Assembly in der .aspx-Datei referenzieren:
<%@ Assembly Name="TidyFilter" %>
Der Filter kann bspw. in der
OnInit
-Methode einer Seite aufgerufen werden:
Protected Overrides Sub OnInit(ByVal e As System.EventArgs)
Response.Filter = new TidyFilter(Response.Filter)
End Sub
Der Filter arbeitet mit Byte-Streams, nicht mit Strings. Sie müssen also dafür sorgen, daß ASP.NET die Zeichencodierung verwendet, die Tidy erwartet. Schreiben Sie bspw.
<globalization responseEncoding="utf-8" />
in der
web.config
und
char-encoding: utf8
in der
tidy.cfg
.
Tidy unterstützt neben UTF-8
einige andere Codierungen, aber es gibt eigentlich keinen Grund, nicht UTF-8 zu verwenden.