Monday, April 2, 2012

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

Monday, April 02, 2012 Posted by Andre Broers , , , , , , , , 61 comments
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.

61 comments:

  1. Reckon for a site like that it would have been a lot simpler just to use node.js and Express.js and it would have used way less RAM.

    ReplyDelete
  2. One of the things on my list, but all my business logic is in c#.

    ReplyDelete
  3. Hi
    Great post. I'm just looking into Nancy at the moment and it's a big help.

    Would it be possible to configure nginx to proxy ssl requests for just /login
    and pass through all the others as per your post?

    Cheers
    Paul

    ReplyDelete
  4. [...] start to serve data over HTTP is to implement a NancyModule. Here I leaned heavily on an excellent blog post by Andre Broers on self hosting Nancy and utilizing the SuperSimpleViewEngine included with [...]

    ReplyDelete
  5. Hi, Neat post. There is a problem along with your site in web explorer, could test this?
    IE nonetheless is the marketplace leader and a big element of people will
    pass over your excellent writing because of
    this problem.

    Also visit my page ... diet plans That Work
    Also see my website - easy diets that work

    ReplyDelete
  6. This logger works really well:
    http://www.kellermansoftware.com/p-14-net-logging-library.aspx

    ReplyDelete
  7. As the admin of this web site is working, no hesitation very soon it will be well-known, due to
    its quality contents.

    Also visit my blog post: diets that work

    ReplyDelete
  8. The one thing most everyone has in closer to your event than
    a solid meal because your stomach digests liquids faster.
    Well that order of the p90x videos was a quick you'll have enough strength and energy to tackle The 50 three times a week. It is a less expensive option than weight management programs the workout that can make my look fit and order of the p90x videos get my dream body shape.

    Feel free to surf to my webpage: order of p90x workouts

    ReplyDelete
  9. Thanks for sharing this useful information.
    Regards..
    Mathan
    DOT NET Training in Chennai

    ReplyDelete
  10. can you please attach the source code for this. Or send it to mailid: pradeepdg02@gmail.com

    ReplyDelete
    Replies
    1. I added the sourcecode to a github repo. https://github.com/broersa/mynancy

      Delete
  11. Good useful article!
    Please send source code to mailid: vkilyin@yandex.ru

    ReplyDelete
    Replies
    1. I added the sourcecode to a github repo: https://github.com/broersa/mynancy

      Delete
  12. I've seen so far, the most descriptive article. Thank you.

    ReplyDelete
  13. I definitely learn a lot from your sample, in fact I am using it as a base code. I am a newbie and I am trying to pass 2 parameters (username & password) to my nancy-based c# web service. Do you happen to have a sample on it? Thanks a lot.

    ReplyDelete
  14. Thanks for sharing these niche piece of coding to our knowledge. Here, I had a solution for my inconclusive problems & it’s really helps me a lot keep updates…

    ReplyDelete
  15. Nice blog, here I had an opportunity to learn something new in my interested domain. I have an expectation about your future post so please keep updates.DOT NET Training in Chennai

    ReplyDelete
  16. Thanks for the tutorial. I'm curious why one would use NginX alongside a self-hosted nancy app? What does it get you that pure nancyfx does not?


    Note: you have a couple broken links under the "Now to the Linux and NginX part" section.

    ReplyDelete
  17. Hi friends, This is Chandrika from Chennai. I did Unix certification course in Chennai at Fita academy. This is really useful for me to make a bright career. Suppose if anyone interested to learn Unix Course in Chennai please visit Fita academy located at Chennai.

    ReplyDelete
  18. Thanks for your wonderful post.It is really very helpful for us and I have gathered some important information from this blog.If anyone wants to get Dot Net Training in Chennai reach FITA, rated as No.1 Dot Net Training Institute in Chennai.

    ReplyDelete
  19. Web design encompasses many different skills and disciplines in the production and maintenance of websites. The different areas of web design include web graphic design; interface design; authoring, including standardised code and proprietary software; user experience design; and search engine optimization. Often many individuals will work in teams covering different aspects of the design process, although some designers will cover them all.Get More ideas on web designing and get practical oriented training for more details visit:http://www.thinkittraining.in/web-designing-training

    ReplyDelete
  20. This is really an awesome article. Thank you for sharing this.It is worth reading for everyone. Visit us:
    Oracle Training in Chennai

    ReplyDelete
  21. very nice blogs!!! i have to learning for lot of information for this sites...Sharing for wonderful information.Thanks for sharing this valuable information to our vision. You have posted a trust worthy blog keep sharing.Oracle DBA Training in Chennai

    ReplyDelete
  22. Wonderful tips, very helpful well explained. Your post is definitely incredible. I will refer this to my friend.SalesForce Training in Chennai

    ReplyDelete
  23. Thanks for sharing this valuable information to our vision. You have posted a trust worthy blog keep sharing.Nice article i was really impressed by seeing this article, it was very interesting and it is very useful for me.. Android Training in Chennai

    ReplyDelete
  24. Really awesome blog. Your blog is really useful for me. Thanks for sharing this informative blog. Keep update your blog.SAP Training in Chennai

    ReplyDelete
  25. I found some useful information in your blog,it was awesome to read, thanks for sharing this great content to my vision, keep sharing..selenium Training in Chennai

    ReplyDelete
  26. Excellent information with unique content and it is very useful to know about the information based on blogs. Hadoop Training in Chennai

    ReplyDelete
  27. There are lots of information about latest technology, like Hadoop cluster is a special type of computational cluster designed specifically for storing and analyzing huge amounts of unstructured data in a distributed computing environment. This information seems to be more unique and interesting. Thanks for sharing.
    Big Data Training Chennai | Big Data Training | Hadoop Course in Chennai

    ReplyDelete
  28. Excellent information with unique content and it is very useful to know about the information based on blogs.
    Hadoop Training In Chennai | oracle apps financials Training In Chennai | advanced plsql Training In Chennai

    ReplyDelete
  29. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging…
    Regards,
    Angularjs training in chennai|Angularjs training chennai|Angularjs course in chennai|Angularjs training center in Chennai

    ReplyDelete
  30. Really awesome blog. Your blog is really useful for me. Thanks for sharing this informative blog. Keep update your blog.
    Oracle Training In Chennai

    ReplyDelete
  31. I am reading ur post from the beginning, it was so interesting to read & i feel thanks to you for posting such a good blog, keep updates regularly.Best Hadoop Training Institute In Chennai

    ReplyDelete
  32. Thanks.. this really helped me out

    ReplyDelete
  33. This is very nice post thanks sharing with lots of apps ..:) Android Application Software

    ReplyDelete
  34. Excellent post!!! Your article helped to under the future of java development. Being an open source platform, java is integrated in most of the software development industries to create rich featured applications. Java Course in Chennai | Best JAVA Training in Chennai

    ReplyDelete
  35. Hadoop is one of the best tool which is used to handle the big data in the IT industy and it is the fastest growing field in information technology.
    hadoop training in Chennai | hadoop training chennai

    ReplyDelete
  36. A good blog. Thanks for sharing the information. It is very useful for my future. keep sharing
    red ball 2 | duck life 2 | happy wheels | Red Ball | Red ball 3 | Flash Games| Tank trouble

    ReplyDelete
  37. I hope that other readers will also experience how I feel after reading your article. I feel very grateful that I read this. It is very helpful and very informative and I really learned a lot from it. web technology experts

    ReplyDelete
  38. Well Said, you have furnished the right information that will be useful to anyone at all time. Thanks for sharing your Ideas.
    Node JS training in chennai | Node JS training institute in chennai

    ReplyDelete
  39. Well Said, you have furnished the right information that will be useful to anyone at all time. Thanks for sharing your Ideas.
    Hadoop Training in Chennai | Hadoop Training Chennai

    ReplyDelete
  40. Thanks for sharing this webpage.It is really useful to me.I will bookmark this page for my future reference.Continue sharing more about dot net.
    Regards,
    Best DOTNET Training in Chennai | dotnet training institutes in Chennai | dotnet training in Chennai

    ReplyDelete
  41. Excellent content. Thanks for sharing content which is very useful that provided me the required information.
    Cloud Computing Training in Chennai | Cloud Computing Courses in Chennai

    ReplyDelete
  42. Nice interesting information on the latest arrived technology which helped me to get update according to the recent trends.
    Salesforce Training in Chennai | Salesforce Course in Chennai

    ReplyDelete
  43. Wow! That's really great information guys.I know lot of new things here. Really great contribution.Thank you ...
    share point training in chennai

    ReplyDelete
  44. The blog gave me an idea about self hosting web application
    Dot Net Training in Chennai

    ReplyDelete
  45. Thanks for posting useful information.You have provided an nice article, Thank you very much for this one. And i hope this will be useful for many people.. and i am waiting for your next post keep on updating these kinds of knowledgeable things...Really it was an awesome article...very interesting to read..
    please sharing like this information......
    Android training in chennai
    Ios training in chennai

    ReplyDelete

  46. Thanks for posting useful information.You have provided an nice article, Thank you very much for this one. And i hope this will be useful for many people.. and i am waiting for your next post keep on updating these kinds of knowledgeable things...Really it was an awesome article...very interesting to read..
    please sharing like this information......
    Web Development Company

    ReplyDelete

  47. Really it was an awesome article...very interesting to read..You have provided an nice article....Thanks for sharing..
    Web Design Company
    Web Development Company

    ReplyDelete