Returning generated CSS from an MVC5 controller action or Web API 2

In our multi-user application, we need to customize the styles used for each tenant.

We are currently planning to do this using LESS and variables as follows on the client:

  • Download dependent LESS files from the server
  • Web service call to get configuration object
  • Form string valid LESS with defined variables
  • Use the less.js compiler to compile LESS based on these variables and fixed LESS files from step 1

This approach has several disadvantages:

  • Customers may behave badly.
  • Some browsers have problems with less.js
  • Compilation takes time

Instead, we would like to take care of this work on the server so that, roughly speaking, this happens on the server:

  • The client asks to download one large compiled style sheet - GET content/styles/{tenantName}.css
  • Using tenantName , the server selects the configuration
  • Using a template and related variables (perhaps string.Format or something more complex)
  • Server compiles LESS into CSS string
  • Server returns CSS string with corresponding Content-Type

Here are my questions:

  • Is this an unusual or undesirable way to achieve this result?
  • Failed to configure the architecture for server-side JavaScript, how can I compile LESS into CSS?
  • What should I do in the controller action or in the route configuration so that the client considers that the server returns the usual old CSS file with cache control, and not changed?
+1
source share
1 answer

You can use BundleTransformer to compile your junior server side.

This may depend on how you want to serve the file. If you know all tenants, just add the bundle URL for each tenant application in the package configuration.

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

If you do not, and the tenants will be flexible, as was the case in our situation, add the following controller action for your themes.

  [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); } 

Implementation of ThemeBuilder for 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); } } 

You need to keep things from getting colors, we hid these variables in the HttpContext repositories so that we can pull them out using the extension method in the GetThemeVariables method.

Hope this helps.

UPDATE I expanded my initial answer and created a more reusable way to include topics.

Demo site here: http://bundletransformer-theme-builder.azurewebsites.net/

GitHub repo here: https://github.com/benembery/bundle-transformer-theme-builder

+5
source

Source: https://habr.com/ru/post/1215176/


All Articles