Возврат сгенерированного CSS из действия контроллера MVC5 или Web API 2

В нашем мультитенантном приложении нам необходимо настроить стили, используемые для каждого арендатора.

В настоящее время мы планируем сделать это с помощью LESS и переменных следующим образом на клиенте:

  1. Загрузите зависимые файлы LESS с сервера
  2. Вызовите веб-сервис для получения объекта конфигурации
  3. Форма строки действительного LESS с определенными переменными
  4. использование less.js компилятор для компиляции LESS на основе этих переменных и фиксированных файлов LESS с шага 1

Этот подход имеет ряд недостатков:

  • Клиенты могут вести себя плохо
  • Некоторые браузеры имеют проблемы с less.js
  • Компиляция требует времени

Вместо этого мы хотели бы позаботиться об этой работе на сервере, так что, грубо говоря, это происходит на сервере:

  1. Клиент просит загрузить одну большую скомпилированную таблицу стилей - GET content/styles/{tenantName}.css
  2. С помощью tenantName сервер выбирает конфигурацию
  3. Используя шаблон и соответствующие переменные (возможно, string.Format или что то более изощренное)
  4. Сервер компилирует LESS в строку CSS
  5. Сервер возвращает строку CSS с соответствующей Content-Type

Вот мои вопросы:

  1. Это необычный или нежелательный способ достижения указанного результата?
  2. Если не считать настройки архитектуры для серверного JavaScript, как я могу скомпилировать LESS в CSS?
  3. Что я должен сделать в действии контроллера или в конфигурации маршрута, чтобы заставить клиента думать, что сервер возвращает обычный старый файл CSS с полным контролем кэша, без изменений?

1 ответ

Решение

Вы можете использовать BundleTransformer для компиляции вашей стороны LESS.

Это может зависеть от того, как вы хотите обслуживать файл. Если вы знаете всех арендаторов, просто добавьте URL-адрес пакета для каждого приложения клиента в конфигурацию пакета.

 var themeStyles = new CustomStyleBundle("~bundles/theme/tenant").Include("~/Content/theme.less");
 themeStyles.Builder = new ThemeBuilder();
 BundleTable.Bundles.Add(themeStyles);

Если вы этого не сделаете, и арендаторы будут гибкими, как это было в нашей ситуации, добавьте следующее действие контроллера для ваших тем.

    [Route("bundles/theme/{id}")]
    public ContentResult Theme(string id)
    {
        var tenantThemePath = string.Format("~/bundles/theme/{0}", id);

        // Check that bundle has not already been added.
        if (BundleTable.Bundles.All(x => x.Path != tenantThemePath))
        {
            var themeStyles = new CustomStyleBundle(tenantThemePath ).Include("~/Content/theme.less");

            themeStyles.Builder = new ThemeBuilder();

            BundleTable.Bundles.Add(themeStyles);
        }

        var context = new BundleContext(HttpContext, BundleTable.Bundles, institutionPath);

        var response = BundleTable.Bundles.GetBundleFor(tenantThemePath).GenerateBundleResponse(context);

        Response.Cache.SetCacheability(response.Cacheability);

        return Content(response.Content, response.ContentType);
    }

Реализация ThemeBuilder для BundleTransformer

public class ThemeBuilder : IBundleBuilder
{
    public string BuildBundleContent(Bundle bundle, BundleContext context, IEnumerable<BundleFile> files)
    {
        var lessTranslator = bundle.Transforms.OfType<StyleTransformer>()
            .Where(x => x != null)
            .Select(x => x.Translators.OfType<LessTranslator>().FirstOrDefault())
            .FirstOrDefault();

        if (lessTranslator == null)
        {
            return string.Empty;
        }

        lessTranslator.GlobalVariables = GetThemeVariables();

        return string.Empty;
    }

    private string GetThemeVariables()
    {
        // Simplified for brevity
        // This will be translated to less variables by the BundleTransformer
        // themeColour should correspond to a variable name in your less file.  
        return string.Format("themeColour={0}", themeColour);
    }

}

Вам не понадобится выводить цвета темы, мы спрятали эти переменные в хранилищах HttpContext, чтобы мы могли извлечь их с помощью метода расширения в методе GetThemeVariables.

Надеюсь, это поможет.

ОБНОВЛЕНИЕ Я расширил свой первоначальный ответ и создал более удобный способ включения тем.

Демо-сайт здесь: http://bundletransformer-theme-builder.azurewebsites.net/

GitHub репо здесь: https://github.com/benembery/bundle-transformer-theme-builder

Другие вопросы по тегам