如何使用 ASP.Net MVC 4 在发布模式下捆绑 LESS 文件? [英] How to use ASP.Net MVC 4 to Bundle LESS files in Release mode?

查看:18
本文介绍了如何使用 ASP.Net MVC 4 在发布模式下捆绑 LESS 文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的 Web 项目中包含 LESS 文件,并将 MVC 4 捆绑功能调用到 dotLess 库中以将 LESS 转换为 CSS,然后缩小结果并将其提供给浏览器.

我在 ASP.NET 站点上找到了一个示例(在LESS、CoffeeScript、SCSS、Sass Bundling 标题下.).这给了我一个 LessTransform 类,如下所示:

公共类 LessTransform : IBundleTransform{public void Process(BundleContext 上下文,BundleResponse 响应){response.Content = dotless.Core.Less.Parse(response.Content);response.ContentType = "text/css";}}

以及我的 BundleConfig 类中的这一行:

bundles.Add(new Bundle("~/Content/lessTest",新的LessTransform(),new CssMinify()).Include("~/Content/less/test.less"));

最后,我的 _Layout.cshtml 中的 <head> 中有以下行:

@Styles.Render("~/Content/lessTest")

如果我的站点处于调试模式,则会呈现给浏览器:

应用 .less 文件中的规则,点击该链接表明 LESS 已正确转换为 CSS.

但是,如果我将站点置于发布模式,则会呈现:

<link href="/Content/less?v=lEs-HID6XUz3s2qkJ35Lvnwwq677wTaIiry6fuX8gz01" rel="stylesheet"/>

.less 文件中的规则应用,因为点击链接后 IIS 会出现 404 错误.

所以捆绑似乎出了点问题.我如何让它在发布模式下工作,或者我如何找出到底出了什么问题?

解决方案

Edited 12/8/2019 这不再是这个问题的可接受答案,因为这些年来 ASP.NET 已经发生了重大变化.还有其他答案修改了此代码或提供了其他答案来帮助您解决此问题.

看来无点引擎需要知道当前处理的bundle文件的路径才能解析@import路径.如果您运行上面的流程代码,当被解析的 .less 文件导入了其他的 less 文件时,dotless.Core.Less.Parse() 的结果是一个空字符串.

本·福斯特 (Ben Foster) 在此处的回复将通过首先读取导入的文件来解决此问题:

导入文件和 DotLess

按如下方式更改您的 LessTransform 文件:

公共类 LessTransform : IBundleTransform{public void Process(BundleContext context, BundleResponse bundle){如果(上下文==空){throw new ArgumentNullException("context");}如果(捆绑==空){throw new ArgumentNullException("bundle");}context.HttpContext.Response.Cache.SetLastModifiedFromFileDependencies();var lessParser = new Parser();ILessEngine lessEngine = CreateLessEngine(lessParser);var content = new StringBuilder(bundle.Content.Length);var bundleFiles = new List();foreach(bundle.Files 中的 var bundleFile){bundleFiles.Add(bundleFile);SetCurrentFilePath(lessParser, bundleFile.FullName);字符串源 = File.ReadAllText(bundleFile.FullName);content.Append(lessEngine.TransformToCss(source, bundleFile.FullName));content.AppendLine();bundleFiles.AddRange(GetFileDependencies(lessParser));}如果(BundleTable.EnableOptimizations){//在包文件中包含导入以注册缓存依赖项bundle.Files = bundleFiles.Distinct();}bundle.ContentType = "text/css";bundle.Content = content.ToString();}///<总结>///创建一个 LESS 引擎的实例.///</总结>///<param name="lessParser">LESS 解析器.</param>私有 ILessEngine CreateLessEngine(Parser lessParser){var logger = new AspNetTraceLogger(LogLevel.Debug, new Http());返回新的 LessEngine(lessParser, logger, true, false);}///<总结>///获取被解析的 LESS 文件的文件依赖(@imports).///</总结>///<param name="lessParser">LESS 解析器.</param>///<returns>对依赖文件引用的文件引用数组.</returns>私有 IEnumerableGetFileDependencies(Parser lessParser){IPathResolver pathResolver = GetPathResolver(lessParser);foreach(lessParser.Importer.Imports 中的 var importPath){yield return new FileInfo(pathResolver.GetFullPath(importPath));}lessParser.Importer.Imports.Clear();}///<总结>///返回一个 <see cref="IPathResolver"/>指定的 LESS lessParser 使用的实例.///</总结>///<param name="lessParser">LESS 解析器.</param>私有 IPathResolver GetPathResolver(Parser lessParser){var importer = lessParser.Importer 作为进口商;var fileReader = importer.FileReader as FileReader;返回 fileReader.PathResolver;}///<总结>///通知 LESS 解析器当前处理文件的路径.///这是通过使用自定义的 <see cref="IPathResolver"/>执行.///</总结>///<param name="lessParser">LESS 解析器.</param>///<param name="currentFilePath">当前处理文件的路径.</param>private void SetCurrentFilePath(Parser lessParser, string currentFilePath){var importer = lessParser.Importer 作为进口商;如果(进口商 == 空)throw new InvalidOperationException("意外的无点导入器类型.");var fileReader = importer.FileReader as FileReader;if (fileReader == null || !(fileReader.PathResolver 是 ImportedFilePathResolver)){fileReader = new FileReader(new ImportedFilePathResolver(currentFilePath));importer.FileReader = fileReader;}}}公共类 ImportedFilePathResolver : IPathResolver{私有字符串 currentFileDirectory;私有字符串 currentFilePath;public ImportedFilePathResolver(string currentFilePath){if (string.IsNullOrEmpty(currentFilePath)){throw new ArgumentNullException("currentFilePath");}CurrentFilePath = currentFilePath;}///<总结>///获取或设置当前处理文件的路径.///</总结>公共字符串 CurrentFilePath{得到 { 返回当前文件路径;}放{当前文件路径 = 值;currentFileDirectory = Path.GetDirectoryName(value);}}///<总结>///返回指定导入文件路径的绝对路径.///</总结>///<param name="filePath">导入的文件路径.</param>公共字符串GetFullPath(字符串文件路径){if (filePath.StartsWith("~")){filePath = VirtualPathUtility.ToAbsolute(filePath);}if (filePath.StartsWith("/")){filePath = HostingEnvironment.MapPath(filePath);}否则如果 (!Path.IsPathRooted(filePath)){filePath = Path.GetFullPath(Path.Combine(currentFileDirectory, filePath));}返回文件路径;}}

I'm trying to have LESS files in my web project, and have the MVC 4 bundling functionality call into the dotLess library to turn the LESS into CSS, then minify the result and give it to the browser.

I found an example on the ASP.NET site (under the heading LESS, CoffeeScript, SCSS, Sass Bundling.). This has given me a LessTransform class that looks like this:

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse response)
    {
        response.Content = dotless.Core.Less.Parse(response.Content);
        response.ContentType = "text/css";
    }
}

and this line in my BundleConfig class:

bundles.Add(new Bundle(
    "~/Content/lessTest", 
    new LessTransform(), 
    new CssMinify()).Include("~/Content/less/test.less"));

finally I have the following line in my _Layout.cshtml, in the <head>:

@Styles.Render("~/Content/lessTest")

If I have the site in debug mode, this is rendered to the browser:

<link href="/Content/less/test.less" rel="stylesheet"/>

The rules in the .less file are applied, and following that link shows that the LESS has been correctly transformed into CSS.

However, if I put the site into release mode, this is rendered out:

<link href="/Content/less?v=lEs-HID6XUz3s2qkJ35Lvnwwq677wTaIiry6fuX8gz01" rel="stylesheet"/>

The rules in the .less file are not applied, because following the link gives a 404 error from IIS.

So it seems that something is going wrong with the bundling. How do I get this to work in release mode, or how do I find out what exactly is going wrong?

解决方案

Edited 12/8/2019 This is no longer an acceptable answer to this issue as there have been breaking changes in ASP.NET over the years. There are other answers further down that have modified this code or supplied other answers to help you fix this issue.

It appears that the dotless engine needs to know the path of the currently processed bundle file to resolve @import paths. If you run the process code that you have above, the result of the dotless.Core.Less.Parse() is an empty string when the .less file being parsed has other less files imported.

Ben Foster's response here will fix that by reading the imported files first:

Import Files and DotLess

Change your LessTransform file as follows:

public class LessTransform : IBundleTransform
{
    public void Process(BundleContext context, BundleResponse bundle)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        if (bundle == null)
        {
            throw new ArgumentNullException("bundle");
        }

        context.HttpContext.Response.Cache.SetLastModifiedFromFileDependencies();

        var lessParser = new Parser();
        ILessEngine lessEngine = CreateLessEngine(lessParser);

        var content = new StringBuilder(bundle.Content.Length);

        var bundleFiles = new List<FileInfo>();

        foreach (var bundleFile in bundle.Files)
        {
            bundleFiles.Add(bundleFile);

            SetCurrentFilePath(lessParser, bundleFile.FullName);
            string source = File.ReadAllText(bundleFile.FullName);
            content.Append(lessEngine.TransformToCss(source, bundleFile.FullName));
            content.AppendLine();

            bundleFiles.AddRange(GetFileDependencies(lessParser));
        }

        if (BundleTable.EnableOptimizations)
        {
            // include imports in bundle files to register cache dependencies
            bundle.Files = bundleFiles.Distinct();
        }

        bundle.ContentType = "text/css";
        bundle.Content = content.ToString();
    }

    /// <summary>
    /// Creates an instance of LESS engine.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    private ILessEngine CreateLessEngine(Parser lessParser)
    {
        var logger = new AspNetTraceLogger(LogLevel.Debug, new Http());
        return new LessEngine(lessParser, logger, true, false);
    }

    /// <summary>
    /// Gets the file dependencies (@imports) of the LESS file being parsed.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    /// <returns>An array of file references to the dependent file references.</returns>
    private IEnumerable<FileInfo> GetFileDependencies(Parser lessParser)
    {
        IPathResolver pathResolver = GetPathResolver(lessParser);

        foreach (var importPath in lessParser.Importer.Imports)
        {
            yield return new FileInfo(pathResolver.GetFullPath(importPath));
        }

        lessParser.Importer.Imports.Clear();
    }

    /// <summary>
    /// Returns an <see cref="IPathResolver"/> instance used by the specified LESS lessParser.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    private IPathResolver GetPathResolver(Parser lessParser)
    {
        var importer = lessParser.Importer as Importer;
        var fileReader = importer.FileReader as FileReader;

        return fileReader.PathResolver;
    }

    /// <summary>
    /// Informs the LESS parser about the path to the currently processed file. 
    /// This is done by using a custom <see cref="IPathResolver"/> implementation.
    /// </summary>
    /// <param name="lessParser">The LESS parser.</param>
    /// <param name="currentFilePath">The path to the currently processed file.</param>
    private void SetCurrentFilePath(Parser lessParser, string currentFilePath)
    {
        var importer = lessParser.Importer as Importer;

        if (importer == null)
            throw new InvalidOperationException("Unexpected dotless importer type.");

        var fileReader = importer.FileReader as FileReader;

        if (fileReader == null || !(fileReader.PathResolver is ImportedFilePathResolver))
        {
            fileReader = new FileReader(new ImportedFilePathResolver(currentFilePath));
            importer.FileReader = fileReader;
        }
    }
}

public class ImportedFilePathResolver : IPathResolver
{
    private string currentFileDirectory;
    private string currentFilePath;

    public ImportedFilePathResolver(string currentFilePath)
    {
        if (string.IsNullOrEmpty(currentFilePath))
        {
            throw new ArgumentNullException("currentFilePath");
        }

        CurrentFilePath = currentFilePath;
    }

    /// <summary>
    /// Gets or sets the path to the currently processed file.
    /// </summary>
    public string CurrentFilePath
    {
        get { return currentFilePath; }
        set
        {
            currentFilePath = value;
            currentFileDirectory = Path.GetDirectoryName(value);
        }
    }

    /// <summary>
    /// Returns the absolute path for the specified improted file path.
    /// </summary>
    /// <param name="filePath">The imported file path.</param>
    public string GetFullPath(string filePath)
    {
        if (filePath.StartsWith("~"))
        {
            filePath = VirtualPathUtility.ToAbsolute(filePath);
        }

        if (filePath.StartsWith("/"))
        {
            filePath = HostingEnvironment.MapPath(filePath);
        }
        else if (!Path.IsPathRooted(filePath))
        {
            filePath = Path.GetFullPath(Path.Combine(currentFileDirectory, filePath));
        }

        return filePath;
    }
}

这篇关于如何使用 ASP.Net MVC 4 在发布模式下捆绑 LESS 文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆