23

我正在使用Postal呈现 MVC Razor 视图并通过电子邮件发送它们。我有一个专门为电子邮件视图定义的自定义 CSS。目前我将它们包括如下:

@Styles.Render("~/Content/EmailStyles.css")

但是,这仅包括样式表的相对链接,这在电子邮件中不起作用:

<link href="/Content/EmailStyles.css" rel="stylesheet"/>

我想包含内联样式表,以便它在电子邮件中正常工作。在 MVC 视图中呈现基于文件的资源内容的最佳方式是什么?

4

4 回答 4

37

我自己也有同样的问题,并且遇到了Premailer.Net。它看起来像您需要的库。这是你必须做的:

  1. 创建一个扩展方法来帮助您将 CSS 嵌入到您的页面中;关于如何在 Razor 视图中嵌入 HTML 的问题有一个答案,应该可以帮助您。我已经修改它以嵌入 CSS:

    public static class HtmlHelpers
    {
        public static MvcHtmlString EmbedCss(this HtmlHelper htmlHelper, string path)
        {
            // take a path that starts with "~" and map it to the filesystem.
            var cssFilePath = HttpContext.Current.Server.MapPath(path);
            // load the contents of that file
            try
            {
                var cssText = System.IO.File.ReadAllText(cssFilePath);
                var styleElement = new TagBuilder("style");
                styleElement.InnerHtml = cssText;
                return MvcHtmlString.Create(styleElement.ToString());
            }
            catch (Exception ex)
            {
                // return nothing if we can't read the file for any reason
                return null;
            }
        }
    }
    
  2. 然后在你的 Razor 模板中,去:

    @Html.EmbedCss("~/Content/EmailStyles.css")
    

    嵌入您的 CSS 文本。

  3. 在你的项目中安装 Premailer.Net 包;你可以通过 NuGet 获得它。

  4. 将您的 Razor 视图呈现为字符串(我猜这就是 Postal 在您的流程中的用途?我相信RazorEngine也可以做到这一点)。

  5. 通过 Premailer.Net 运行字符串:

    PreMailer pm = new PreMailer();
    string premailedOutput = pm.MoveCssInline(htmlSource, false);
    
  6. 作为电子邮件发送!

我在生产中使用这种技术已经有一段时间了,它似乎运行得很好。

编辑:请记住,伪元素上的样式不能内联,因为它们不存在于标记中。我还注意到 Premailer.Net 中的一个奇怪的小错误——我认为它们的特异性和级联规则并不完全一致。尽管如此,它还是很不错的,而且它是我不必编写的又一段代码!

于 2013-03-15T23:23:26.310 回答
13

赞成 Paul d'Aoust 的回答,但我发现他的辅助方法的这个版本对我来说更好一些(不会尝试在 CSS 中对引号之类的东西进行编码):

public static class CssHelper
{
  public static IHtmlString EmbedCss(this HtmlHelper htmlHelper, string path)
  {
    // take a path that starts with "~" and map it to the filesystem.
    var cssFilePath = HttpContext.Current.Server.MapPath(path);
    // load the contents of that file
    try
    {
      var cssText = File.ReadAllText(cssFilePath);
      return htmlHelper.Raw("<style>\n" + cssText + "\n</style>");
    }
    catch
    {
      // return nothing if we can't read the file for any reason
      return null;
    }
  }
}
于 2015-09-11T20:08:09.670 回答
1

我想你需要有一个自定义的助手。在我的头上,没有这样的方法来呈现包括网站绝对路径的 css 路径。

例如 http:www.example.com/css/EmailStyles.css

于 2013-01-12T22:33:50.037 回答
0

我意识到这是一个老问题,但这里是 dprothero 答案的修改版本,它将嵌入捆绑包。创建一个静态 C# 类并将此方法放入其中:

public static IHtmlString EmbedCss(this HtmlHelper htmlHelper, string path)
{
  try
  {
      // Get files from bundle
      StyleBundle b = (StyleBundle)BundleTable.Bundles.GetBundleFor("~/Content/css");
      BundleContext bc = new BundleContext(new HttpContextWrapper(HttpContext.Current), BundleTable.Bundles, "~/Content/css");
      List<BundleFile> files = b.EnumerateFiles(bc).ToList();
      // Create string to return
      string stylestring = "";
      // Iterate files in bundle
      foreach(BundleFile file in files)
      {
          // Get full path to file
          string filepath = HttpContext.Current.Server.MapPath(file.IncludedVirtualPath);
          // Read file text and append to style string
          string filetext = File.ReadAllText(filepath);
          stylestring += $"<!-- Style for {file.IncludedVirtualPath} -->\n<style>\n{filetext}\n</style>\n";
      }
      return htmlHelper.Raw(stylestring);
  }
  catch
  {
      // return nothing if we can't read the file for any reason
      return null;
  }

然后转到您要在其中使用它的任何视图。请务必添加 using 语句,以便您的视图可以看到 CSS 帮助器。我还使用 TempData 来决定是否内联渲染它:

<!-- Using statement -->
@using Namespace.Helpers;

<!-- Check tempdata flag for whether or not to render inline -->
@if (TempData["inlinecss"] != null)
{
    <!-- Embed CSS with custom code -->
    @Html.EmbedCss("~/Content/css")
}
else
{
    <!-- Use links to reference CSS -->
    @Styles.Render("~/Content/css")
}
于 2021-06-28T15:49:19.867 回答