2011-01-25 18 views
12

Ho scritto un metodo di azione ASP.NET MVC che riceve un nome file .less, lo elabora tramite Less.Parse(<filename>) e restituisce il file css elaborato.Come posso generare errori quando si utilizza .less programmaticamente?

Questo funziona bene finché il codice .less è valido, ma se c'è un errore, dotLess restituisce semplicemente una stringa vuota. Quindi, se c'è un errore nell'elaborazione del file, il mio metodo di azione restituisce un file css vuoto.

Come è possibile visualizzare un messaggio di errore con una descrizione più dettagliata dell'errore di sintassi?

+0

Hai visto SquishIt? http://www.codethinked.com/post/2010/05/26/SquishIt-The-Friendly-ASPNET-JavaScript-and-CSS-Squisher.aspx –

+0

@qstarin: Grazie, ma sto già usando Combres (http: //combres.codeplex.com/) per scopi di produzione e le combres possono fare tutto ciò che fa lo squishit (più un po 'di più). Ma ho ancora bisogno della risposta alla mia domanda di cui sopra per scopi di sviluppo. Il motivo è che non mi piace lavorare con un download combinato di tutti i miei file CSS, preferisco invece file separati. E tutto funziona bene, tranne che per i messaggi di errore ... –

+0

Sono curioso, quali utili funzionalità fornisce a SquishIt le funzionalità di SquishIt? Guardando nella documentazione di Combres sembra che facciano lo stesso compito. Squish, tuttavia, include dotLess nella sua elaborazione. Questo è il motivo per cui l'ho menzionato, perché non avresti bisogno di un'azione separata per eseguire la compilazione dotLess. –

risposta

14

Il parser dotLess intercetta le eccezioni e le restituisce a un Logger. Il frammento dalla fonte di errati privi di punti che esegue questa è 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 ha un sovraccarico che prende un oggetto DotlessConfiguration, che prevede diverse proprietà che è possibile utilizzare:

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

si noterà che il Logger proprietà è di tipo Type. Qualunque sia il tipo fornite deve implementare 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); 
} 

Come abbiamo visto nel primo frammento di, il metodo Error sul logger verrà chiamato quando viene rilevato un errore durante l'analisi.

Ora, l'unico punto importante di tutto questo è il modo in cui un'istanza del tipo che implementa ILogger viene istanziata. DotLess utilizza internamente un contenitore IoC cotto nella DLL. Seguendo le chiamate al metodo, sembra che alla fine chiamerà Activator.CreateInstance per creare un'istanza di ILogger.

Spero che questo sia almeno in qualche modo utile.

+0

Eccellente. Molte grazie! :-) –

+1

Dovrei davvero rielaborare l'API del logger un po '.. Al momento della scrittura aveva senso solo con gli usi interni in mente .. ma per i chiamanti esterni questo è piuttosto brutto .. – Tigraine

+0

@Tigraine: spero almeno Ho spiegato correttamente come catturare gli eventi di registrazione, ero un po 'insicuro se avessi risolto tutto, in particolare quali erano le ramificazioni di specificare un oggetto DotlessConfiguration con solo la classe di registrazione compilata. Inoltre, se stai rielaborando questo , Vorrei puntare a SimpleLoggingFramework o Common.Logging come due potenziali facciate ai sistemi di registrazione, anche se onestamente può arrivare a fare casino con tutte le dipendenze .... buona fortuna. heh –

5

Ho appena affrontato questo oggi nel mio progetto RequestReduce. Stavo diventando meno bianco -> css trasforma perché c'erano errori di analisi che sembravano andare nell'etere. Grazie alla risposta di qes sono riuscito a trovare una soluzione in cui scrivere gli errori nel flusso di risposta. Qui è la mia 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; } 
} 

passo questo nella configurazione inviato al EngineFactory:

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

A scopo di verifica delle unità volevo passare nel mio HttpResponseBase che scrivere l'errore. Questo è dove ho sentito cose ottenere brutto con qualche brutta fusione per ottenere un riferimento al mio logger:

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

Spero che questo aiuta e se qualcuno sa di un modo più elegante per ottenere un riferimento allo strumento, si prega di fammi sapere.

+0

Dove inserisci la classe 'LessLogger'? – guanome

5

È possibile farlo molto facilmente con web.config. Nella sezione di configurazione senza punti, aggiungere quanto segue: logger="dotless.Core.Loggers.AspResponseLogger". Questo renderà in uscita senza errori gli errori invece dei css vuoti.

Ho incluso il seguente esempio. ("..." rappresenta materiale esistente nel tuo web.config). Nel mio esempio qui sotto la cache è impostata su false. Questo è utile per scopi di debug.Probabilmente dovrebbe essere impostato su true in circostanze normali.

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

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

Ho aggiunto la riga che appare come segue Non fa niente per me? – Jacques

+0

Non sei sicuro del motivo per cui questo è contrassegnato come risposta? –

+1

+1 Funziona - se usi qualcosa come SquishIt, assicurati di fare riferimento ai file meno direttamente nel tuo browser (o qualsiasi altra cosa) per vedere gli errori. – davidsleeps

0

Per il bene degli altri, la soluzione @ di tony722 funziona se semplicemente riferimento a file .less dalle pagine.

Ma se si chiama Less.Parse direttamente, questo metodo scriverà ogni errore in Response:

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

Questo registra alla finestra di uscita 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); 
+0

è questa soluzione a parte. Richiede che un logger sia implementato come i precedenti? – petersmm

1

Sto usando una classe wrapper intorno errati privi di punti , come segue:

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

Il trucco è usare il proprio impleme informazione dell'interfaccia IStylizer chiamata in caso di errore di analisi per formattare il messaggio di errore risultante. Ciò ci consente di acquisire parti discrete dell'errore, a differenza dell'implementazione dell'interfaccia ILogger in cui l'errore è già un testo formattato.

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); 
Problemi correlati