The important functionality of any ecommerce system is to transfer the content using SSL when the user enters the user name & password or while accepting the credit card info. Steve Sanderson has blogged about this and unfortunately it didn’t work for me. Also MVC Futures has RequiresSSL extension which transform the content to secure. But in a typical system, we need to redirect back to HTTP as well, i.e if there is a booking flow, after we get the credit card and validating the payment details, the confirmation page might or should be in HTTP.

There are various solution available, i have decided to use the MVC Futures code and slightly modify the same to implement this functionality.

public sealed class SwitchSsl : FilterAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if (!Enable)
            return;

        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
        if (!filterContext.HttpContext.Request.IsSecureConnection && Mode==Protocol.https)
        {
            if (!this.Redirect)
            {
                throw new HttpException(0x193, "Must use SSL");
            }
            RedirectUrl(Protocol.https, filterContext);
        }
        else if (filterContext.HttpContext.Request.IsSecureConnection && Mode==Protocol.http)
        {
            RedirectUrl(Protocol.http, filterContext);
        }
    }

    public bool Redirect { get; set; }

    public Protocol Mode { get; set; }

    public bool Enable
    {
        get {
            return Convert.ToBoolean(ConfigurationManager.AppSettings.Get("EnableSsl"));
        }
    }

    private void RedirectUrl(Protocol scheme, AuthorizationContext filterContext)
    {
        UriBuilder builder2 = new UriBuilder();
        builder2.Scheme = scheme.ToString();
        builder2.Host = filterContext.HttpContext.Request.Url.Host;
        builder2.Path = filterContext.HttpContext.Request.RawUrl;
        UriBuilder builder = builder2;
        filterContext.Result = new RedirectResult(builder.ToString());
    }

    public enum Protocol
    {
        http,
        https
    }

}

Enable property is an optional configuration in the web.config, so that we can enable this only in the production environment. And in the controller action,we can call like below.

[SwitchSsl(Redirect = true, Mode=SwitchSsl.Protocol.https)]
public ActionResult Reserve()

Happy Coding!


 
Categories: ASP.NET MVC

It is important to have logging enabled in any production site which will be used by the technical team to debug the issues or monitor the health of the site. We chose BitFactory logging (Now the author is calling as Termite) to implement the logging in WCF Services, Business Layer & ASP.NET MVC Sites.

Why did we choose BitFactory?

  • It is very easy to implement.
  • Absolutely working with complex categories (we have 43 projects in a solution with more than 25 categories configured for logging)
  • No issue with Multithreading or Filelocking issue.

I have used Nlog, Log4net, MS Enterprise library & Elmah in various projects, somehow my choice at this time is BitFactory. Other frameworks are good on their own flavor and i am not against any frameworks whether it is commercial or open source.

In ASP.NET MVC we have to use HandleError attribute to redirect to the customized error page and i would like to implement the logging using the same attribute. Thanks to Mark Seemann for the poor man’s Dependency Injection for the logging. I used the same snippet but tried to integrate BitFactory logging.

ErrorHandlingActionInvoker.cs

public class ErrorHandlingActionInvoker : ControllerActionInvoker
{
    private readonly IExceptionFilter filter;

    public ErrorHandlingActionInvoker(IExceptionFilter filter)
    {
        if (filter == null)
        {
            throw new ArgumentNullException("filter");
        }
        
        this.filter = filter;
    }

    protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        var filterInfo =base.GetFilters(controllerContext, actionDescriptor);
        filterInfo.ExceptionFilters.Add(this.filter);
        return filterInfo;
    }

}

ErrorHandlingControllerFactory.cs

public class ErrorHandlingControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(RequestContext requestContext,string controllerName)
    {
        var controller =base.CreateController(requestContext,controllerName);
        var c = controller as Controller;
        if (c != null)
        {
            c.ActionInvoker = new ErrorHandlingActionInvoker(new LoggingExceptionFilter(new HandleErrorAttribute()));
        }
        return controller;
    }

}

LoggingExceptionFilter.cs

public class LoggingExceptionFilter : IExceptionFilter
{
    private readonly IExceptionFilter filter;
    private readonly ConfigLogger logWriter = ConfigLogger.Instance;

    public LoggingExceptionFilter(IExceptionFilter filter) 
    { 
        if (filter == null) 
        { 
            throw new ArgumentNullException("filter"); 
        } 
        if (logWriter == null) 
        { 
            throw new ArgumentNullException("logWriter"); 
        } 
        this.filter = filter; 
    }

    public void OnException(ExceptionContext filterContext)
    {
        this.logWriter.LogError(filterContext.Exception);
        this.filter.OnException(filterContext);
    }
}

Web.Config

<BitFactory.Logging name="global" xmlns="http://BitFactory.Logging">
  <rollingDateFileLoggers>
    <rollingDateFileLogger isEnabled="true" name="FrontEndLogger" formattedFileName="C:\Logs\services_{timestamp:yyyyMMdd}.log"/>
  </rollingDateFileLoggers>
</BitFactory.Logging>

Add this line in Global.asax.cs

ControllerBuilder.Current.SetControllerFactory(new ErrorHandlingControllerFactory());

Happy Coding!


 
Categories: ASP.NET MVC

In my current project I need to load the user control dynamically without post back. After some googling found couple of posts which converts the partial view as string. The issue here is it doesn’t support viewcontext, in my case i have custom view engine which implements localization. Earlier i have gone through this useful cheat sheet. Below is the simple implementation which works with custom view engine and view context.

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult LoadTopDestinations()
{
    return PartialView("TopDestinations");
}

And this is how i call the controller from the javascript using jquery post.

function TopDestinationClick() {
    proxy.invoke("/Home/LoadTopDestinations", "", TopDestinationCallback, OnError);

}

Thanks to Rick Strahl for the easy service proxy for jquery post.

function serviceProxy(serviceUrl)
{
    var _I = this;
    this.serviceUrl = serviceUrl;
    
    // *** Call a wrapped object
    this.invoke = function(method, data, callback, error) {
        // *** The service endpoint URL        
        var url = _I.serviceUrl + method;
        //alert(url);
        $.ajax({
            url: url,
            data: '',
            type: "GET",
            contentType: "html",
            timeout: 10000,
            success: callback,
            error: OnError
        });
    }
}

// *** Create a static instance
var serviceUrl = "";
var proxy = new serviceProxy(serviceUrl);

And the callback event, just use jquery.html() method to load the partial view result.


 
Categories: ASP.NET MVC

We used to bind model class in ASP.NET MVC dropdownlist like below

<%= Html.DropDownList("ApprovalStatus", Model.ApprovalStatus, new { @class = "select", onchange = "javascript:CheckApprovalStatus(this);" })%>

The HTML output of the above is

<select class="select" id="ApprovalStatus" name="ApprovalStatus" >
<option value="Approved">Approved</option>
<option value="Rejected">Rejected</option>
<option value="InProgress">In Progress</option>
</select>

What if you would like to have Keys & Value separately and like below and if the model class is KeyValuePair

<select class="select" id="Country" name="Country">
<option value="US">United States</option>
<option value="IN">India</option>
<option value="SG">Singapore</option>
</select>

then it should bind with Keys & Values.

CountryList = new SelectList(Country.ToList(),"Key","Value");

Happy Coding!


 
Categories: ASP.NET MVC