Monday, April 2, 2012

C# Nancy SelfHosting WebApplication with authentication deployed toLinux - nGinx - Mono

Monday, April 02, 2012 Posted by Andre Broers , , , , , , , ,
For my little vmware esxi homeserver I need my webapplications to have the smallest footprint possible. I like the c# language and the visual studio development environment. But running my web applications on a full blown Windows Server with IIS and ASP.NET is a bit overkill in my opinion. Therefore I started looking for a small web framework developped in C# which had the critical things I needed for backend server side: Authorization/Authentication, an MVC pattern with a simple viewengine all running in a dotnet client profile as a console application. It runs perfect on Linux with nGinx as a proxy in front of it.

The sourcecode of this project can be found here: https://github.com/broersa/mynancy

In this blog post I will demonstrate how to create the above and deploy it on an Ubuntu Linux Server with nGinx and Mono.

We start by creating a new Console Application in Visual Studio 2010 an call it MyNancy.

Use Nuget to import the needed assemblies:
PM> install-package Nancy
Successfully installed 'Nancy 0.10.0'.
Successfully added 'Nancy 0.10.0' to MyNancy.

PM> install-package Nancy.Hosting.Self
Attempting to resolve dependency 'Nancy (= 0.10.0)'.
Successfully installed 'Nancy.Hosting.Self 0.10.0'.
Successfully added 'Nancy.Hosting.Self 0.10.0' to MyNancy.

PM> install-package Nancy.Authentication.Forms
Attempting to resolve dependency 'Nancy (= 0.10.0)'.
Successfully installed 'Nancy.Authentication.Forms 0.10.0'.
Successfully added 'Nancy.Authentication.Forms 0.10.0' to MyNancy.

PM>

The Basic Nancy Console Application


In the main loop of our web application we create a new NancyHost object which resides in the Nancy.Hosting.Self assembly. As the parameter we give it the bind address where to listen. In our case this is the localhost and a high portnumber. This portnumber is easyer to test on and when we will deploy to linux we will use nginx as a proxy to this port.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Nancy.Hosting.Self;

namespace MyNancy
{
    class Program
    {
        static void Main(string[] args)
        {
            // initialize an instance of NancyHost (found in the Nancy.Hosting.Self package)
            var host = new NancyHost(new Uri("http://127.0.0.1:8888"));
            host.Start();  // start hosting

            //Under mono if you deamonize a process a Console.ReadLine with cause an EOF
            //so we need to block another way
            if (args.Any(s => s.Equals("-d", StringComparison.CurrentCultureIgnoreCase)))
            {
                while (true) Thread.Sleep(10000000);
            }
            else
            {
                Console.ReadKey();
            }

            host.Stop();  // stop hosting
        }
    }
}

After this we have to create a loop so the application will run endlessly. There are two ways to run the application. When run on linux we need to run with a parameter -d. Which is very nice explained by HumbleCoder.

Now we need a so called Module which will act a our Controller (in the Model-View-Controller pattern)

Implement a Nancy Module (aka Controller)


The Nancy Module is the Controller that reacts to the client requests. It's responsible for routing the request to the right action. Ater this filling the (View)Model with data and than returning a View (Content). That's a lot of technical terms that I will explain in the next chapters.

I will choose to implement a BaseModule which will implement the Global Logic. In the BaseModule we can add handlers to the request pipeline. In this example I will show a MasterPage sample for the SuperSimpleViewEngine (the viewengine included in the standard Nancy assembly). In the BaseModule we will append the ViewModel for the MasterPage, so that the masterpage always has it's model data to render. We can of course overwrite this data in the specific action.

Here is the implementation of the BaseModule:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using System.Dynamic;
using MyNancy.Models;

namespace MyNancy.Modules
{
    public abstract class BaseModule : NancyModule
    {
        public dynamic Model = new ExpandoObject();

        public BaseModule()
        {
            SetupModelDefaults();
        }

        public BaseModule(string modulepath)
            : base(modulepath)
        {
            SetupModelDefaults();
        }

        private void SetupModelDefaults()
        {
            Before.AddItemToEndOfPipeline(ctx =>
            {
                Model.MasterPage = new MasterPageModel();
                Model.MasterPage.Title = "MyNancy - Hello World!";
                if (Request.Cookies.ContainsKey("lastvisit"))
                {
                    Model.MasterPage.LastVisit = Uri.UnescapeDataString(Request.Cookies["lastvisit"]);
                }
                else
                {
                    Model.MasterPage.LastVisit = "No cookie value yet";
                }
                Model.MasterPage.IsAuthenticated = (ctx.CurrentUser == null);
                return null;
            });
            After.AddItemToEndOfPipeline(ctx =>
            {
                var now = DateTime.Now;
                ctx.Response.AddCookie("lastvisit", now.ToShortDateString() + " " + now.ToShortTimeString(), now.AddYears(1));
            });
        }
    }
}

In the Before in line 28 we add our MasterPage viewmodel to the dynamic Model. The Model is the ViewModel. It's of a ExpandoObject type which is some sort of dynamic dictionary where we can Add all kind of objects to.
In the After in line 43 we set a cookie. Which we will read in the before. We will check if the value is set. I will explain this later on.

Now it is time for the MainModule which is inherited from the above BaseModule.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using Nancy.Extensions;
using Nancy.Authentication.Forms;
using MyNancy.Models;

namespace MyNancy.Modules
{
    public class MainModule : BaseModule
    {
        public MainModule()
        {
            Get["/"] = x =>
            {
                Model.index = new indexModel();
                Model.index.HelloWorld = "HelloWorld!";
                return View["index", Model];
            };

            Get["/login"] = x =>
            {
                Model.login = new loginModel() { Error = this.Request.Query.error.HasValue, ReturnUrl = this.Request.Url };
                return View["login", Model];
            };

            Post["/login"] = x =>
            {
                var userGuid = MyUserMapper.ValidateUser((string)this.Request.Form.Username, (string)this.Request.Form.Password);

                if (userGuid == null)
                {
                    return Context.GetRedirect("~/login?error=true&username=" + (string)this.Request.Form.Username);
                }

                DateTime? expiry = null;
                if (this.Request.Form.RememberMe.HasValue)
                {
                    expiry = DateTime.Now.AddDays(7);
                }

                return this.LoginAndRedirect(userGuid.Value, expiry);
            };
            Post["/logout"] = x =>
            {
                return this.LogoutAndRedirect("/");
            };

        }
    }
}

In the constructor of the NancyModule we default the routes. In this sample we have Get and Post actions. The first one in line 16 is the default page which will add the indexModel class to our Model which is created in the BaseModule. Last but not least it will return the index View and the Model. Same thing for the login and logout actions which I will discuss later on.

Views


For the views in Nancy we can use all sorts of viewengines. (Razor, Spark etc.) I will use the SuperSimpleViewEngine which is included in Nancy and doesn't need all the ASP.NET shizzle like Razor does. The SuperSimpleViewEngine is pretty good explained here. It has a little less features as the full blown Razor viewengine, but it is more than enough to create everything possible, and it keeps the views nice and clean and will force you to put all the logic in the action and in the viewmodel.

In this example we will create a MasterPage and the Index view. We will start with the Masterpage which uses the MasterPage viewmodel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyNancy.Models
{
    public class MasterPageModel
    {
        public string Title { get; set; }
        public string LastVisit { get; set; }
        public bool IsAuthenticated { get; set; }
    }
}

This is the model that gets filled in the Before pipeline in the BaseModule class. It contains the Page Title the LastVisit date and a boolean to see if the user is authenticated. These values are filled for every request that reaches an action that is in a Module that inherits from BaseModule. Now let's continue with the MasterPage.html:
<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>@Model.MasterPage.Title</title>
 <link rel="stylesheet" type="text/css" href="/Content/site.css" />
 @Section['Scripts']
</head>
<body>
    <section>
        <div id="content">
        <h1>Last visit: @Model.MasterPage.LastVisit</h1>
        @If.MasterPage.IsAuthenticated
            <a href="/login">Login</a>
        @EndIf
        @IfNot.MasterPage.IsAuthenticated
            <form id="logoutform" method="post" action="/logout">
                <input type=submit value="Logout" />
            </form>
        @EndIf
        @Section['Content']
        </div>
    </section>
</body>
</html>

Remember that every static content in the content and the view folder of the project needs to be Copy to Output Directory setting to Copy Always. This will copy the content to the bin directory when running the application in Visual Studio.

In this MasterPage we create a template for all our web pages. The MasterPage contains sections. This sections are placeholders where our views are added. We define a Script placeholder, so we can add scripts to the top of the page and we have a Content placeholder where our rendered HTML content is placed.

We add the MasterPage ViewModel data like the title between the title tags in the head of our document. We also add a css file which we reference in the /Content folder. This will reference the site.css file in our project in the /content folder.
body
{
    background-color: Yellow;
}

It's just for the demo that this will generate a nice yellow background ;-)
Later on in the MasterPage is a conditional sample. The SuperSimpleViewEngine doesn't have the 'else' keyword. Instead we have to make two if statements. Mind the difference between the if and the display reference. The if doesn't need the Model reference while the @ does need it.

Now the indexviewmodel and view:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyNancy.Models
{
    public class indexModel
    {
        public String HelloWorld { get; set; }
    }
}

@Master['MasterPage']
@Section['Scripts']
@EndSection
@Section['Content']
    <h1>@Model.index.HelloWorld</h1>
    <a href="/admin/">Admin</a>
@EndSection

To use the MasterPage template we add the @Master in the first line of the view. After this we implement the sections we defined in the MasterPage. In this content section we print the HelloWorld string from the indexModel viewmodel. We also create a hyperlink to an adminmodule which we will implement in the authorisation section later on.

We can now build and run our console application and point a browser to http://127.0.0.1:8888

Adding Authorisation


Many webapplications have the need to let people create an account and access secured pages. Nancy has an implementation of forms authentication which I will demonstrate here.

To work with users we need some sort of database where the users and their passwords reside. For simplicity we implement a static list with users. But first we need to implement the Nancy IUserIdentity interface:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy.Security;

namespace MyNancy
{
    public class MyUserIdentity : IUserIdentity
    {
        public IEnumerable<string> Claims { get; set; }

        public string UserName { get; set; }
    }
}

This is the User class which we can extend with other properties like address and email and other things.

Now we need to implement the Nancy IUserMapper which implements the GetUserFromIdentifier function which in our case simply gets the user from the static pool of users.

We also add a static function to verify a username and password combination. All user related functions can be implemented in this class. In our case we only need a function to authenticate a user.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy.Authentication.Forms;
using Nancy.Security;

namespace MyNancy
{
    public class MyUserMapper : IUserMapper
    {
        private static List<Tuple<string, string, Guid>> users = new List<Tuple<string, string, Guid>>();

        static MyUserMapper()
        {
            users.Add(new Tuple<string, string, Guid>("admin", "password", new Guid("55E1E49E-B7E8-4EEA-8459-7A906AC4D4C0")));
            users.Add(new Tuple<string, string, Guid>("user", "password", new Guid("56E1E49E-B7E8-4EEA-8459-7A906AC4D4C1")));
        }

        public IUserIdentity GetUserFromIdentifier(Guid identifier)
        {
            var userRecord = users.Where(u => u.Item3 == identifier).FirstOrDefault();

            return userRecord == null ? null : new MyUserIdentity { UserName = userRecord.Item1 };

        }

        public static Guid? ValidateUser(string username, string password)
        {
            var userRecord = users.Where(u => u.Item1 == username && u.Item2 == password).FirstOrDefault();

            if (userRecord == null) return null;

            return userRecord.Item3;
        }

    }
}

Now we have to supply some configuration to the forms authentication mechanism. We can do this by overriding the NancyBootStrapper and add some initialisation to the pipeline.

To add authentication we need to create our own overridden NancyBootStrapper.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using TinyIoC;
using Nancy.Authentication.Forms;
using Nancy.Bootstrapper;
using System.IO;

namespace MyNancy
{
    public class MyBootStrapper : DefaultNancyBootstrapper
    {
        byte[] favicon;

        protected override byte[] DefaultFavIcon
        {
            get
            {
                if (favicon == null)
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        Resources.FavIcon.favicon.Save(ms);
                        favicon = ms.ToArray();
                    }
                }
                return favicon;
            }
        }

        protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
        {
            base.ConfigureRequestContainer(container, context);

            container.Register<IUserMapper, MyUserMapper>();
        }

        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
        {
            base.ApplicationStartup(container, pipelines);
            var formsAuthConfiguration =
                new FormsAuthenticationConfiguration()
                {
                    RedirectUrl = "~/login",
                    UserMapper = container.Resolve<IUserMapper>()
                };

            FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
        }
    }
}

In the ConfigureRequestContainer we must add the IUserMapper interface as an Inversion of Control (IoC) mapping to our implemented MyUserMapper class.

In the ApplicationStartup we configure our FormsAuthentication. We configure the RedirectUrl which points to the action where users can login. When a user requests a secured action our application will redirect to this action to authenticatie and on succesfull login it will redirect to the requested action. The UserMapper is our new IUserMapper which will be resolved to the MyUserMapper class by the IoC mechanism. The last line will Enable the Forms Authentication in the pipeline.

After this the configuration is done and we can create our actions and views.

Login


We will implement the actions in the MainModule. I will include it here again:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using Nancy.Extensions;
using Nancy.Authentication.Forms;
using MyNancy.Models;

namespace MyNancy.Modules
{
    public class MainModule : BaseModule
    {
        public MainModule()
        {
            Get["/"] = x =>
            {
                Model.index = new indexModel();
                Model.index.HelloWorld = "HelloWorld!";
                return View["index", Model];
            };

            Get["/login"] = x =>
            {
                Model.login = new loginModel() { Error = this.Request.Query.error.HasValue, ReturnUrl = this.Request.Url };
                return View["login", Model];
            };

            Post["/login"] = x =>
            {
                var userGuid = MyUserMapper.ValidateUser((string)this.Request.Form.Username, (string)this.Request.Form.Password);

                if (userGuid == null)
                {
                    return Context.GetRedirect("~/login?error=true&username=" + (string)this.Request.Form.Username);
                }

                DateTime? expiry = null;
                if (this.Request.Form.RememberMe.HasValue)
                {
                    expiry = DateTime.Now.AddDays(7);
                }

                return this.LoginAndRedirect(userGuid.Value, expiry);
            };
            Post["/logout"] = x =>
            {
                return this.LogoutAndRedirect("/");
            };

        }
    }
}

First the Get Login Action. This will fill the login viewmodel, and return the login view. It is a simple form where users can type there username, password and a remember me checkbox.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;

namespace MyNancy.Models
{
    public class loginModel
    {
        public bool Error { get; set; }
        public Url ReturnUrl { get; set; }
    }
}

@Master['MasterPage']
@Section['Scripts']
@EndSection
@Section['Content']
    <h1>Login</h1>
    <form method="POST">
        Username <input type="text" name="Username" />
        <br />
        Password <input name="Password" type="password" />
        <br />
        Remember Me <input name="RememberMe" type="checkbox" value="True" />
        <br />
        <input type="submit" value="Login" />
    </form>
    @If.Model.Errored
        <div>Invalid Username or Password</div>
    @EndIf
@EndSection

This view will post the completed form to the Post Login Action in the MainModule. This action will verify the Username and Password combination.

When it fails it will redirect to login page again. When it succeeds it will check for the RememberMe and login and redirect to the returnUrl.

Logout


When we are logged on the login link will change to a logout butten because of the if statements in our MasterPage. When we click the logout button the html form will Post to the Logout Action in our MainModule. This action will log the user out of the system and redirect to index page which will show the login button again.

Authentication


On the index page we created a link to the /admin/ module. Let's create this secure module and create a secure view that will display the username of the logged in user.

We create a new Module, inherit it from BaseModule and call it AdminModule:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using Nancy.Extensions;
using Nancy.Authentication.Forms;
using Nancy.Security;
using MyNancy.Models;

namespace MyNancy.Modules
{
    public class AdminModule : BaseModule
    {
        public AdminModule() : base("/admin")
        {
            this.RequiresAuthentication();
            Get["/"] = x =>
            {
                Model.Index = new MyNancy.Models.admin.indexModel();
                Model.Index.UserName = Context.CurrentUser.UserName;
                return View["admin/index", Model];
            };
        }
    }
}

This module calls the base constructor with a route prefix. All the actions in this Module are prefixed with /admin. Then in line 17 we call this.RequiresAuthentication(). By calling this method all requests to this Module must be authenticated. When not the requests will be redirected to the "/login" action as we configured in the MyBootStrapper class. Then we create the default action which will fill the admin indexModel with the CurrentUser (MyUserIdentity object) UserName string. After this the view is returned. Remember to add the prefix to the view name because the view folders aren't the same as the routing structure.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyNancy.Models.admin
{
    public class indexModel
    {
        public string Guid { get; set; }
        public string UserName { get; set; }
    }
}

@Master['MasterPage']
@Section['Scripts']
    <script src="@Path['~/Content/myscript.js'];" type="text/javascript"></script>
@EndSection
@Section['Content']
    <h1>Hello @Model.Index.UserName!</h1>
    <input type="button" onclick="myalert()" value="Demo scripts" />
@EndSection

JavaScript


The above view also demonstrates how to reference a static javascript file and use it. When you click the button Demo Scripts it will show an alert.

Cookies


Nancy includes a way to add cookies and read cookies to and from the response and request streams. I showed an example earlier in the BaseModule.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using System.Dynamic;
using MyNancy.Models;

namespace MyNancy.Modules
{
    public abstract class BaseModule : NancyModule
    {
        public dynamic Model = new ExpandoObject();

        public BaseModule()
        {
            SetupModelDefaults();
        }

        public BaseModule(string modulepath)
            : base(modulepath)
        {
            SetupModelDefaults();
        }

        private void SetupModelDefaults()
        {
            Before.AddItemToEndOfPipeline(ctx =>
            {
                Model.MasterPage = new MasterPageModel();
                Model.MasterPage.Title = "MyNancy - Hello World!";
                if (Request.Cookies.ContainsKey("lastvisit"))
                {
                    Model.MasterPage.LastVisit = Uri.UnescapeDataString(Request.Cookies["lastvisit"]);
                }
                else
                {
                    Model.MasterPage.LastVisit = "No cookie value yet";
                }
                Model.MasterPage.IsAuthenticated = (ctx.CurrentUser == null);
                return null;
            });
            After.AddItemToEndOfPipeline(ctx =>
            {
                var now = DateTime.Now;
                ctx.Response.AddCookie("lastvisit", now.ToShortDateString() + " " + now.ToShortTimeString(), now.AddYears(1));
            });
        }
    }
}

Line 46 will set a cookie with the last visit date and time after each request. The cookie will stay active for 1 year. When this function is called in a normal action it will send a cookie to the user for a specific request to an action. This cookie will be sent by every request from the same client to your domain. In the sample at line 23 I will check if the cookie exists if it does I will set the value in the MasterPage viewmodel. If it doesn't exist it will set the value "No cookie value yet".
In the Masterpage template view this value will be displayed.

ErrorHandling


It's always a good manner to catch all exceptions. Even more in web applications because it's not you who will run in the exceptions! Nancy has the IErrorHandler interface to catch the exceptions. Although Nancy has some very nice default templates to handle exceptions (httpstatus 500) and page not found errors (httpstatus 404). It's most of the time desireable to make your own style errormessages. Luckily it is quite easy in Nancy. All we have to do is create our own implementation of the IErrorHandler and Nancy will automagicly use our new MyErrorHandler class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using Nancy.ErrorHandling;
using System.IO;

namespace MyNancy
{
    public class MyErrorHandler : IErrorHandler
    {
        private HttpStatusCode[] supportedStatusCodes = new[] { HttpStatusCode.NotFound, HttpStatusCode.InternalServerError };

        public void Handle(HttpStatusCode statusCode, NancyContext context)
        {
            if (context.Response == null)
            {
                context.Response = new Response() { StatusCode = statusCode };
            }
            context.Response.ContentType = "text/html";
            context.Response.Contents = s =>
            {
                using (var writer = new StreamWriter(s, Encoding.UTF8))
                {
                    if (statusCode == HttpStatusCode.NotFound)
                    {
                        writer.Write("Page not found: " + context.Request.Url);
                    }
                    else
                    {
                        writer.Write("error: " + context.Items["ERROR_TRACE"]);
                    }
                }
            };
        }

        public bool HandlesStatusCode(HttpStatusCode statusCode)
        {
            return this.supportedStatusCodes.Any(s => s == statusCode);
        }

    }
}

All we have to do is implement the HandleStatusCode function to return true if our implementation supports the statuscode. We have a private array of the statuscodes that we support. In our case the 404 and the 500. The HandleStatusCode return true in these both cases. After this we have to implement the actual handler. In this case it simply Writes the errormessage to the Response stream. But in real live you will format nice web layout pages. But remember in case of a 500 errormessage you'd better use static html because the the system doesn't know what went wrong. And calling another action will probably go wrong again.
You can test this errorhandler by calling an invalid action and you will get a Page not found error. An exception can be tested by adding a row:
throw new ApplicationException();

in the first line of the default action in the MainModule. This will return the error message.

Finally a nice favicon


It's always nice to set a nice favicon. Download a nice favicon here. Add it to the visual studio project in the /content/image folder and name it favicon.ico. Now create a /Resources folder in the visual studio project. Create a new resource file in this Resources folder and name it FavIcon.resx. In the resource editor click Add Resource and select Add existing file... Select the favicon.ico in the /content/image folder of your project. Now we have the resource in place. Now get back to coding.

We allready had the MyBootStrapper class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Nancy;
using TinyIoC;
using Nancy.Authentication.Forms;
using Nancy.Bootstrapper;
using System.IO;

namespace MyNancy
{
    public class MyBootStrapper : DefaultNancyBootstrapper
    {
        byte[] favicon;

        protected override byte[] DefaultFavIcon
        {
            get
            {
                if (favicon == null)
                {
                    using (MemoryStream ms = new MemoryStream())
                    {
                        Resources.FavIcon.favicon.Save(ms);
                        favicon = ms.ToArray();
                    }
                }
                return favicon;
            }
        }

        protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
        {
            base.ConfigureRequestContainer(container, context);

            container.Register<IUserMapper, MyUserMapper>();
        }

        protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
        {
            base.ApplicationStartup(container, pipelines);
            var formsAuthConfiguration =
                new FormsAuthenticationConfiguration()
                {
                    RedirectUrl = "~/login",
                    UserMapper = container.Resolve<IUserMapper>()
                };

            FormsAuthentication.Enable(pipelines, formsAuthConfiguration);
        }
    }
}

All we have to do is override the DefaultFavIcon property with the code shown above. It will load the favicon from the resource and provide it as byte array. Nancy will be so polity to pick it up and serve it to your viewers. Most of the time it won't show up immediatly because most browsers cache the favicon. I know in internet explorer you can point your browser to the url http://127.0.0.1:8888/favicon.ico and press F5 to refresh the image.

Now run your application and see if it all works. Remember that for all static content and views you must set Copy to Output Directory must to Copy Always.

Now to the Linux and NginX part...


When it all works we can zip the bin/Debug folder in the solution map copy this to our Linux Ubuntu Server which is like the one we installed in this blog post. In this blog post I configured port forwarding of port 2222 and port 8888 to our virtual linux ubuntu server. It is a bit confusion but port 8888 wil forward to 80 on our virtual ubuntu server. So the nginx on our ubuntu server will listen to port 80 and will connect to our nancy application on port 8888.

I use pscp (putty secure copy) to get the file on the linux machine.
pscp.exe -P 2222 mynancy.zip me@127.0.0.1:

Now logon to the linux machine.
First install nginx and the mono runtime and an unzip tool. Then create a folder to unzip the mynancy.zip to.
me@ubuntu01:~$ sudo apt-get install nginx mono-runtime unzip
me@ubuntu01:~$ mkdir mynancy
me@ubuntu01:~$ cd mynancy
me@ubuntu01:~$ unzip ../mynancy.zip

now edit the nginx configuration.
me@ubuntu01:~$ cd /etc/nginx/sites-enabled
me@ubuntu01:/etc/nginx/sites-enabled$ sudo vi default

Let this config file look like:
server {
        root /usr/share/nginx/www;
        server_name localhost;
        location / {
                proxy_pass http://127.0.0.1:8888;
        }
        location /Content/ {
                alias /home/me/mynancy/content/;
        }
}

It's a good habit to serve the static content direct from the folder, not through the mono process.
Al the rest will be proxied to our mono application.
Restart the nginx service:
me@ubuntu01:/etc/nginx/sites-enabled$ sudo service nginx restart

Now go back to the mynancy folder and start our MyNancy application:
me@ubuntu01:/etc/nginx/sites-enabled$ cd
me@ubuntu01:~$ cd mynancy
me@ubuntu01:~/mynancy$ mono MyNancy.exe -d

Now start a browser on you workstation and point it to : http://127.0.0.1:8888 and our MyNancy application will apear from nginx and mono.





Here's the memory footprint:
me@ubuntu01:~$ ps euf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
me         848  0.0  0.4  23048  2204 pts/1    Ss   21:14   0:00 -bash USER=me LOGNAME=me HOME=/home/me PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games MAIL=/var/mail/me SHELL=/bin/bash SSH_
me         862  0.0  0.2  17996  1140 pts/1    R+   21:16   0:00  \_ ps euf TERM=xterm SHELL=/bin/bash SSH_CLIENT=10.0.2.2 50228 22 SSH_TTY=/dev/pts/1 USER=me LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=
me         756  0.0  0.4  23048  2240 pts/0    Ss   20:50   0:00 -bash USER=me LOGNAME=me HOME=/home/me PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games MAIL=/var/mail/me SHELL=/bin/bash SSH_
me         818  0.3  6.9 121900 35176 pts/0    Sl+  21:09   0:01  \_ mono MyNancy.exe -d TERM=xterm SHELL=/bin/bash SSH_CLIENT=10.0.2.2 50141 22 SSH_TTY=/dev/pts/0 USER=me LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33

About 35 megabytes, not bad for a full webapplication.