问题描述
在我们的多租户应用程序,我们有需要来定制每个租户使用的样式。
我们目前计划这样做在客户端以下列方式的使用更少和变量的
- 从服务器上下载相关的减档
- 呼叫网络服务来获取配置对象
- 有效LESS形式字符串定义的变量
- 使用
less.js
根据这些变量和固定LESS文件从第1步 编译器编译LESS
这方法有许多缺点的:
- 客户端可以表现不好
- 有些浏览器有
less.js
问题 - 编译需要一定的时间
我们将改为借此在服务器上这项工作的关心,这样粗略地讲,这种情况发生在服务器上,而不是:
- 客户端请求下载一个大编译的样式表 -
获取内容/风格/ {} tenantName名为.css
- 使用
tenantName
服务器获取配置 - 使用模板和相应的变量(也许
的String.Format
或一些更复杂) - Server编译少CSS串
- 服务器返回的字符串的CSS适当
内容类型
下面是我的问题:
- 这是一种非正常的方式来实现上述结果呢?
- 短设置架构服务器端JavaScript的,我怎么能编译少进CSS?
- 我必须在控制器的动作或路由配置,使客户端确实认为服务器返回一个普通的旧CSS文件,完整的高速缓存控制,不修改?
您可以使用编译您LESS服务器端。
这可能取决于你想如何提供服务的文件。如果你知道所有的住户则只需添加添加一个包的网址为每个租户应用程序包的配置。
VAR themeStyles =新CustomStyleBundle(〜包/主题/租客),包括(〜/内容/ theme.less)。
themeStyles.Builder =新ThemeBuilder();
BundleTable.Bundles.Add(themeStyles);
如果你不与租户灵活是我们的情况的话,那么添加下面的控制器操作为您的主题。
[路线(包/主题/(编号))
公共ContentResult类型主题(字符串ID)
{
VAR tenantThemePath =的String.Format(〜/包/主题/ {0},身份证); //检查这个包尚未加入。
如果(BundleTable.Bundles.All(X =>!x.Path = tenantThemePath))
{
VAR themeStyles =新CustomStyleBundle(tenantThemePath).INCLUDE(〜/内容/ theme.less); themeStyles.Builder =新ThemeBuilder(); BundleTable.Bundles.Add(themeStyles);
} VAR语境=新的BundleContext(HttpContext的,BundleTable.Bundles,institutionPath); 变种响应= BundleTable.Bundles.GetBundleFor(tenantThemePath).GenerateBundleResponse(上下文); Response.Cache.SetCacheability(response.Cacheability); 返回内容(response.Content,response.ContentType);
}
有关BundleTransformer的ThemeBuilder实施
公共类ThemeBuilder:IBundleBuilder
{
公共字符串BuildBundleContent(捆绑包,BundleContext的背景下,IEnumerable的< BundleFile>文件)
{
VAR lessTranslator = bundle.Transforms.OfType< StyleTransformer>()
。凡(X => X!= NULL)
。选择(X => x.Translators.OfType< LessTranslator>()FirstOrDefault())。
.FirstOrDefault(); 如果(lessTranslator == NULL)
{
返回的String.Empty;
} lessTranslator.GlobalVariables = GetThemeVariables(); 返回的String.Empty;
} 私人字符串GetThemeVariables()
{
//简化为简洁
//这将由BundleTransformer转换为较少的变量
// themeColour应该对应一个变量名在较少的文件。
返回的String.Format(themeColour = {0},themeColour);
}}
您将需要越来越远的主题颜色了,我们在卖场的HttpContext藏匿这些变量,这样我们就可以在GetThemeVariables方法使用扩展的方法将它们拉出来。
我希望这有助于。
更新
我已经在我原来的答复扩大和创造,包括主题,更可重用的方式。
在这里演示网站:
In our multi-tenant application we have a need to customize the styles used per-tenant.
We currently plan to do so using LESS and variables in the following way on the client:
- Download dependent LESS files from server
- Call web service to get configuration object
- Form string of valid LESS with variables defined
- Use
less.js
compiler to compile LESS based on these variables and the fixed LESS files from step 1
This approach has a number of downsides:
- Clients can behave badly
- Some browsers have problems with
less.js
- Compilation takes time
We would instead like to take care of this work on the server, so that roughly speaking, this happens on the server instead:
- Client requests to download one big compiled stylesheet -
GET content/styles/{tenantName}.css
- Using
tenantName
the server fetches configuration - Using a template and the appropriate variables (maybe
string.Format
or something more sophisticated) - Server compiles LESS to CSS string
- Server returns CSS string with appropriate
Content-Type
Here are my questions:
- Is this an unusual or undesirable way to achieve said result?
- Short of setting up architecture for server-side JavaScript, how can I compile the LESS into CSS?
- What must I do in the controller action or in the route configuration to make the client think that the server is returning a regular old CSS file, complete with cache control, not modified?
You could use BundleTransformer to compile your LESS server side.
It can depend on how you want to serve the file. If you know all the tenants then just add add a bundle url for each tenant application to the bundle config.
var themeStyles = new CustomStyleBundle("~bundles/theme/tenant").Include("~/Content/theme.less");
themeStyles.Builder = new ThemeBuilder();
BundleTable.Bundles.Add(themeStyles);
If you don't and the tenants are flexible as was the case in our situation then 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);
}
The ThemeBuilder implementation 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 will need away of getting the theme colours out we stashed those variables in HttpContext stores so that we could pull them out using an extension method in the GetThemeVariables method.
I hope this helps.
UPDATEI've expanded on my original answer and created a more reusable way of including themes.
Demo site here: http://bundletransformer-theme-builder.azurewebsites.net/
GitHub repo here: https://github.com/benembery/bundle-transformer-theme-builder
这篇关于从MVC5或Web API 2控制器动作返回生成CSS的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!