Yatendra Khandelwal

  • Random
  • Archive
  • RSS
  • Ask me anything

Handling sub-domains in ASP.Net MVC

In SaasApp which is framework for mulituser-multitenant SAAS framework for ASP.Net MVC, what I wanted was that when user types the url without a subdomain my app should display the website where user can get details about the product and plans, select a plan and register an account (in this post account refers to say a company that can have multiple users) with selected plan. Once an account is created their users should be able to go to their selected subdomain like myaccount.[SAAS app domain].com, login and access the product. I wanted the subbdomain “myaccount” to be forwarded as a parameter to required actions in the controller to identify the account I am dealing with. 

Demo: http://saasapp.yatendra.com

ASP.Net MVC has a sophisticated and flexible routing mechanism. It allows developers to create their own customized routing system by extending RouteBase class and providing your own implementation of GetRouteData(HttpContextBase) function.

Following is what I have used - 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using SaasApp.Utility;

namespace System.Web.Routing

{

    public class DomainRoute : RouteBase

    {

        public override RouteData GetRouteData(HttpContextBase httpContext)

        {

    //get subdomain, the string before first dot in the url

            string subdomain = UtilityHelper.GetSubdomain(httpContext.Request.Headers[“HOST”]);

            RouteData routeData = new RouteData(this, new MvcRouteHandler());

            if (!string.IsNullOrEmpty(subdomain))

            {

//url has subdomain

                routeData.Values.Add(“account”, subdomain);

                string filepath = httpContext.Request.FilePath;

                string[] parts = filepath.Split(‘/’);

                switch (parts[1].ToLower())

                {

                    case “app”:

                        routeData.Values.Add(“controller”, “App”);

                        if (parts.Length > 2)

                        {

                            switch (parts[2].ToLower())

                            {

                                case “index”:

                                    routeData.Values.Add(“action”, “Index”);

                                    break;

                                case “about”:

                                    routeData.Values.Add(“action”, “About”);

                                    break;

                                default:

                                    routeData.Values.Add(“action”, “Index”);

                                    break;

                            }

                        }

                        else

                        {

                            routeData.Values.Add(“action”, “Index”);

                        }

                        break;

                    case “account”:

                        routeData.Values.Add(“controller”, “Account”);

                        if (parts.Length > 2)

                        {

                            switch (parts[2].ToLower())

                            {

                                case “login”:

                                    routeData.Values.Add(“action”, “Login”);

                                    break;

                                case “logout”:

                                    routeData.Values.Add(“action”, “Logout”);

                                    break;

                            }

                        }

                        break;

                    default:

                        if (httpContext.Request.IsAuthenticated)

                        {

                            httpContext.Response.Redirect(“/App/Index”);

                        }

                        httpContext.Response.Redirect(“/Account/Login”);

                        break;

                }

            }

            else

            {

//url does not have subdomain

                string filepath = httpContext.Request.FilePath;

                string[] parts = filepath.Split(‘/’);

                switch (parts[1].ToLower())

                {

                    case “home”:

                        routeData.Values.Add(“controller”, “Home”);

                        if (parts.Length > 2)

                        {

                            switch (parts[2].ToLower())

                            {

                                case “index”:

                                    routeData.Values.Add(“action”, “Index”);

                                    break;

                                case “plans”:

                                    routeData.Values.Add(“action”, “Plans”);

                                    break;

                                case “selectplan”:

                                    routeData.Values.Add(“action”, “SelectPlan”);

                                    if (parts.Length > 3)

                                    {

                                        routeData.Values.Add(“planType”, parts[3]);

                                    }

                                    else

                                    {

                                        routeData.Values.Add(“planType”, “1”);

                                    }

                                    break;

                                case “selectaplan”:

                                    routeData.Values.Add(“action”, “SelectAPlan”);

                                    break;

                                case “accountcreated”:

                                    routeData.Values.Add(“action”, “AccountCreated”);

                                    if (parts.Length > 3)

                                    {

                                        routeData.Values.Add(“accountName”, parts[3]);

                                    }

                                    else

                                    {

                                        routeData.Values.Add(“accountName”, “”);

                                    }

                                    break;

                                case “about”:

                                    routeData.Values.Add(“action”, “About”);

                                    break;

                                default:

                                    routeData.Values.Add(“action”, “Index”);

                                    break;

                            }

                        }

                        else

                        {

                            routeData.Values.Add(“action”, “Index”);

                        }

                        break;

                    default:

                        httpContext.Response.Redirect(“/Home/Index”);

                        break;

                }

            }

            return routeData;

        }

        public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)

        {

            //Implement your formating Url formating here

            return null;

        } 

    }

}

  • 1 month ago
  • Comments
  • Permalink
  • Share
    Tweet

Compressing HTML output in ASP.Net / ASP.Net MVC

Most modern browsers have capability to get output from webservers in compressed form. This reduces number of bytes to be transferred and thus making transmission faster. Browsers that support this functionality specify supported compression type as value of “Accept-Encoding” attribute for example “Accept-Encoding: gzip, deflate”. gzip and deflate being two most commonly supported compression types. This can be configured at web server level but if you want to do have a lot of control over we server configuration like in shared hosting environment you can implement IHttpModule and set it up for your application as follows - 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Web;

using System.IO.Compression;

namespace MyPackage

{

    public class HttpCompressionModule : IHttpModule

    {

        #region IHttpModule Members

        void IHttpModule.Dispose()

        {            

        }

        void IHttpModule.Init(HttpApplication context)

        {

            context.PostAcquireRequestState += new EventHandler(context_PostAcquireRequestState);

            context.EndRequest += new EventHandler(context_EndRequest);

        }

        void context_EndRequest(object sender, EventArgs e)

        {

            HttpApplication context = sender as HttpApplication;

            context.PostAcquireRequestState -= new EventHandler(context_PostAcquireRequestState);

            context.EndRequest -= new EventHandler(context_EndRequest);

        }

        void context_PostAcquireRequestState(object sender, EventArgs e)

        {

            this.RegisterCompressFilter();    

        }

        private void RegisterCompressFilter()

        {

            HttpContext context = HttpContext.Current;

            if (context.Handler is StaticFileHandler 

                || context.Handler is DefaultHttpHandler) return;

            HttpRequest request = context.Request;            

            string acceptEncoding = request.Headers[“Accept-Encoding”];

            if (string.IsNullOrEmpty(acceptEncoding)) return;

            //if (request.FilePath.EndsWith(“.ashx”)) return;

            if (request.FilePath.Contains(“.”))

            {

                return;

            }

            acceptEncoding = acceptEncoding.ToUpperInvariant();

            HttpResponse response = HttpContext.Current.Response;

            if (acceptEncoding.Contains(“GZIP”))

            {

                response.AppendHeader(“Content-encoding”, “gzip”);

                response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);

            }

            else if (acceptEncoding.Contains(“DEFLATE”))

            {

                response.AppendHeader(“Content-encoding”, “deflate”);

                response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);

            }

        }

        #endregion

    }

}

    • #ASP.Net MVC
    • #http
    • #compression
  • 3 months ago
  • 56
  • Comments
  • Permalink
  • Share
    Tweet

MongoDB @ KhojKhabar

(Migrated from old wordpress blog post dated 7/12/2011)

These days NoSQL databases are getting very popular. These databases don’t have a SQL interface for querying data and a lot of times don’t store data as collections of rows and tables. MongoDB is one such database that stores data in form of BSON(a form of JSON) documents and collections. The data in MongoDB can be queried using a REST API. It has a .Net driver available and there are a couple of hosted MongoDB cloud hosted providers with a free plan so I thought I will try to convert KhojKhabar data store from SQLite to MongoDB.

Out of MongoHQ and MongoLab I decided to use MongoLab as it allows 240 MB (vs 16 MB by MongoHQ). MongoLab also provides an option to host your MongoDB instance on Amazon or Rackspace cloud. So if your app is hosted on one of these providers you can reduce latency by hosting MongoDB on the same provider. Moving data storage of khojkhabar to MongoDB was not that difficult as the driver usage is like a combination of dataset style programming and fluid function calls (like used in subsonic 2), so someone with experience these can pick it up easily. There is also a good tutorial available at MongoDb.org, which was very helpful. Now the live khojkhabar site is using MongoLab as the data store.In khojkhabar right now I store the news data of last 10 days only so I think 240 MB storage quota should be fine for now.

    • #khojkhabar
    • #mongodb
    • #mongolab
  • 3 months ago
  • 2
  • Comments
  • Permalink
  • Share
    Tweet

AppHarbor - Heroku like service for ASP.Net

(Migrated from old wordpress blog post dated 2/17/2011)

I recently came across AppHarbor. It looks like another interesting startup to come out of  YCombinator. Its a Heroku like service that builds, runs unit tests and deploys your ASP.Net code for you. It works with Git so to upload the code all you need to do is git push. They provide shared and dedicated databases with option of MS SQL server and MySQL.  Whats cool is that they provide a free application instance per applicaiton. So you pay only if your app grows to need another instance. Now if you need to test or host your next ASP.Net MVC app you know where to go.

Tonight I plan to play with it and see how it works for an ASP.Net MVC app I have been working on using Subsonic and SQLite. Details later.

    • #appharbor
    • #asp.net
  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

jQuery ListCollapse

(Migrated from old wordpress blog post dated 1/9/2011)

This plugin is meant to collapse/expand a list of items that grows beyond certain predefined number of items. It filters and collapses a list when data is bound, added or removed. If number of items are more than specified number n it displays first n items and displays an expand(customizable) link. When a user clicks on this link it displays the entire list and changes this link to collapse(customizable).

Download listcollapse

(Note: This plugin is based on Collapsorz 1.1 created by Aaron Kuzemchak. I have customized it to be able to be called whenever an item is added or removed and some minor modifications is think were required for dynamic list where items are added and removed on the fly)

Usage

$(listelement).listcollapse();

$(listelement).listcollapse(options);

Options

(All parameters are optional)

toggle

To select elements that you wish to collapse. By default it will collapse all direct children of the list selected.

maximum

Maximum number of elements to show. Collapses all items beyond this number. It defaults to 5.

showText

Text to be shown for the expand link. It defaults to Show.

hideText

Text to be shown for the collapse link. It defaults to Hide.

linkLocation

Whether to display expand/collapse link before or after the selected list. You can choose one of the following settings:

  • after—Places the link after the list. This is the default setting.
  • before—Places the link before the list.

defaultState

Whether the list should be displayed expanded or collapsed by default.

  • collapsed—List will be collapsed by default. This is the default setting.
  • expanded—List will be expanded by default.

wrapLink

HTML to wrap around the link.

    • #jquery
  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

Load balancing

(Migrated from old wordpress blog post dated 12/22/2010)

I read an interesting article today about load balancing. Its a must read for anyone looking to scale a website or anyone just simple interested in knowing how these high traffic websites scale on the web server side. This documents explains different ways of doing it like hardware based approach and software based approach. It also explains various related factors like cookie persistence and using SSL certs in load balancing.

http://www.exceliance.fr/en/ART-2006-making%20applications%20scalable%20with%20LB.pdf

    • #load balancing
  • 3 months ago
  • Comments
  • Permalink
  • Share
    Tweet

Adding a new volume to EC2 linux instance

(Migrated from old wordpress blog post dated 12/22/2010)

Our postgres EC2 linux instance ran out of disk space so I had to add another volume to the server. Following is what I did -

  1. Login to http://aws.amazon.com and go to EC2 tab.
  2. Go to volumes section and create a new volume.
  3. Enter the size of volume and select the availability zone. I generally skip creating a snapshot unless there is explicit need.
  4. Once the volume is created and its status is available, select it and click attach button. Select instance id of the instance to which it needs to be associated and map it to /dev/sda2
  5. Login to the server as root and execute following commands -
       mkfs -t ext3 /dev/sda2   #dont do it if creating from an existing snapshot
       echo "/dev/sda2  /disk2  ext3     noatime  0 0" >> /etc/fstab
       mkdir /disk2
       mount /disk2
       df -h #you should now see /disk2 as a new volume

    • #EBS
    • #EC2
  • 3 months ago
  • 1
  • Comments
  • Permalink
  • Share
    Tweet

Moving postgres data folder

(Migrated from old wordpress blog post dated 12/22/2010)

Our postgres server ran out of disk space on our EC2 server so we figured we should move the postgres data folder to a new bigger volume. We created an additional volume mounted to /disk2 and then moved the data folder as follows -

1. Login to shell as root. Stop the Postgres if running

$service postgresql-9.0 stop

2. Copy the data folder to new location

$cp -R /var/lib/pgsql/9.0/data  /disk2/pgdata/

3. Modify postgres startup script to point to new data directory

In  /etc/init.d/postgresql  file,  change the value of  PGDATA  variable to new location  which is /disk2/pgdata/data

4. Start the db server

$ service postgresql start

    • #postgres
  • 3 months ago
  • 5
  • Comments
  • Permalink
  • Share
    Tweet

Setting up replication in Postgres 9.0

 (Migrated from old wordpress blog post dated 12/22/2010)

For picksie I had to set up streaming replication in Postgres 9.0. We have a master server which is used for read/write and a hot standby where we want the data to updated using streaming replication. Following is a summary of what I did

1. Install postgres on both master and standby

2. Allow standby server to connect to the master by editing pg_hba.conf

host replication postgres 192.168.0.20/22 trust

3. Setup streaming on master by updating postgresql.conf

wal_level = hot_standby

max_wal_senders = 5

wal_keep_segments = 32

archive_mode = on

archive_command = ‘cp %p /path_to/archive/%f’

4. Start postgres on master

5. Make base backup on master

$ psql -c “SELECT pg_start_backup(‘label’, true)”

$ rsync -a ${PGDATA}/ standby:/srv/pgsql/standby/ —exclude postmaster.pid

$ psql -c “SELECT pg_stop_backup()”

6. Do authentication and streaming related configuration in postgresql.conf and pg_hba.conf in standby server so that when needed it can be promoted as master with less effort.

7. Set standby server as a hot standby by updating postgresql.conf

hot_standby = on

8. Create recovery.conf with following configuration on standby server in same folder as postgresql.conf

standby_mode = ‘on’

primary_conninfo = ‘host=192.168.0.10 port=5432 user=postgres’

trigger_file = ‘/path_to/trigger’

restore_command = ‘cp /path_to/archive/%f “%p”’

Note: trigger file is the file, presence of which will make the standby server to stop replication and failover

9. Start postgres service on standby and it will start replication

Reference: http://wiki.postgresql.org/wiki/Streaming_Replication

$ $EDITOR postgresql.conf

listen_addresses = '192.168.0.10'

$ $EDITOR pg_hba.conf

# The standby server must have superuser access privileges.
host  replication  postgres  192.168.0.20/22  trust
    • #postgres
    • #replication
  • 3 months ago
  • 5
  • Comments
  • Permalink
  • Share
    Tweet

Interesting projects at work

(Migrated from old wordpress blog post dated 9/21/2010)

Cloud, iPad and SAAS are some of the most sought after buzz words of software industry right now.  Apart from looking after the IT infrastructure, my work involves projects bridge and picksie that together involve all of these.

Bridge is a product targeting enterprise collaboration space, built on my stack of choice for web apps these days ASP.Net/Subsonic/MySQL. We are looking into SAASifying this app and see if we can host it on the cloud using some knowledge that I have gained working on picksie.

Picksie is a web app for iPad, hosted on the cloud. My involvement is designing the infrastructure, making sure it is scalable and easily managable. Infrastructure of a consumer web app is really a completely different ballgame compared to enterprise IT infrastructure. So I am really getting to learn alot as picksie moves from demo to beta and from a simple 3 server setup to a more elaborate and load balanced and more elaborate setup.

    • #cloud
    • #ipad
    • #saas
    • #work
  • 3 months ago
  • 6
  • Comments
  • Permalink
  • Share
    Tweet

About

.... technology, business, technology in business, business in technology and more ....
  • Projects

Me, Elsewhere

  • @yatendra on Twitter
  • Linkedin Profile
  • yatendra on github

Twitter

loading tweets…

  • RSS
  • Random
  • Archive
  • Ask me anything
  • Mobile

Effector Theme by Carlo Franco.

Powered by Tumblr