如何以编程方式使用.less时输出错误?

时间:2022-03-20 00:12:09

I've written an ASP.NET MVC action method that receives a .less file name, processes it via Less.Parse(<filename>) and outputs the processed css file.

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

This works fine as long as the .less code is valid, but if there is an error, dotLess just returns an empty string. So if there is an error processing the file, my action method returns an empty css file.

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

How can I output an error message with a closer description of the syntax error instead?

如何输出错误消息,更详细地描述语法错误?

6 个解决方案

#1


14  

The dotLess parser traps Exceptions and outputs them to a Logger. The snippet from dotLess's source that performs this is LessEngine.TransformToCss:

dotLess解析器捕获异常并将它们输出到Logger。 dotLess执行此操作的源代码片段是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 has an overload that takes a DotlessConfiguration object, which provides several properties that you can use:

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

You will notice that the Logger property is of type Type. Whatever type you supply must implement dotless.Core.Loggers.ILogger:

您会注意到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);
}

As we saw in the first snippet, the Error method on the logger will get called when an error is encountered during parsing.

正如我们在第一个片段中看到的那样,在解析过程中遇到错误时,将调用记录器上的Error方法。

Now, the one sticky point of all this is how exactly an instance of the type that implements ILogger gets instantiated. Internally, dotLess uses an IoC container that is baked into the DLL. Following the method calls, it appears that it will eventually call Activator.CreateInstance to instantiate your ILogger.

现在,所有这一点的一个关键点是如何实现ILogger的类型实例的实例化。在内部,dotLess使用一个烘焙到DLL中的IoC容器。在方法调用之后,它似乎最终将调用Activator.CreateInstance来实例化您的ILogger。

I hope this is at least somewhat helpful.

我希望这至少有点帮助。

#2


5  

I just faced this today in my RequestReduce project. I was getting blank less -> css transforms because there were parse errors that appeared to be going into the ether. Thanks to qes's answer I was able to work out a solution where I could write the errors to the response stream. Here is my dotless.Core.Loggers.ILogger:

我今天刚刚在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; }
}

I pass this into the Configuration sent to the EngineFactory:

我把它传递给发送到EngineFactory的配置:

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

For unit testing purposes I wanted to pass in my HttpResponseBase that would write the error. This is where I felt things getting ugly with some nasty casting to get a reference to my logger:

出于单元测试的目的,我想传入我的HttpResponseBase来编写错误。这是我觉得事情变得丑陋与一些讨厌的演员来获得我的记录器的参考:

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

I hope this helps out and if someone knows of a more elegant way to get a reference to the logger, please let me know.

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

#3


5  

You can do this very easily with web.config. In your dotless configuration section, add the following: logger="dotless.Core.Loggers.AspResponseLogger". This will make dotless output the errors instead of blank css.

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

I've included the following as an example. ("..." represents existing stuff in your web.config). In my example below cache is set to false. This is useful for debugging purposes. It should probably be set to true under normal circumstances.

我已经将以下内容作为示例。 (“...”表示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>    

#4


1  

I am using a wrapper class around dotless, as follows:

我正在使用无点的包装类,如下所示:

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

The trick is to use own implementation of IStylizer interface that is called upon encountering a parse error to format the resulting error message. This allows us to capture discrete pieces of the error, unlike implementation of ILogger interface where the error is already a formatted text.

诀窍是使用自己的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);

#5


0  

For the benefit of others, @tony722's solution works if you simply reference .less files from your pages.

为了他人的利益,@ tony722的解决方案适用于您只是从您的页面引用.less文件。

But if you call Less.Parse directly, this method will write any error into Response:

但是如果直接调用Less.Parse,此方法会将任何错误写入Response:

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

#6


0  

This logs to output window in VS:

这会记录到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);

#1


14  

The dotLess parser traps Exceptions and outputs them to a Logger. The snippet from dotLess's source that performs this is LessEngine.TransformToCss:

dotLess解析器捕获异常并将它们输出到Logger。 dotLess执行此操作的源代码片段是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 has an overload that takes a DotlessConfiguration object, which provides several properties that you can use:

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

You will notice that the Logger property is of type Type. Whatever type you supply must implement dotless.Core.Loggers.ILogger:

您会注意到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);
}

As we saw in the first snippet, the Error method on the logger will get called when an error is encountered during parsing.

正如我们在第一个片段中看到的那样,在解析过程中遇到错误时,将调用记录器上的Error方法。

Now, the one sticky point of all this is how exactly an instance of the type that implements ILogger gets instantiated. Internally, dotLess uses an IoC container that is baked into the DLL. Following the method calls, it appears that it will eventually call Activator.CreateInstance to instantiate your ILogger.

现在,所有这一点的一个关键点是如何实现ILogger的类型实例的实例化。在内部,dotLess使用一个烘焙到DLL中的IoC容器。在方法调用之后,它似乎最终将调用Activator.CreateInstance来实例化您的ILogger。

I hope this is at least somewhat helpful.

我希望这至少有点帮助。

#2


5  

I just faced this today in my RequestReduce project. I was getting blank less -> css transforms because there were parse errors that appeared to be going into the ether. Thanks to qes's answer I was able to work out a solution where I could write the errors to the response stream. Here is my dotless.Core.Loggers.ILogger:

我今天刚刚在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; }
}

I pass this into the Configuration sent to the EngineFactory:

我把它传递给发送到EngineFactory的配置:

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

For unit testing purposes I wanted to pass in my HttpResponseBase that would write the error. This is where I felt things getting ugly with some nasty casting to get a reference to my logger:

出于单元测试的目的,我想传入我的HttpResponseBase来编写错误。这是我觉得事情变得丑陋与一些讨厌的演员来获得我的记录器的参考:

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

I hope this helps out and if someone knows of a more elegant way to get a reference to the logger, please let me know.

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

#3


5  

You can do this very easily with web.config. In your dotless configuration section, add the following: logger="dotless.Core.Loggers.AspResponseLogger". This will make dotless output the errors instead of blank css.

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

I've included the following as an example. ("..." represents existing stuff in your web.config). In my example below cache is set to false. This is useful for debugging purposes. It should probably be set to true under normal circumstances.

我已经将以下内容作为示例。 (“...”表示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>    

#4


1  

I am using a wrapper class around dotless, as follows:

我正在使用无点的包装类,如下所示:

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

The trick is to use own implementation of IStylizer interface that is called upon encountering a parse error to format the resulting error message. This allows us to capture discrete pieces of the error, unlike implementation of ILogger interface where the error is already a formatted text.

诀窍是使用自己的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);

#5


0  

For the benefit of others, @tony722's solution works if you simply reference .less files from your pages.

为了他人的利益,@ tony722的解决方案适用于您只是从您的页面引用.less文件。

But if you call Less.Parse directly, this method will write any error into Response:

但是如果直接调用Less.Parse,此方法会将任何错误写入Response:

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

#6


0  

This logs to output window in VS:

这会记录到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);