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 < and >. 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:
- Characters need to be properly escaped
- 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 < 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; }); }