Donkere uithoeken van .NET
De uitdaging
Voor het bouwen van een maatwerk web-portal voor een van onze klanten, kwam ik tot de ogenschijnlijke kleine uitdaging om een stuk java-script aan te maken in C# code. Piece of cake… Totdat er een new-line in de java-script kwam te staan, die de browser niet (zomaar) slikte.
In het voorbeeld dat ik hieronder aanhaal gebruik ik het ‘OnClientClick’ event van een Button. Daar komt ter bevestiging een “Weet u het zeker” melding in te staan, alvorens de server-side event afgaat. Het OnClientClick event is eigenlijk een string property die gevuld mag worden met java-script. Voor een “Weet u het zeker” melding zou daar het volgende in kunnen komen te staan:
return confirm('Weet u het zeker.');
Als in de confirmatietekst een new-line (“\r\n”) staat, dan gaat het niet zomaar lukken. De new-line moet ge-escaped worden naar:
return confirm('Weet u het zeker.\\r\\nAlle data gaat verloren');
De eerste oplossing
In eerste instantie zegt mijn intuïtie dat ASP.NET vast ergens wel een method heeft die dit voor me doet. Zo is er de HttpServerUtility.HtmlEncode, en de HttpServerUtility.UrlEncode. Deze zijn hier echter niet voor geschikt.
In een vorig project werd het opgelost door middel van enkele string-replace aanroepen. Zie onderstaand voorbeeld:
/// <summary>
/// Build a javascript string, to be used in a javascript.|
/// </summary>
/// <param>The raw value to create a javascript string for.</param>
/// <returns>The javascript string.</returns>
private static string BuildJavascriptString(string rawValue)
{
string escapedValue =
rawValue
.Replace("\\", "\\\\")
.Replace("\'", "\\\'")
.Replace("\"", "\\\"")
.Replace("\r", "\\r")
.Replace("\n", "\\n");
return string.Format("'{0}'", escapedValue);
}
Het gebruik van deze method ziet er als volgt uit:
MyButton.OnClientClick = string.Format("return confirm({0});",
BuildJavascriptString(confirmMessage));
De echte oplossing
Het is en blijft echter vreemd dat zoveel string-replace aanroepen nodig zijn. Het zou wellicht op te lossen zijn met een regular expression, maar zelfs dan zit ik nog op het verkeerde spoor. En zijn nu alle varianten afgedekt? Blijft dit werken in de toekomst? Dit moet toch beter kunnen!
Ergens in een donkele hoek van alle Microsoft assemblies in de GAC, staat de Microsoft.JScript assembly. Laat deze nou toevallig een method hebben die lijkt op de java-script escape method. Door de method BuildJavascriptString te vervangen door onderstaande, weet ik zeker dat ik er nooit meer naar om hoef te kijken.
/// <summary>
/// Build a javascript string, to be used in a javascript.
/// </summary>
/// <param>The raw value to create a javascript string for.</param>
/// <returns>The javascript string.</returns>
private static string BuildJavascriptString(string rawValue)
{
return string.Format("unescape('{0}')",
Microsoft.JScript.GlobalObject.escape(rawValue));
}
Bovenstaande method is overigens in ons framework geadopteerd, met een uitbreiding voor booleans, integers, doubles en nullables:
/// <summary>
/// Convert a value, regardless of it's type, to be used in a javascript.
/// </summary>
/// <typeparam>The type of the parameter.</typeparam>
/// <param>The value to convert.</param>
/// <returns>A javascript string which represents the specified value.</returns>
public static string ConvertValueToJavaScript<T>(T value)
{
string valueAsJavaScriptString = string.Empty;
if (value == null)
valueAsJavaScriptString = "null";
else if (typeof(T) == typeof(bool))
valueAsJavaScriptString =
value.ToString().ToLowerInvariant();
else if (typeof(T) == typeof(string))
valueAsJavaScriptString =
"unescape('{0}')".FormatInvariant(
Microsoft.JScript.GlobalObject
.escape(value as string));
else
valueAsJavaScriptString = Convert.ToString(value,
CultureInfo.InvariantCulture);
return valueAsJavaScriptString;
}
Java-script syntax verifiëren met C#
In diezelfde donkere uithoek zit ook een java-script compiler, die run-time gebruikt kan worden om java-script te complileren en verifiëren. Hiermee is het mogelijk om een unit-test te schrijven, die alle java-script bestanden opzoekt in het project, en verifieërd. Maar voor deze vonst gaan de credits naar Mads Kristensen, zie zijn blog-post: Verify JavaScript syntax using C#






