String.Format failing on style section of HTML Email Template
Asked Answered
P

3

5

I am sending HTML email template using Bodybuilder and a mail sending service. I used this link learn.

https://www.c-sharpcorner.com/article/send-email-using-templates-in-asp-net-core-applications/

Anyway, I created my email template using an external service (https://topol.io/).

It is giving me errors when I try to use the generated HTML file with the String.FOrmat command referenced in the first link. I finally figured out it is this section of code causing the String.Format to fail.

This is my c# code that normally works with other HTML templates:

using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile))
            {
                builder.HtmlBody = SourceReader.ReadToEnd();
            }

string messagebody = string.Format(builder.HtmlBody,
                    //Not important
                    );

Now, this style code in the HTML causes the String.Format to crash (or the bodybuilder, I'm not sure exactly which one has the issue, it fails at string.format).

<style type="text/css">
        #outlook a {
            padding: 0;
        }

        .ReadMsgBody {
            width: 100%;
        }

        .ExternalClass {
            width: 100%;
        }

            .ExternalClass * {
                line-height: 100%;
            }

        body {
            margin: 0;
            padding: 0;
            -webkit-text-size-adjust: 100%;
            -ms-text-size-adjust: 100%;
        }

        table, td {
            border-collapse: collapse;
            mso-table-lspace: 0pt;
            mso-table-rspace: 0pt;
        }

        img {
            border: 0;
            height: auto;
            line-height: 100%;
            outline: none;
            text-decoration: none;
            -ms-interpolation-mode: bicubic;
        }

        p {
            display: block;
            margin: 13px 0;
        }
    </style>
    <!--[if !mso]><!-->
    <style type="text/css">
        @media only screen and (max-width:480px) {
            @-ms-viewport {
                width: 320px;
            }

            @viewport {
                width: 320px;
            }
        }
    </style>
    <style type="text/css">
        @media only screen and (min-width:480px) {
            .mj-column-per-100 {
                width: 100% !important;
            }

            .mj-column-per-60 {
                width: 60% !important;
            }

            .mj-column-per-40 {
                width: 40% !important;
            }

            .mj-column-per-50 {
                width: 50% !important;
            }
        }
    </style>
    <!--[if mso]><xml>
            <o:OfficeDocumentSettings>
            <o:AllowPNG/>
            <o:PixelsPerInch>96</o:PixelsPerInch>
            </o:OfficeDocumentSettings></xml><![endif]-->
    <!--[if lte mso 11]><style type="text/css">.outlook-group-fix {    width:100% !important;  }</style>
        <![endif]-->
    <!--[if !mso]><!-->
    <link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
    <link href="https://fonts.googleapis.com/css?family=Merriweather" rel="stylesheet" type="text/css">
    <style type="text/css">
        @import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
        @import url(https://fonts.googleapis.com/css?family=Merriweather);
    </style>
    <!--<![endif]-->

Any ideas? I am open to suggestions of how to put this CSS section externally if the reason for the failure is not obvious.

Proceeds answered 14/6, 2018 at 8:0 Comment(1)
The most elegant way to solve this problem is use CSS inliner. Without <style> tags you'll remove all { and } symbolsBunkhouse
H
5

You are getting that error because curly braces are used in string.Format as a place holder for a value parameter.

One way to fix your problem is replacing braces with double braces:

using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile))
            {
                builder.HtmlBody = SourceReader.ReadToEnd();
            }

string htmlBody = builder.HtmlBody.Replace("{", "{{").Replace("}","}}")
string messagebody = string.Format(htmlBody,
                    //Not important
                    );
Hedvige answered 14/6, 2018 at 8:20 Comment(3)
@dave317, Doing this every time you read the the template is inefficient and, how would you know which braces are and are not place holders. If there are no place holders, you don't need String.Format at all.Bratton
I agree, but when I wrote the answer I interpreted OP's comment //not important as not important to the question, but important to his code.Hedvige
OF COURSE! I am using placeholders. I should have realized that. SMH moment right now. I'll replace the single braces wth doubles and see if it works. I will do it on the template. You are correct Mat, the //not important is some placeholder info.Proceeds
B
2

You are calling String.Format on a string that contains lots of CSS definitions. CSS definitions include many { and } pairs. String.Format interprets { and } pairs as placeholders for substitution but, that is not what they represent in your string.

Why do you need to use String.Format in this scenario?

Why not instead do,

builder.HtmlBody = File.ReadAllText(pathToFile);

If //Not important is, in fact important, you could escape the braces in your template, as per this answer. So that braces you do not want to substitute become '{{' and '}}' respectively.

Basically, run a further one time preparation step on your template, switching { for {{ and } for }} unless they represent a placeholder you would like to substitute.

Bratton answered 14/6, 2018 at 8:5 Comment(0)
G
0

The problem are the { and } of the CSS part. For string.Format they represent the placeholders, but CSS code is no valid placeholder.

If you really need to replace some text with string.Format and you always push your inputfile throughstring.Format, you could simply replace each { and } of your input with {{ and }} respectively, where you actually want a { to appear in the final result and use { and } where a replacement should be done. But not sure how that plays along with Bodybuilder.

Guss answered 14/6, 2018 at 8:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.