【问题标题】:How can I output errors when using .less programmatically?以编程方式使用 .less 时如何输出错误?
【发布时间】:2011-06-15 10:49:53
【问题描述】:

我编写了一个 ASP.NET MVC 操作方法,它接收一个 .less 文件名,通过Less.Parse(<filename>) 处理它并输出处理后的 css 文件。

只要 .less 代码有效,它就可以正常工作,但如果出现错误,dotLess 只会返回一个空字符串。因此,如果处理文件时出错,我的操作方法会返回一个空的 css 文件。

我怎样才能输出一条错误消息,其中包含对语法错误的更详细描述?

【问题讨论】:

  • 你看过 SquishIt 吗? codethinked.com/post/2010/05/26/…
  • @qstarin:谢谢,但我已经将梳子 (combres.codeplex.com) 用于生产目的,并且梳子可以做 squishit 所做的一切(还有更多)。但是出于开发目的,我仍然需要上述问题的答案。原因是我不喜欢使用所有 CSS 文件的组合下载包含,而是更喜欢单独的文件。一切正常,除了错误消息......
  • 我很好奇,combres 提供了哪些 SquishIt 没有的有用功能?查看 Combres 的文档,他们似乎做了很多相同的任务。然而,SquishIt 确实将 dotLess 包含在它的处理中。这就是我提到它的原因,因为您不需要单独的操作来执行 dotLess 编译。
  • 看一下 dotLess 的源代码显示了一种获取错误信息的方法,我会写一个答案并稍后发布。
  • @qstarin:Combres 还提供无点支持,但如果我理解正确的话,combres 和 squishit 都不允许我在调试模式下单独输出我的 css 文件。另外,我还想在所有其他文件中从一个全局文件中引用无点变量和 mixin,即使没有将所有内容合并到一个文件中。这就是我编写自己的解决方法的原因。

标签: css asp.net-mvc dotless


【解决方案1】:

dotLess 解析器捕获异常并将它们输出到记录器。 dotLess 源中执行此操作的 sn-p 是 LessEngine.TransformToCss

public string TransformToCss(string source, string fileName)
{
    try
    {
        Ruleset ruleset = this.Parser.Parse(source, fileName);
        Env env = new Env();
        env.Compress = this.Compress;
        Env env2 = env;
        return ruleset.ToCSS(env2);
    }
    catch (ParserException exception)
    {
        this.Logger.Error(exception.Message);
    }
    return "";
}

Less.Parse 有一个采用DotlessConfiguration 对象的重载,它提供了几个可以使用的属性:

public class DotlessConfiguration
{
    // Properties
    public bool CacheEnabled { get; set; }
    public Type LessSource { get; set; }
    public Type Logger { get; set; }
    public LogLevel LogLevel { get; set; }
    public bool MinifyOutput { get; set; }
    public int Optimization { get; set; }
    public bool Web { get; set; }
}

您会注意到Logger 属性的类型为Type。无论您提供什么类型,都必须实现dotless.Core.Loggers.ILogger

public interface ILogger
{
    // Methods
    void Debug(string message);
    void Error(string message);
    void Info(string message);
    void Log(LogLevel level, string message);
    void Warn(string message);
}

正如我们在第一个 sn-p 中看到的,当解析过程中遇到错误时,记录器上的 Error 方法将被调用。

现在,所有这一切的一个难点在于,实现ILogger 的类型的实例究竟是如何被实例化的。在内部,dotLess 使用嵌入到 DLL 中的 IoC 容器。在方法调用之后,它似乎最终会调用Activator.CreateInstance 来实例化您的 ILogger。

我希望这至少有点帮助。

【讨论】:

  • 我真的应该对记录器 API 进行一些修改。在撰写本文时,只考虑内部使用是有意义的。但对于外部调用者来说,这非常糟糕。。
  • @Tigraine:我希望至少我已经正确解释了如何捕获日志事件,我有点不确定我是否把它全部记下来,特别是指定 DotlessConfiguration 对象的后果是什么只填充了日志记录类。另外,如果你正在修改它,我会指出 SimpleLoggingFramework 或 Common.Logging 作为日志记录系统的两个潜在外观,但老实说,它可能会与所有依赖项混淆......祝你好运。呵呵
  • 谢谢.. 我们尽量避免像害虫这样的外部依赖,这就是为什么我们还没有依赖 log4net 或其他东西,但几乎复制了 Castle.ILogger 的想法.. 但我们至少可以更轻松地插入您自己的日志记录方法..
  • 完美 +1... 帮助我发现无点解析器在两个“;;”上阻塞在 css 中连续!现在愉快地捆绑最小化的无点 css :)
【解决方案2】:

我今天刚刚在我的RequestReduce 项目中遇到了这个问题。我得到了更少的空白 - > css 转换,因为有解析错误似乎进入了以太。感谢 qes 的回答,我能够找到一个解决方案,我可以将错误写入响应流。这是我的 dotless.Core.Loggers.ILogger:

public class LessLogger : ILogger
{
    public void Log(LogLevel level, string message)
    {
    }

    public void Info(string message)
    {
    }

    public void Debug(string message)
    {
    }

    public void Warn(string message)
    {
    }

    public void Error(string message)
    {
        Response.Write(message);
    }

    public HttpResponseBase Response { get; set; }
}

我将它传递到发送到 EngineFactory 的配置中:

            var engine = new EngineFactory(new DotlessConfiguration
                                               {
                                                   CacheEnabled = false,
                                                   Logger = typeof (LessLogger)
                                               }
                ).GetEngine();

出于单元测试的目的,我想传入会写入错误的 HttpResponseBase。这就是我觉得通过一些讨厌的转换来获得对我的记录器的引用的事情变得丑陋的地方:

            ((LessLogger)((LessEngine)((ParameterDecorator)engine).Underlying).Logger).Response = response;

我希望这会有所帮助,如果有人知道更优雅的方式来获取对记录器的引用,请告诉我。

【讨论】:

  • LessLogger 类放在哪里?
【解决方案3】:

您可以使用 web.config 轻松完成此操作。在无点配置部分中,添加以下内容:logger="dotless.Core.Loggers.AspResponseLogger"。这将使无点输出错误而不是空白 css。

我已将以下内容作为示例。 (“...”代表 web.config 中的现有内容)。在我下面的示例中,缓存设置为 false。这对于调试目的很有用。正常情况下应该设置为true。

<configuration>    
     <configSections>
           ...
          <section name="dotless" type="dotless.Core.configuration.DotlessConfigurationSectionHandler,dotless.Core" />
      </configSections>

      <dotless minifyCss="false" cache="false" 
            logger="dotless.Core.Loggers.AspResponseLogger" />
       ...    
</configuration>    

【讨论】:

  • 我添加了如下所示的行 什么都不做给我?
  • +1 这行得通 - 如果使用 SquishIt 之类的东西,请确保您直接在浏览器(或其他)中引用较少的文件来查看错误
【解决方案4】:

为了其他人的利益,@tony722 的解决方案可以在您简单地从您的页面中引用 .less 文件时使用。

但是如果你直接调用Less.Parse,这个方法会把任何错误写入Response

var lessConfig = new DotlessConfiguration { Logger = typeof(AspResponseLogger) };
string css = Less.Parse(someInput, lessConfig);

【讨论】:

    【解决方案5】:

    这会记录到 VS 中的输出窗口:

    var config = dotless.Core.configuration.DotlessConfiguration.GetDefault();
    config.Logger = new dotless.Core.Loggers.DiagnosticsLogger(dotless.Core.Loggers.LogLevel.Debug).GetType();
    config.MinifyOutput = minified;
    css= Less.Parse(css, config);
    

    【讨论】:

    • 是这个解决方案本身。它是否需要像以前的记录器一样实现记录器?
    【解决方案6】:

    我在dotless周围使用了一个包装类,如下:

    public class LessParser : IStylizer
    {
        public string ErrorFileName { get; private set; }
        public int ErrorLineNumber { get; private set; }
        public int ErrorPosition { get; private set; }
        public string ErrorMessage { get; private set; }
    
        string IStylizer.Stylize(Zone zone)
        {
            ErrorFileName = zone.FileName;
            ErrorLineNumber = zone.LineNumber;
            ErrorPosition = zone.Position;
            ErrorMessage = zone.Message;
    
            return String.Empty;
        }
    
        public string Compile(string lessContent, string lessPath)
        {
            var lessEngine = new EngineFactory(new DotlessConfiguration
            {
                CacheEnabled = false,
                DisableParameters = true,
                LogLevel = LogLevel.Error,
                MinifyOutput = true
            }).GetEngine();
    
            lessEngine.CurrentDirectory = lessPath;
    
            /* uncomment if DisableParameters is false
            if (lessEngine is ParameterDecorator)
                lessEngine = ((ParameterDecorator)lessEngine).Underlying;
            */
    
            /* uncomment if CacheEnabled is true
            if (lessEngine is CacheDecorator)
                lessEngine = ((CacheDecorator)lessEngine).Underlying;
            */
    
            ((LessEngine)lessEngine).Parser.Stylizer = this;
    
            return lessEngine.TransformToCss(lessContent, null);
        }
    
        public FileInfo SyncCss(FileInfo lessFile)
        {
            var cssFile = new FileInfo(
                lessFile.FullName.Substring(0, lessFile.FullName.Length - lessFile.Extension.Length) + ".css");
    
            if (!cssFile.Exists || cssFile.LastWriteTimeUtc < lessFile.LastWriteTimeUtc)
            {
                string cssContent = Compile(ReadFileContent(lessFile), lessFile.DirectoryName);
    
                if (String.IsNullOrEmpty(cssContent))
                    return null;
    
                using (var stream = cssFile.Open(FileMode.Create))
                using (var writer = new StreamWriter(stream, Encoding.UTF8))
                {
                    writer.Write(cssContent);
                }
            }
    
            return cssFile;
        }
    
        public string ReadFileContent(FileInfo file)
        {
            using (var reader = file.OpenText())
            {
                return reader.ReadToEnd();
            }
        }
    }
    

    诀窍是使用自己的IStylizer 接口实现,在遇到解析错误时调用该接口来格式化生成的错误消息。这使我们能够捕获离散的错误片段,这与错误已经是格式化文本的ILogger 接口的实现不同。

    var parser = new LessParser();
    var lessFile = new FileInfo("C:\\temp\\sample.less"));
    var cssFile = parser.SyncCss(lessFile);
    
    if (cssFile != null)
        Console.WriteLine(parser.ReadFileContent(cssFile));
    else
        Console.WriteLine("Error '{3}' in {0}, line {1}, position {2}",
            parser.ErrorFileName, parser.ErrorLineNumber, parser.ErrorPosition, parser.ErrorMessage);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多