Push C# (Razor) objects to JavaScript array of objects

If you wonder, how to push C# objects to a JavaScript array in ASP.NET View using Razor, you came to the right place. 🙂 In this post I will tell you, how I achieved it.

Lets say I have a collection List<Category>. Object Category consists of parameters id, category name and list of subcategories (also of type Category):

public class Category {
  public int Id { get; set; }

  public string Name { get; set; }

  public List<Category> Subcategories { get; set; }
}

List<Category> is also my strongly typed View’s Model. My goal is to achieve Javascript array like this: [{“id”:…, “name”:”…”, “subcategories”:[{“id”:…, “name”:”…”}]},{…}]. In order to create objects like that, I must firstly create collection of anonymous objects and serialize it to string.

@{
  //serializer initialization
  System.Web.Script.JavascriptSerializer js = new System.Web.Script.JavascriptSerializer();

  var data = Model.Select(c => new {
    id = c.Id,
    name = c.Name,
      subcategories = c.Subcategories.Select(cc => new {
      id = cc.Id,
      name = cc.Name
    })
  });

  //serialize collection of anonymous objects
  string arrStr = js.Serialize(data);
}

Now I have to inject this string to JavaScript:

var categories = JSON.parse('@arrStr'.replace(/&quot;/g, '"'));

As you can see, I injected string using Razor. I also used replace function in order to replace all &quot; occurances with " (double quotes).

Warning: JavaScript replace function replaces only first occurrance of searched pattern. In order to replace all occurrances I had to use regular expression with flag g, which is used to perform a global match.

The last thing to do is to call JSON.parse, which converts properly processed string into JavaScript object. Now we have JavaScript array of desired objects, which can be used further in our JavaScript functions/events.

How to override IIS default error messages?

Today I was working on retrieving status codes in ASP.NET MVC 4 application. I needed correct status codes for my ajax requests to show correct error message (in case of an error, of course). The problem was that IIS was redirecting when status code was added to response. In case of status code 401 it redirected to basic login, in case of status code 403 it redirected to IIS generic error page and so on. If you are working with .NET version 4.5 there exists very simple solution:

Response.TrySkipIisCustomErrors = true;

But not with .NET version 4.0. 🙂 In that case you have to add few lines into your Web.config file inside of system.webServer node:

<httpErrors existingResponse="PassThrough">
  <remove statusCode="403"/>
  <error statusCode="403" responseMode="ExecuteURL" path="~/StatusCode/Unauthorized"/>
  <remove statusCode="401"/>
  <error statusCode="401" responseMode="ExecuteURL" path="~/StatusCode/Forbidden"/>
  <remove statusCode="404"/>
  <error statusCode="404" responseMode="ExecuteURL" path="~/StatusCode/NotFound"/>
</httpErrors>

With attribute value “PassThrough” (attribute existingResponse) you tell IIS to leave response untouched if an existing response exists. You can check other attribute options here. Value of attribute path inside of node error represents the path to the response (for example: “~/ControllerName/ActionName”). That way you can override IIS’ default error responses with your custom error responses.

How to get user names and surnames and show them in ASP.NET MVC 4 autocomplete?

Today I was working on some autocompletion with users (name + surname) from Active Directory (from now on AD) in ASP.NET MVC 4 application. I wanted to get suggestions on the fly based on user’s input. I was accessing AD using Lightweight Directory Access Protocol (from now on LDAP).

You have to add two references into your project: System.DirectoryServices.dll and System.DirectoryServices.AccountManagement.dll. In your class you have to use two namespaces:

using System.DirectoryServices.AccountManagement;
using System.DirectoryServices;
In your controller you do something like this:
public JsonResult AutocompleteUsers(string term)
{
    string filter = "(&(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))(|(givenName={0}*)(sn={1}*)))";
    List persons = new List();

    DirectoryEntry dE = new DirectoryEntry(); //initialization
    {
        //uniquely identifies entry in a networked environment
        dE.Path = "directory entry path"; 

        //username for client authentication
        dE.Username = "directory entry username"; 
        
        //password for client authentication
        dE.Password = "directory entry password";

        //authentication types
        dE.AuthenticationType = AuthenticationTypes.ServerBind; 
    }
    
    DirectorySearcher dS = new DirectorySearcher(dE); //applying search filter
    {
        dS.Filter = String.Format(filter, term, term);
    }

    foreach (SearchResult result in dS.FindAll())
    {
        if (result != null)
        {
            DirectoryEntry entry = result.GetDirectoryEntry();
            
            string name = (entry.Properties["givenName"].Value ?? "").ToString();
            string surname = (entry.Properties["sn"].Value ?? "").ToString();

            if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(surname)) 
            {
                persons.Add(name + " " + surname);
            }
        }
    }

    IEnumerable ipersons = persons.AsEnumerable().OrderBy(p => p);

    return this.Json(ipersons, JsonRequestBehavior.AllowGet);
}
So, firstly you initialize connection with AD. After that you apply search filter. Part (objectCategory=person) returns user and contact objects. Because we only want users, we also need part (!userAccountControl:1.2.840.113556.1.4.803:=2). Contacts don’t have userAccountControl attribute, so this part will always be True for them. With part (objectClass=user) we restrict query to just user object. Part (|(givenName={0}*)(sn={1}*))) restricts query on users which name or surname starts with specified string. After that we loop through search results and get the names.
Collection of names is returned as Json. We must allow get with option JsonRequestBehavior.AllowGet. By default “Json get” is not allowed and without mentioned option exception is produced (Internal Server Error).On client site we have to attach following JavaScript code:

$(document).ready(function () {
    $('#textbox_id').autocomplete({
	source: '@Url.Action("action_name", "controller_name")'
    });
});
Textbox value (inserted string) is automaticaly sent in query string as parameter term. We must not forget to import jQuery scripts:
@Styles.Render("~/Content/themes/base/css")
@Scripts.Render("~/bundles/jquery")    
@Scripts.Render("~/bundles/jqueryui")

Without that callback won’t happen. 🙂