How to create a properly escaped JavaScript string in Razor

I am working on a project in which I have a Razor view where I ouput JavaScript code calling a server method with ajax (using jQuery). It looks like this:

$(document).ready(function() {
	var inputVars =
	{
		"input": @Model.MyText
	};
	$.ajax({
		async: false,
		type: "POST",
		url: '@Url.Action("MyMethod", "MyController", new {area = "MyArea"})',
		dataType: "json",
		traditional: true,
		data: inputVars
	}).done(function(result) {
		if (result) {
			// Do something
		} else {
			// Log or show an error message
		}
		return false;
	});
}

The problem is that if MyText contains backslashes it doesn’t work. So I need to escape them. I just did it with a string replace. Then I saw that it also doesn’t work with newlines. So I escaped them as well. Then I noticed that it was also replacing < and > by &lt; and &gt;. So my list of string replaces was growing and growing. So I decided to create an helper class handling this. It has to solve two problems:

  1. Characters need to be properly escaped
  2. Since we’re not writing HTML but JavaScript code, we need to tell Razor not to additionally escape the string

For the first one, you just need to go through the input string and replace each character which needs escaping by a proper escape sequence.
For the second one, you need to return an IHtmlString to tell Razor that the string is already properly escaped and doesn’t require additional escaping.

The resulting helper looks like this:

using System.Text; // Required for StringBuilder
using System.Web;  // Required for IHtmlString

namespace Benohead.Helpers
{
	/// <summary>
	///  A class to help you output JavaScript code in a Razor view.
	/// </summary>
    public class BenoheadJavaScript
    {
        /// <summary>
        /// Encodes a c# string to a string which can be used in JavaScript code escaping the relevant characters
        /// </summary>
        /// <param name="input">the string to escape</param>
        /// <returns>a string which can be used in JavaScript code</returns>
        public static IHtmlString EncodeJavaScriptString(string input)
        {
            StringBuilder builder = new StringBuilder();
			// Open the double quotes
            builder.Append("\"");
			// Then add each character properly escaping them
            foreach (char c in input)
            {
                switch (c)
                {
					//First check whether it's one of the defined escape sequences
					case '\'': //single quote
						builder.Append("\\\'");
						break;
					case '\"': //double quote
						builder.Append("\\\"");
						break;
					case '\\': //backslash
						builder.Append("\\\\");
						break;
					case '\0': //Unicode character 0
						builder.Append("\\0");
						break;
					case '\a': //Alert (character 7)
						builder.Append("\\a");
						break;
					case '\b': //Backspace (character 8)
						builder.Append("\\b");
						break;
					case '\f': //Form feed (character 12)
						builder.Append("\\f");
						break;
					case '\n': //New line (character 10)
						builder.Append("\\n");
						break;
					case '\r': //Carriage return (character 13)
						builder.Append("\\r");
						break;
					case '\t': //Horizontal tab (character 9)
						builder.Append("\\t");
						break;
					case '\v': //Vertical quote (character 11)
						builder.Append("\\v");
						break;
					default:
						// If it's none of the defined escape sequences, convert the character to an int and check the code
						int i = (int)c;
						if (i >= 32 && i <= 127)
						{
							// if it's a displayable ASCII character, just write the character
							builder.Append(c);
						}
						else
						{
							// otherwise write the Unicode escape sequence for the character with hex value
							builder.AppendFormat("\\u{0:X04}", i);
						}
						break;
                }
            }
 			// Close the double quotes
           builder.Append("\"");
			// You have to return an IHtmlString otherwise an HTML escape will be performed e.g. < will be replaced by &lt;
            return new HtmlString(builder.ToString());
        }
    }
}

In order to use it, you need to add a using at the top of your cshtml file:

@using Benohead.Helpers

And wrap the reference to the model which are to be written as JavaScript strings with a call to EncodeJavaScriptString e.g.:

$(document).ready(function() {
	var inputVars =
	{
		"input": @BenoheadJavaScript.EncodeJavaScriptString(Model.MyText)
	};
	$.ajax({
		async: false,
		type: "POST",
		url: '@Url.Action("MyMethod", "MyController", new {area = "MyArea"})',
		dataType: "json",
		traditional: true,
		data: inputVars
	}).done(function(result) {
		if (result) {
			// Do something
		} else {
			// Log or show an error message
		}
		return false;
	});
}

Leave a Reply

Your email address will not be published. Required fields are marked *