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

发布于 2024-10-14 02:58:10 字数 244 浏览 8 评论 0原文

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

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

如何输出包含更详细的语法错误描述的错误消息?

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.

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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

真心难拥有 2024-10-21 02:58:10

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

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

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:

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:

            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:

            ((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.

你好,陌生人 2024-10-21 02:58:10

您可以使用 web.config 轻松完成此操作。在 dotless 配置部分中,添加以下内容: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>    

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.

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.

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

      <dotless minifyCss="false" cache="false" 
            logger="dotless.Core.Loggers.AspResponseLogger" />
       ...    
</configuration>    
夕色琉璃 2024-10-21 02:58:10

我正在使用 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);

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.

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);
北方。的韩爷 2024-10-21 02:58:10

为了他人的利益,如果您只是从页面引用 .less 文件,@tony722 的解决方案就可以工作。

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

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

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

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

var lessConfig = new DotlessConfiguration { Logger = typeof(AspResponseLogger) };
string css = Less.Parse(someInput, lessConfig);
浪推晚风 2024-10-21 02:58:10

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

This logs to output window in 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);
路还长,别太狂 2024-10-21 02:58:10

这会将所有 LESS 解析错误记录到调试控制台:

var config = dotless.Core.configuration.DotlessConfiguration.GetDefault();
config.Logger = new dotless.Core.Loggers.ConsoleLogger(dotless.Core.Loggers.LogLevel.Debug).GetType();

var lessCSS = Less.Parse("your css source", config);

This will log any LESS parsing errors to the debug console:

var config = dotless.Core.configuration.DotlessConfiguration.GetDefault();
config.Logger = new dotless.Core.Loggers.ConsoleLogger(dotless.Core.Loggers.LogLevel.Debug).GetType();

var lessCSS = Less.Parse("your css source", config);
一腔孤↑勇 2024-10-21 02:58:10

dotLess 解析器捕获异常并将其输出到记录器。执行此操作的 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 有一个重载,它接受一个 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);
}

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

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

我希望这至少有一些帮助。

The dotLess parser traps Exceptions and outputs them to a Logger. The snippet from dotLess's source that performs this is 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:

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:

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.

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.

I hope this is at least somewhat helpful.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文