Shared Client and Server configuration with custom ActionResult – Solving it in a DRY way

I’m developing a MVC application and there are some configurations I want to use both on client and server side but I don’t want to replicate them, I want it be DRY (Don’t Repeat Yourself). Having it gives me the hability of changing some value on both sides without deploying the application again.

I could have created my properties in my AppSettings section and then print the variables on a page, but the AppSettings would become huge and the page header a mess.

Here is the solution I end up with. I created a JSON file containing my propeties, like so:

common.json

{
	"highlightProductId": 5,
	"bannerEnabled": false,
	"passwordLength": 15,
	"adURL": "http://google.com/ad"
}

That allows me to use the Newtonsoft or any other JavaScript parser like the JavaScriptDeserializer from .Net and have that JSON as a class. You can also deserialize it as “dynamic” if you are using the Newtonsoft library.

Well, that solves the problem for the server side. The client side though would have to call jQuery.getJSON to be able to read that file but that slows the page loading down and doesn’t give the user a nice experience because the user has to wait until the ajax call gets finished, so it can continue processing the rest of the script.

What I did was to turn that JSON into a JS file. To do that, you just have to assign that JSON to a variable and then you can embed the result in the page as a JavaScript file!

var common = { };

That is a valid JS file!

Now let’s teach MVC how to do that. Create the file below:

namespace System.Web.Mvc
{
    using System;

    public class JsonToJavaScriptResult : ActionResult
    {
        public string Prefix { get; private set; }
        public string JsonFile { get; set; }

        public JsonToJavaScriptResult(string prefix, string jsonFile)
        {
            JsonFile = jsonFile;
            Prefix = prefix;
        }

        public override void ExecuteResult(ControllerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = "application/x-javascript";

            response.Write(Prefix);            
            response.WriteFile(JsonFile);
        }
    }
}

There we are creating a custom ActionResult that concatenates the prefix – which are the part that will hold our variable name – with the content of the file – our JSON – and writes it all to the page buffer.

Now let’s make our Controller that will use the newly created class.

    public class SharedController : Controller
    {        
        //
        // GET: /Shared/Script

        public JsonToJavaScriptResult Script()
        {
            const string fileName = "common.json"
            const string prefix = "var Shared = ";
            var jsonFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);

            return new JsonToJavaScriptResult(prefix, jsonFile);
        }
    }

The last step is just insert the script tag in your page that will embed our code.

<script src="@Url.Action("Script", "Shared")" type="application/x-javascript"></script>

and you can do something like that

<script type="text/javascript">
  alert(Shared.highlightProductId);
</script>

Any new ideas or improvements are welcome.

Advertisements

Working with Configuration Files – List Items

Continuing my posts about configuration files, now I’m going to show you how to work with a list of items.

Here is the xml:

<?xml version="1.0" encoding="utf-8" ?>
<main>
  <enabled>true</enabled>
  <name>Example text name</name>
  <hosts>
    <host ip="127.0.0.1" port="80" username="test" password="pass" />
    <host ip="172.168.0.1" port="80" username="test2" password="pass2" />
  </hosts>
</main>

Is basically the same xml file I was using in the previous post but now hosts is a list and not only a single entry.

So, let’s see how we handle it in our code:

[XmlRoot("main")]
public class ModelExampleConfiguration
{
    public ModelExampleConfiguration()
    {
        this.Hosts = new List<HostConfiguration>();
    }
        
    [XmlElement("enabled")]
    public bool Enabled { get; set; }

    [XmlElement("name")]
    public string Name { get; set; }

    [XmlArray("hosts")]
    [XmlArrayItem("host")]
    public List<HostConfiguration> Hosts { get; set; }
}

This is important! Note that the list had to be instantiated in the constructor so that the .NET can add the items into the list. Also, it was used to new attributes: XmlArray and XmlArrayItem. The first one defines the name of the list, and the second the name of the items of the list.

You might also need a list where you don’t have a parent item. Let’s see an example:

<?xml version="1.0" encoding="utf-8" ?>
<main>
  <enabled>true</enabled>
  <name>Example text name</name>
  <hosts>
    <host ip="127.0.0.1" port="80" username="test" password="pass" />
    <host ip="172.168.0.1" port="80" username="test2" password="pass2" />
  </hosts>
  <users groupName="admin">
    <user name="user1" />
    <user name="user2" />
  </users>
  <users groupName="guests">
    <user name="user3" />
    <user name="user4" />
  </users>
</main>

I add those two groups of users and each group of user has a list of users. Our full code then will look like:

using System.Xml.Serialization;

namespace ConsoleApplication1
{
    [XmlRoot("main")]
    public class ModelExampleConfiguration
    {
        public ModelExampleConfiguration()
        {
            this.Hosts = new List<HostConfiguration>();
        }
        
        [XmlElement("enabled")]
        public bool Enabled { get; set; }

        [XmlElement("name")]
        public string Name { get; set; }

        [XmlArray("hosts")]
        [XmlArrayItem("host")]
        public List<HostConfiguration> Hosts { get; set; }

        [XmlElement("users")]
        public UserGroupConfiguration[] UserGroup { get; set; }
    }

    public class HostConfiguration
    {
        [XmlAttribute("ip")]
        public string IP { get; set; }

        [XmlAttribute("port")]
        public int Port { get; set; }

        [XmlAttribute("username")]
        public string Username { get; set; }

        [XmlAttribute("password")]
        public string Password { get; set; }
    }

    public class UserGroupConfiguration
    {
        [XmlAttribute("groupName")]
        public string GroupName { get; set; }

        [XmlElement("user")]
        public UserConfiguration[] Users { get; set; }
    }

    public class UserConfiguration
    {
        [XmlAttribute("name")]
        public string Name { get; set; }
    }
}

See that now I used an array instead of a generic list. I could have used the list as well, but I think that way is simpler.

Working with Configuration Files – Quick Start

I’m currently working in a project where one need is to create easy customizable configuration files. The interest part comes when we need to read these configuration files and parsing them to .NET classes.

As I myself had to look up how some attributes work, I decided to share my experience creating those files, so this might help someone else going through the same problems.

To start off, let’s have a simple xml:

<?xml version="1.0" encoding="utf-8" ?>
<main>
  <enabled>true</enabled>
  <name>Example text name</name>
  <host ip="127.0.01" port="80" username="test" password="pass" />
</main>

Now the our model class:

using System.Xml.Serialization;

namespace ConsoleApplication1
{
    [XmlRoot("main")]
    public class ModelExampleConfiguration
    {
        [XmlElement("enabled")]
        public bool Enabled { get; set; }

        [XmlElement("name")]
        public string Name { get; set; }

        [XmlElement("host")]
        public HostConfiguration Host { get; set; }
    }

    public class HostConfiguration
    {
        [XmlAttribute("ip")]
        public string IP { get; set; }

        [XmlAttribute("port")]
        public int Port { get; set; }

        [XmlAttribute("username")]
        public string Username { get; set; }

        [XmlAttribute("password")]
        public string Password { get; set; }
    }
}

Note: If you have your class and property names exactly like your config file then you don’t need to specify the attributes, but I just prefer to always use them, this way I can name my classes however I want.

Notice I’m handling both elements and attributes, using XmlElement and XmlAttributes.

Now, the code to parse the xml into a model class:

using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var reader = XmlReader.Create("Example.xml");

            var factory = new XmlSerializerFactory();
            var serializer = factory.CreateSerializer(typeof(ModelExampleConfiguration));
            var config = serializer.Deserialize(reader) as ModelExampleConfiguration;
        }
    }
}

// config.Enabled will output true
// config.Host.Username will output test

In the next post I’ll show how to deserialize a list / array of items.

Configuring ASP.NET MVC on IIS 5 and IIS 6

I know there are other tutorials on the internet about this topic but I’m going to write here the way that worked for me.

Some tutorials I’ve seen tell you to change your Global.asax to add some routes. I didn’t have to change it at all. I only had to setup the IIS itself.

IIS 5

Configure your Virtual Directory as usual, then right click on it and select Properties. In the properties dialog, click on Configuration.  In this dialog, click on Add. Then use the settings below:

Configuring IIS 5

For Executable, use the following path:

c:\windows\microsoft.net\framework\v4.0.30319\aspnet_isapi.dll
or
c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll

depending on the version of .NET you are using on your application. You can copy the path from the other mappings that already exist.

Also make sure you uncheck the “Check that file exists” box.

Well, that is what it took for me to make it work on IIS 5.

IIS 6

For IIS 6 is not that different from what it needs on IIS 5. The process is the same until you get to the Configuration dialog. Now you add the same path showed above but the extension will change a bit. Take a look:

Configuring IIS 6

The option “Verify that file exists” must be unchecked.

After hitting OK, still in the Configuration dialog you have to add a Wildcard. Click on Insert and use the same DLL path you’ve been adding so far.

Configuring IIS 6

Once again, the option “Verify that file exists” must be unchecked – I know it’s annoying but that’s how it must be done.

See how the Configuration dialog must look like:

Configuring IIS 6

Note the extension and the wildcard added. Now, just hit OK and you’re done.

One thing I noticed is that if you name your Virtual Directory Anything.MVC it won’t work because of that “.mvc” extension you added.

Don’t worry. After doing that over and over again you get practice 😉

One good thing is that Microsoft is releasing a new Web Server called IIS Express which is as easy as the ASP.NET Development Server (that instance of Web Server that Visual Studio launches when you run a project) to configure but has almost all the features from the full version of IIS. On the new IIS Express you won’t need any extra configuration to have your ASP.NET MVC application up and running.

The new Microsoft WebMatrix – it’s like a small version of Visual Studio – already comes with the IIS Express. You can download WebMatrix here.