Email berichten met HTML én platte tekst versturen
Door Pieter Dubelaar
20 maart 2002
Wat zijn multipart e-mails?
E-mail kan in HTML vorm of platte tekst worden verstuurd. Wat velen niet weten is
dat al sinds 1992 het volgens de MIME (Multipurpose Internet Mail Extensions) standaard,
een standaard die door vrijwel alle mailprogramma's wordt ondersteund, mogelijk is e-mail
te versturen met HTML én platte tekst, en dat door de clientsoftware wordt bepaald
welk van deze vormen aan de gebruiker wordt getoond.
MIME definieert onder meer een header veld, dat het soort informatie aangeeft dat zich
in een mailbericht bevindt. Zo zal een bericht met een header veld Content-type: text/plain
een bericht bevatten in platte tekst, en Content-type: image/gif betekenen dat de inhoud
van het bericht een GIF-illustratie bevat. Ook kan een bericht opgesteld worden dat
meerdere van deze typen binnen één bericht bevat:
Content-type: multipart/alternative laat de clientsoftware weten dat de inhoud van het
bericht bestaat uit een aantal losstaande onderdelen, elk qua informatie gelijkwaardig.
De clientsoftware kan vervolgens bepalen welk van de aangeboden "alternatieven" aan de
gebruiker getoond wordt.
Hieronder volgt een stuk voorbeeldcode dat via de populaire mailcomponenten ASPMail
en ASPEmail een multipart/alternative emailbericht stuurt. Uitleg volgt er direct op:
<%
Dim ASPMailer, ASPEMailer
Dim ContentType, Boundary
Dim MailServer, FromName, FromAddress
Dim ToName, ToAddress, BodyText
Dim PlainText, HTMLText, mailer, message
MailServer = "smtp.uwdomein.nl"
FromName = "Naam afzender"
FromAddress = "afzender@uwdomein.nl"
ToName = "Naam ontvanger"
ToAddress = "ontvanger@uwdomein.nl"
Subject = "Een multipart/alternative test"
PlainText = "Dit deel van de email zal te zien zijn voor mensen " & _
"met een mailclient die geen HTML ondersteunt."
HTMLText = "<body bgcolor=""#ffff33""><font color=""#ff0000"">" &_
"Deze rode tekst met gele achtergrond</font> is te zien " &_
"voor mensen met een mailclient die wel <b>HTML</b> ondersteunt.</body>"
ContentType = "multipart/alternative"
Boundary = "boundary1234"
BodyText = _
"--" & Boundary & VbCrLf & _
"Content-Type: text/plain;" & vbCrLf & VbCrLf & _
PlainText & VbCrLf & VbCrLf & _
"--" & Boundary & VbCrLf & _
"Content-Type: text/html;" & vbCrLf & VbCrLf & _
HTMLText & VbCrLf & VbCrLf & _
"--" & Boundary & "--"
Set ASPEMailer = Server.CreateObject("Persits.MailSender")
ASPEMailer.Host = MailServer
ASPEMailer.From = FromAddress
ASPEMailer.FromName = FromName
ASPEMailer.AddAddress ToAddress, ToName
ASPEMailer.Subject = Subject & " via ASPEmail"
ASPEMailer.Body = BodyText
ASPEMailer.AddCustomHeader "Content-Type: " & ContentType &_
"; boundary=" & Boundary
On Error Resume Next
ASPEMailer.Send
If Err <> 0 Then
Response.Write "ASPEmail error: " & Err.Description & "<br><br>"
Response.Write "ASPEmail was successful<br><br>"
End If
On Error Goto 0
Set ASPEMailer = Nothing
Set ASPMailer = Server.CreateObject("SMTPsvg.Mailer")
ASPMailer.RemoteHost = MailServer
ASPMailer.FromAddress = FromAddress
ASPMailer.FromName = FromName
ASPMailer.AddRecipient ToName, ToAddress
ASPMailer.ContentType = ContentType & "; boundary=" & Boundary
ASPMailer.Subject = Subject & " via ASPMail"
ASPMailer.BodyText = BodyText
ASPMailer.WordWrap = true
If Not ASPMailer.SendMail Then
Response.Write "ASPMail error: " & ASPMailer.Response &_ "<br><br>"
Else
Response.Write "ASPMail was successful<br><br>"
End If
Set ASPMailer = Nothing
%>
De truc achter de Multipart e-mail berichten zit in de zogenaamde boundary. Deze willekeurige string
karakters dient als scheiding tussen de diverse delen van het mail bericht. Als voorbeeld, bovenstaande
code resulteert in twee identieke e-mail berichten zoals hieronder getoond.
< ...diverse mailheaders zoals subject, to & from etc.>
--boundary1234
Content-Type: text/plain;
Dit deel van de email zal te zien zijn voor mensen met een mailclient
die geen HTML ondersteunt.
--boundary1234
Content-Type: text/html;
<body bgcolor="#ffff33"><font color="#ff0000">Deze rode tekst met gele
achtergrond</font> is te zien voor mensen met een mailclient die wel
<b>HTML</b> ondersteunt.</body>
--boundary1234--
Hierin zijn twee delen te zien, beide delen gescheiden door de -—boundary1234 boundaries.
Het eerste deel begint met de header Content-Type: text/plain;, wat beschrijft dat de rest van
dit deel platte tekst bevat. Het tweede deel, beginnend met Content-Type: text/html;, bevat het bericht in HTML.
De inhoud van de variabele BodyText bevat een groot aantal keer vbCrLf ingevoegd. Dit is een constante (in VBScript) die staat voor
Carriage Return + Line Feed, en waardoor er dus een nieuwe regel wordt ingevoegd. Je voegt vbCrLf dus toe als je een nieuwe regel wil invoegen.
Om de boundaries goed te kunnen onderscheiden wil een mailclient dat elke boundary zijn eigen regel krijgt, direct gevolgd door een Content-Type header
(op een eigen regel), en vervolgens een witregel om aan te geven dat de headers afgelopen zijn en de inhoud van het bericht begint. Een nieuw deel wordt vervolgens
gescheiden door opnieuw een witregel en de boundary, en het laatste deel wordt afgesloten door de boundary met twee streepjes "--".
Het ASPEmail en ASPMail component hebben beide een eigen manier van Multipart mail versturen. De nieuwste versie van ASPEmail heeft een "premium" optie ingebouwd,
die het versturen van Multipart mail vereenvoudigt, echter voor deze functionaliteit moet na 30 dagen betaald worden. Bovenstaand voorbeeld gebruikt deze functionaliteit niet.
Daarvoor in de plaats wordt de AddCustomHeader methode gebruikt, die voorzover bekend geen 30 dagen limiet heeft en precies hetzelfde resultaat geeft :)
ASPMail heeft een aparte ContentType property, speciaal voor gebruiken als het versturen van Multipart/alternative mail. Alhoewel een property als deze doet vermoeden dat het
opzetten van een multipart e-mail eenvoudig is, kan een programmeur echter nog lelijke zaken tegenkomen. Een van deze problemen is het automatisch encoderen van de te versturen
e-mail volgens de Quoted-Printable encodering. Om onbekende redenen worden berichten, die via ASPMail volgens deze encodering verstuurd zijn niet goed gedecodeerd. Dit resulteert
in = tekens aan het eind van afgebroken regels, en worden = tekens vervangen door =3D.
ASPMail stapt over op Quoted-Printable op het moment dat een bericht langer is dan 256 karakters, of een niet-ASCII teken tegengekomen wordt. Om nu te voorkomen dat ASPMail overstapt van
7bits (de standaard encodering) naar Quoted-Printable, adviseert ServerObjects om de property WordWrap aan te zetten. Na veel testen met lange teksten en buitenlandse karakters bleek deze oplossing effectief.
FAQ:
Het verstuurde emailtje komt wel aan, maar is leeg, of wordt meegestuurd als attachment. Wat is er mis?
Een leeg e-mailtje of attachment betekent meestal dat de mailclient niet de headers van de werkelijke body heeft kunnen scheiden.
Dit is vaak het resultaat van teveel of te weinig VbCrLf karakters tussen de headers van het bericht en de body, en de individuele,
door de boundaries gescheiden body-delen. Als Outlook gebruikt wordt, klik op View, Options en bekijk de Internet Headers. In principe
zouden hier alleen headers te zien moeten zijn. Mocht ook de tekst van (een deel van) het bericht terug te vinden zijn in dit scherm,
betekent dit dat Outlook de tekst als header ziet, en de e-mail zelf dus leeg weergeeft.
De structuur van een correct bericht is als volgt opgebouwd (let op de plaats van de enters en de streepjes voor en achter de boundaries):
<e-mail headers>
--<boundary>
<header van body-deel 1>
<inhoud van body-deel 1>
--<boundary>
<header van body-deel 2>
<inhoud van body-deel 2>
…(repeterend)
--<boundary>--
Een andere reden kan zijn dat ergens tijdens het ontvangen van de e-mail, de waarde van de boundary in de headers naar kleine letters (lowercase) omgezet is,
iets wat tussen de mailservers en de clients bleek te gebeuren. Sommige mailclients zijn case-sensitive, waardoor ze de gedefinieerde lowercase boundary zien als
een andere waarde dan de boundary zoals deze tussen de body-delen voorkomt, welke niet automatisch naar lowercase omgezet wordt en nog hoofdletters kan bevatten.
Om dit probleem te voorkomen is het handig altijd een boundarywaarde te kiezen die alleen kleine letters bevat.
Met dank aan Jurgen Mahn!
|