понедельник, 24 июня 2013 г.

Getting the HTTP Referrer in ASP.NET

You could use the UrlReferrer property of the current request:
Request.UrlReferrer
This will read the Referer HTTP header from the request which may or may not be supplied by the client (user agent).

воскресенье, 23 июня 2013 г.

Addition to ASP.NET MVC Localization - Using routing

In my previous post I described the way of localization using session, but in real-world applications it's definitely not the best way of localization. Now I'll describe very simple and very powerful way of storing it in URL using routing mechanism.

Also this way of localization will not require OutputCache tricks described in previous post

The goal of this post is to show how to get URL  like this/{culture}/{Controller}/{Action}... in your application like /ru/Home/About.


Custom Route Handlers

First of all we'll need to extend standard MvcRouteHandler class. One classMultiCultureMvcRouteHandler for routes that will use culture in params andSingleCultureMvcRouteHandler class (will be used as a marker, no implementation changes)

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var culture = requestContext.RouteData.Values["culture"].ToString();
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
        return base.GetHttpHandler(requestContext);
    }
}

In the overridden GetHttpHandler before calling it's base implementation we just get "culture" param from RouteData collection, create CultureInfo object and set it to current thread current culture. So here is a place where we set culture and will not useApplication_AcquireRequestState method in Global.asax

public class SingleCultureMvcRouteHandler : MvcRouteHandler {}

As I mention this class will be used only for marking some routes for case if you'll need some routes to be culture independent.


Registering routes

Now lets go to Global.asax file where we have route registering methodRegisterRoutes(). Right after last route mapping add foreach statement code snippet like in the following example.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
         "Default", // Route name
         "{controller}/{action}/{id}", // URL with parameters
         new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
    );

    foreach (Route r in routes)
    {
        if (!(r.RouteHandler is SingleCultureMvcRouteHandler))
        {
            r.RouteHandler = new MultiCultureMvcRouteHandler();
            r.Url = "{culture}/" + r.Url;
           //Adding default culture 
           if (r.Defaults == null)
           {
               r.Defaults = new RouteValueDictionary();
           }
           r.Defaults.Add("culture", Culture.ru.ToString());

           //Adding constraint for culture param
           if (r.Constraints == null)
           {
               r.Constraints = new RouteValueDictionary();
           }
           r.Constraints.Add("culture", new CultureConstraint(Culture.en.ToString(), 
Culture.ru.ToString()));
        }
   }

}


OK, lets go through this code... So for each route we first of all check whether its handler type is SingleCultureMvcRouteHandler or not... So if not we change RouteHandler property of the current route to MultiCulture one, add prefix to Url, add default culture and finally add constraint for culture param checking.

public class CultureConstraint : IRouteConstraint
{
    private string[] _values;
    public CultureConstraint(params string[] values)
    {
        this._values = values;
    }

    public bool Match(HttpContextBase httpContext,Route route,string parameterName,
                        RouteValueDictionary values, RouteDirection routeDirection)
    {

        // Get the value called "parameterName" from the 
        // RouteValueDictionary called "value"
        string value = values[parameterName].ToString();
        // Return true is the list of allowed values contains 
        // this value.
        return _values.Contains(value);

    }

}

And enum of cultures
    public enum Culture
    {
        ru = 1,
        en = 2
    }


Simple culture switching mechanism

For changing culture we'll need following simple action which I placed in AccountController

public ActionResult ChangeCulture(Culture lang, string returnUrl)
{
     if (returnUrl.Length >= 3)
     {
         returnUrl = returnUrl.Substring(3);
     }
     return Redirect("/" + lang.ToString() + returnUrl);
}

and partial view with languages links - CultureSwitchControl.ascx


<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<%= Html.ActionLink("eng", "ChangeCulture", "Account",
    new { lang = (int)MvcLocalization.Helpers.Culture.en, returnUrl =  
    this.Request.RawUrl }, new { @class = "culture-link" })%>

<%= Html.ActionLink("рус", "ChangeCulture", "Account",
    new { lang = (int)MvcLocalization.Helpers.Culture.ru, returnUrl = 
    this.Request.RawUrl }, new { @class = "culture-link" })%>



Single culture case

Finally, if we need some single culture route all we need to do is to set RouteHandlerproperty to SingleCultureMvcRouteHandler  like this

routes.MapRoute(
          "AboutRoute",
          "About",
          new { controller = "Home", action = "About"}
   ).RouteHandler = new SingleCultureMvcRouteHandler();


So, that's it :) Localization without using Session, without problems with OutputCache(will be explained in my next post) and with use of routing.

Here is the link of  source code(project created in VS2010) 

понедельник, 17 июня 2013 г.

Javascript Namespace Declaration

I use the approach found on the Enterprise jQuery site, here
Here is their example showing how to declare private & public properties and functions. Everything is done as a self-executing anonymous function.
(function( skillet, $, undefined ) {
    //Private Property
    var isHot = true;

    //Public Property
    skillet.ingredient = "Bacon Strips";

    //Public Method
    skillet.fry = function() {
        var oliveOil;

        addItem( "\t\n Butter \n\t" );
        addItem( oliveOil );
        console.log( "Frying " + skillet.ingredient );
    };

    //Private Method
    function addItem( item ) {
        if ( item !== undefined ) {
            console.log( "Adding " + $.trim(item) );
        }
    }    
}( window.skillet = window.skillet || {}, jQuery ));
So if you want to access one of the public members you would just go skillet.fry() or skillet.ingredients
What's really cool is that you can now extend the namespace using the exact same syntax.
//Adding New Functionality to the Skillet
(function( skillet, $, undefined ) {
    //Private Property
    var amountOfGrease = "1 Cup";

    //Public Method
    skillet.toString = function() {
        console.log( skillet.quantity + " " + 
                     skillet.ingredient + " & " + 
                     amountOfGrease + " of Grease" );
        console.log( isHot ? "Hot" : "Cold" );
    };    
}( window.skillet = window.skillet || {}, jQuery ));

How do I get migration seed data for a default user with pw and role, which works locally to work when I publish it to Azure?

I have an MVC 4 EF web project with a code-first sql server db that very closely follows the basic and usual structure of any you would see as an example anywhere on the web.
I have read EVERY article posted on the internet concerning how to do what I need and none of them so far make any difference to my situation. I am not doing anything extraordinary, I just took out the 'Register' new user capability as needed by my project, and am trying to create a couple of users as login points by including them in the Migrations/Configuration.cs file like this:
    protected override void Seed (EntityContext context)
    {
        if (!WebSecurity.Initialized)
            WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);

        if (!Roles.RoleExists("Administrator"))
            Roles.CreateRole("Administrator");

        if (!WebSecurity.UserExists("Owner"))
            WebSecurity.CreateUserAndAccount("Owner", "password");

        if (!Roles.GetRolesForUser("Owner").Contains("Administrator"))
            Roles.AddUsersToRoles(new[] { "Owner" }, new[] { "Administrator" });

        if (!WebSecurity.UserExists("fred"))
            WebSecurity.CreateUserAndAccount("leemid", "pw");
        if (!Roles.GetRolesForUser("leemid").Contains("Administrator"))
            Roles.AddUsersToRoles(new[] { "leemid" }, new[] { "Administrator" });

        base.Seed(context);
    }
Everything works beautifully locally, but when I publish to my Azure account, where everything is set up properly as near as I can tell, the app appears correctly except that I can't login as my seeded user. I have connected to my Azure db using SQL Management Studio and all of the UserProfile and webpages_ tables are there, the app specific ones are not because the code-first hasn't had a chance to build the yet (I assume), but there are no users or roles. Locally, everything is built and populated as expected.
I have added the often suggested code in the root Web.config file thus:
<system.web>
  <roleManager enabled="true" defaultProvider="SimpleRoleProvider">
      
          <clear/>
          <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
      </providers>
  </roleManager>
  <membership defaultProvider="SimpleMembershipProvider">
      
          <clear/>
          <add name="SimpleMembershipProvider"
               type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/>
      </providers>
  </membership>
It certainly took all of this to accomplish successful publishing of the site, but I am frustrated by the lack of success in seeding the users. Any one solved this already?