Как включить встроенные стили CSS в представление Razor?

Я использую Postal для визуализации представлений MVC Razor и отправки их по электронной почте. У меня есть собственный CSS, который я определил специально для просмотра электронной почты. В настоящее время я включаю их следующим образом:

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

Однако это включает только относительную ссылку на таблицу стилей, которая не будет работать в электронном письме:

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

Я хочу включить встроенную таблицу стилей, чтобы она правильно работала в электронном письме. Как лучше всего отобразить содержимое файлового ресурса в представлении MVC?


person luksan    schedule 12.01.2013    source источник


Ответы (4)


Я думаю, вам понадобится специальный помощник для этого. На мой взгляд, нет такого метода для отображения пути css, включая абсолютный путь веб-сайта.

например http:www.example.com/css/EmailStyles.css

person Stay Foolish    schedule 12.01.2013
comment
Да, но я даже не хочу включать абсолютный путь к таблице стилей, так как почтовый клиент не получит его из соображений безопасности. Я хочу включить содержимое файла css. - person luksan; 13.01.2013
comment
Итак, напишите помощник html, который будет отображать содержимое вашего файла css как встроенный стиль. - person mipe34; 13.01.2013
comment
Многие почтовые клиенты даже не используют стили, определенные в заголовке. Вам лучше использовать встроенные стили для электронных писем. См. campaignmonitor.com/css. - person viperguynaz; 13.01.2013
comment
нет, не бессмысленный вопрос; просто хитрый. Вам нужно сделать еще один шаг, помимо простого встраивания стилей в заголовок. См. мой ответ. - person Paul d'Aoust; 16.03.2013

У меня самого был тот же вопрос, и я наткнулся на Premailer.Net. Похоже, нужная библиотека. Вот что вам нужно сделать:

  1. Создайте метод расширения, который поможет вам встроить CSS на вашу страницу; есть ответ на вопрос о том, как встроить HTML в представление Razor, который должен вам помочь. Я изменил его для встраивания 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? тоже так делай).

  5. Пропустите строку через Premailer.Net:

    PreMailer pm = new PreMailer();
    string premailedOutput = pm.MoveCssInline(htmlSource, false);
    
  6. Отправить по электронной почте!

Я уже некоторое время использую эту технику в производстве, и, похоже, она работает довольно хорошо.

Изменить. Помните, что стили псевдоэлементов не могут быть встроены, поскольку они не существуют в разметке. Я также заметил странную маленькую ошибку в Premailer.Net — я думаю, что их специфичность и каскадные правила не полностью соответствуют друг другу. Тем не менее, это довольно хорошо, и это еще один фрагмент кода, который мне не пришлось писать!

person Paul d'Aoust    schedule 15.03.2013
comment
Это хорошее начало.... 1.) Как это можно изменить для работы со связанными стилями? 2.) Как насчет связанных файлов .less с использованием пакета nuget без точек, который обслуживает файлы .less, преобразованные в .css. - person sheamus; 21.06.2015
comment
Хм, интересный вопрос. Компиляция и объединение ресурсов всегда были прерогативой моего коллеги. Бьюсь об заклад, у Cassette или System.Web.Optimization есть какой-то способ вывода пакета в виде строки или потока памяти; это было бы местом для начала. Тогда вы могли бы ответить на оба ваших вопроса одним решением. - person Paul d'Aoust; 23.06.2015
comment
клиент gmail не может читать встроенные таблицы стилей, такие как тег стиля внутри тега заголовка. Единственный подход состоит в том, чтобы разработать файл .css и проанализировать определения этого идентификатора/класса и преобразовать их в html-теги как inline-style=background:blue, которые будут работать на всех почтовых клиентах! - person Pascal; 06.05.2016
comment
Можете ли вы поделиться примером того, как вы используете почтовые отправления, чтобы вернуть обработанный HTML? Я пытаюсь следовать этому примеру, но у меня возникают проблемы с реализацией IEmailService aboutcode.net/postal /create-mail-message.html - person TWilly; 17.05.2016
comment
@TWilly Извините, к сожалению, у меня нет опыта работы с почтовой частью, и это выходит за рамки моего ответа. Может, задать вопрос задавшему? Но я предполагаю, что Postal принимает тело сообщения в виде строки? Если это так, вы просто передадите значение premailedOutput на моем шаге 5 в качестве тела. Обязательно сообщите ему, что вы используете text/html mimetype, если для этого есть место. - person Paul d'Aoust; 18.05.2016
comment
привет, Пол, это здорово, не могли бы вы показать модифицированную версию для непосредственного встраивания блока CSS, а не для чтения из файла .myIconDiv { color : @getColrVariableFromMyAction}, и как использовать/использовать его внутри представления, и нужны ли ему дополнительные крышки, когда я пытался его встроить собственные блоки css, которые он не будет компилировать - person Transformer; 23.01.2017

Проголосовал за ответ Поля д'Оуста, но я обнаружил, что эта версия его вспомогательного метода работает для меня немного лучше (не стал бы пытаться кодировать такие вещи, как кавычки в 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;
    }
  }
}
person dprothero    schedule 11.09.2015
comment
Спасибо что подметил это; Я не думаю, что заметил, что мой код избегал таких вещей, как кавычки. - person Paul d'Aoust; 18.05.2016
comment
привет, это здорово, не могли бы вы показать модифицированную версию для непосредственного встраивания блока CSS, а не для чтения из файла .p { color : @getColrVariableFromMyAction}, и как использовать/использовать его внутри представления. какие дополнительные библиотеки мне нужны? - person Transformer; 23.01.2017
comment
@transformer Непосредственное встраивание блока CSS было бы простым; вы просто примете строку как сам CSS, а не загрузите содержимое файла в строку; Я оставлю это в качестве упражнения для читателя :) Что касается использования его в представлении, все, что вам нужно сделать, это @Html.EmbedCss(path), где path — это путь к вашему файлу CSS. (Или, если вы измените функцию для использования блока CSS, это будет настоящий CSS.) - person Paul d'Aoust; 23.01.2017
comment
Понятно! поэтому он испускает блоки CSS, которые я должен обернуть внутри раздела? body {background-color: powderblue;} h1 {color: blue;} p {color: red;} </style> - person Transformer; 24.01.2017
comment
могу ли я поместить статическое расширение public class NotStaticClass{ public static IHtmlString EmbedCssStaticFunc(this HtmlHelper htmlHelper, string path) внутри нестатического класса? Я пытаюсь сгруппировать свои утилиты и хотел бы добавить это туда - person Transformer; 24.01.2017

Я понимаю, что это старый вопрос, но вот модифицированная версия ответа 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")
}
person brandonstrong    schedule 28.06.2021