Ninject with custom attribute and filter

I am implementing a custom credit check filter for a controller action. I use Ninject as my Ioc container. First, create a action filter and implement IActionFilter interface, implement the OnActionExecuted() and OnActionExecuting() methods. Within the credit checking functions, it needs a CreditService to check against the repository and database. So I have created the parameterized constructor using Ninject to inject the CreditService object.

 public class CreditCheckFilter : IActionFilter
    {
        private ICreditService _creditService { get; set; }

        public CreditCheckFilter(ICreditService myService)
        {
            _creditService = myService;
        }

        public void OnActionExecuted(ActionExecutedContext filterContext)
        {

        }

        public void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var userName = filterContext.HttpContext.User.Identity.Name;
            var bal = _creditService.GetTokenBalance(WebSecurity.GetUserId(userName));
            if (bal <= 0)
            {
                RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
                redirectTargetDictionary.Add("action", "GetTokens");
                redirectTargetDictionary.Add("controller", "Account");
                filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
               
            }
        }
    }

Now Let’s create a CreditCheckAttribute that implements FilterAttribute, this is the attribute we put against the controller action,

    public class CreditCheckAttribute : FilterAttribute { }

Now, we need to use Ninject BindFilter() method to bind our CreditCheckFilter, when a controller action has the attribute CreditCheckAttribute. In the ninject kernel, do this,

            kernel.BindFilter(FilterScope.Action, 0).WhenActionMethodHas();

The last step is to put CreditCheckAttribute() against the controller action,

        [CreditCheckAttribute]
        public ActionResult Playlist(TestViewModel model)
        {
            return View(model);
        }
Advertisements

Create a custom HtmlHelper.TextBoxFor() to display label text as “placeholder” integrated with twitter bootstrap

We have TextBoxFor() and LabelFor() in MVC, they are binded with my model. If you are using twitter bootstrap and want to display the model property label as a placeholder in textboxes, what should you do.

Here is what I have done.

My model:

public class User
{
    [Display(Name="User name")]
    public string Username {get;set;}
}

Normally in the view we put,

@model User

<ol>
    <li>
        @Html.LabelFor(p=&gt;p.Username)
        @Html.TextBoxFor(p=&gt;p.Username)
    </li>
</ol>

image

If you want to display it as placeholder in twitter bootstrap,

image

Let’s create a TextBoxPlaceHolderFor() extension method of the htmlhelpers, remember to reference the namespace,

using System.Web.Mvc.Html;

So you can re-use the HtmlHelper.TextBoxFor() in your TextBoxPlaceHolderFor() method.

Here is the code with three overloads,

public static class TextBoxForExtensions
    {
        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression, object htmlAttributes)
        {
            var dict = new RouteValueDictionary(htmlAttributes);
            return html.TextBoxPlaceHolderFor(expression, dict);
        }
        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression)
        {
            var htmlAttributes = new Dictionary();
            return html.TextBoxPlaceHolderFor(expression, htmlAttributes);
        }

        public static MvcHtmlString TextBoxPlaceHolderFor(this HtmlHelper html, Expression<Func> expression, IDictionary htmlAttributes)
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
            string htmlFieldName = ExpressionHelper.GetExpressionText(expression);
            string labelText = metadata.DisplayName ?? metadata.PropertyName ?? htmlFieldName.Split('.').Last();
            if (!String.IsNullOrEmpty(labelText))
            {
                if (htmlAttributes == null )
                {
                    htmlAttributes = new Dictionary();
                }
                htmlAttributes.Add("placeholder", labelText);
            }
            return html.TextBoxFor(expression, htmlAttributes);
        }

    }

When you call the methods you can do,

@Html.TextBoxPlaceHolderFor(m => m.UserName)

//or 

@Html.TextBoxPlaceHolderFor(m => m.UserName, new { @class="form-field" })


You are done. Now get your form neat and clean using the TextBoxPlaceHolderFor().

ajax request only action filter, MVC

I created a handy ajax request filter, that you can apply to your ajax action method call.

    public class AjaxRequestAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if(!filterContext.HttpContext.Request.IsAjaxRequest())
                	//do something
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {

        }
    }
    [AjaxRequest]
    public ActionResult AjaxActionMethod()
    {
        		//to do
    }

Use Enum & Description to create select list item in MVC

There are always situations that we make good use of enums in projects. I like to use an Enum values to make my select list to save select-list-creation time. Here is what I always do,

Create an Enum, Eg. MonthFilter

 public enum MonthFilter
    { 
        [Description("Last Month")]
        LastMonth = 1,
        [Description("Last 3 Months")]
        Last3Months = 3,
        [Description("Last 6 Months")]
        Last6Months = 6,
        [Description("Last 12 Months")]
        Last12Months = 12,
        [Description("Year on Year")]
        YearOnYear = -1
    }

And then create the SelectListItem using this Enum and its descriptions,

    var monthFilters = from MonthFilter n in Enum.GetValues(typeof(MonthFilter))
                     select new SelectListItem { 
                            Value = ((int)n).ToString(), 
                            Text = Enumerations.GetEnumDescription(n)
                     };

Last step is create the dropdown list, by passing in the “monthFilters” Select List Items,

                @Html.DropDownList("MonthFilterDropDown", monthFilters, "Select...", null)

Handle Ajax request with expired forms authentication, in MVC ajax.form

There is a situation where I display a screen dynamically load a partial view using ajax.

And when the authentication expires, user initiates an ajax call, then the action is being called without authorization.

It actually returns a login view inside the DOM object on the page rather than returns a expected content with partial view.

The users will have bad UI experience and get confused.

Here I demonstrate the scenario to show you what exactly happens and what we can do about it.

1. Create an ajax.form which simply returns a partial view.


@using (Ajax.BeginForm("AjaxTestNoHandle", "Home", new AjaxOptions { UpdateTargetId = "testcontainer2"}, new { id="test-form"}))
{ 
    
}

<div id="testcontainer2">
</div>

2. Create the action method with “Authorize” attribute and return a partial view “TestPartial”

  [Authorize]
        public ActionResult AjaxTestNoHandle()
        {
            return PartialView("TestPartial");
        }

3. Run the application and click button “No Redirection Handling”. Because the user is not authenticated and it returns a partial view with the whole “Login” page. (I made login page with black background for displaying purpose)

image

Now let’s create  a customized authorize attribute to handle this situation.

1.  Create a CustomizedAuthorizeAttribute to handle login redirection if authentication is expired.  I only override the action HandleUnauthorizedRequest to detect if request is ajax, return a json result with a redirect url.

    public class CustomizedAuthorizeAttribute : AuthorizeAttribute
    {
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            string url = string.Format("{0}?returnUrl={1}",System.Web.Security.FormsAuthentication.LoginUrl,
                filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
            if (filterContext.HttpContext.Request.IsAjaxRequest())
            {
                var redirectResult = filterContext.Result as RedirectResult;
                if (filterContext.Result is RedirectResult)
                {
                    // It was a RedirectResult => we need to calculate the url
                    var result = filterContext.Result as RedirectResult;
                    url = UrlHelper.GenerateContentUrl(result.Url, filterContext.HttpContext);
                }
                else if (filterContext.Result is RedirectToRouteResult)
                {
                    // It was a RedirectToRouteResult => we need to calculate
                    // the target url
                    var result = filterContext.Result as RedirectToRouteResult;
                    url = UrlHelper.GenerateUrl(result.RouteName, null, null, result.RouteValues, RouteTable.Routes, filterContext.RequestContext, false);
                }
                filterContext.Result = new JsonResult
                {
                    Data = new { Redirect = url },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
            }
            else
            {
                //non-ajax request
                base.HandleUnauthorizedRequest(filterContext);
            }
            
        }

2. Create controller action method with CustomizedAuthorize

        [CustomizedAuthorize]
        public ActionResult AjaxTest()
        {
            return PartialView("TestPartial");
        }

3. Create ajax.form post to “AjaxTest” method.


@using (Ajax.BeginForm("AjaxTest", "Home", new AjaxOptions { UpdateTargetId = "testcontainer" }, new { id="test-form"}))
{ 
    
}

<div id="testcontainer">
</div>

4. Click “Redirection Handling” button on the page without login. In FireBug – Console, you can see a json is returned

{"Redirect":"/Account/Login?returnUrl=%2fHome%2fAjaxTest%3fLength%3d4"}

5. Now let’s add a javascript function to handle the json and redirect to the returned url. Add a OnSuccess event in “AjaxOptions” call “validate”


@using (Ajax.BeginForm("AjaxTest", "Home", new AjaxOptions { UpdateTargetId = "testcontainer", OnSuccess="validate" }, new { id="test-form"}))
{ 
    
}

<div id="testcontainer">
</div>

6. Create javascript function validate.


    function validate(data) {
        if (data.Redirect)
            window.location = data.Redirect;
    }

7. Here you go, all done! When you click the “Redirection Handling” button, it will detect the forms authentication login url and return to the login page inside the ajax request!

Cools!

To download the demo project, go to codeplex https://ajaxrequestauthentic.codeplex.com/

CSS media queries not working with IE7, IE8

I have worked with media queries on a project for a while and notice that it is not actually working well in IE 7 or IE 8.

Here is a simple sample of the media query I have.

@import url("desktop.css") screen;
@import url("ipad.css") only screen and (device-width:768px);

To make sure things working alright in those browsers, you will need a separate css file for those two. And a detection of “if it is less than IE9”, use the “style-ie.css”



<link rel="stylsheet" type="text/css" media="all" href="style.css"/>
<!--[if lt IE 9]>
<link rel="stylesheet" type="text/css" media="all" href="style-ie.css"/>
<![endif]-->

Validation Summary is not cleared after Ajax.Form successfully posted, MVC 3

My scenario is after the invalid form values are posted through ajax.form, and the validation summary is displaying on the view. After I filled in all the required fields and posted the form with valid values, the post back data is correctly displaying, but the validation summary is remaining on the page.

Here are some code snippets,

The view contains two date picker (textbox) which are required fields.

    @model ClosedTicketByProductReportViewModel
    @using (Ajax.BeginForm("ClosedTicketByProductReport", "Report", new AjaxOptions { UpdateTargetId = "closedTicketByProductGrid", LoadingElementId = "loadingcontainer" }))
    { 
        @Html.ValidationSummary()
                
        <div class="control-group">
            Date From
            <div class="controls">
                @Html.TextBoxFor(p =&gt; p.DateFromForClosedProduct, new Dictionary { { "class", "small" }, { "type", "date" }, { "data-datepicker", "datepicker" } })
            </div>
        </div>
        <div class="control-group">
            Date To
            <div class="controls">
                @Html.TextBoxFor(p =&gt; p.DateToForClosedProduct, new Dictionary { { "class", "small" }, { "type", "date" }, { "data-datepicker", "datepicker" } })
            </div>
        </div>

        Generate Report
        
    }

Here is the view model I created for the view,

    public class ClosedTicketByProductReportViewModel
    {  

        [Required(ErrorMessage = "Please select Date From")]
        public DateTime? DateFromForClosedProduct { get; set; }

        [Required(ErrorMessage = "Please select Date To")]
        public DateTime? DateToForClosedProduct { get; set; }

        //report data
        public List Cases { get; set; }

    }

After I click submit without entering any dates, it displays the error messages as expected.

image

Then I enter the dates, and click submit again. The post back data is retrieved from controller action, but the validation summary remains on the page. As you can see the validation errors on the textbox itself are gone. Ajax.Form is only lack of support on the validation summaries.

image

Here is my fix, I don’t think it is great, but it is a work around.

    @using (Ajax.BeginForm("ClosedTicketByProductReport", "Report", new AjaxOptions { OnSuccess = "HideValidationSummary", UpdateTargetId = "closedTicketByProductGrid", LoadingElementId = "loadingcontainer" }))
    { }

I created a javascript function and call it on OnSuccess = "HideValidationSummary, the javascript function just hide the validation summary div.

function HideValidationSummary() {
    $(".validation-summary-errors").hide();
}

If anyone come across my blog post and has a better solution, please leave me a message. Much appreciated.