by Joakim

Detecting Browser Types and Browser Capabilities in Asp.Net

The following helper class allows you to detect browser types and browser capabilities in Asp.Net. It works in both MVC-controllers/views and WebAPI-controllers (and I would think it should work with WebForms as well).

using System;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.Web.Configuration;

namespace MyProject.Helpers
{
    public static class BrowserInfoHelper
    {
        public static string GetUserAgent()
        {
            return HttpContext.Current.Request.UserAgent;
        }

        public static HttpBrowserCapabilities GetCapabilities()
        {
            var capabilities = new HttpBrowserCapabilities();

            var hashtable = new Hashtable(180, StringComparer.OrdinalIgnoreCase);
            hashtable[string.Empty] = GetUserAgent();
            capabilities.Capabilities = hashtable;

            var capabilitiesFactory = new BrowserCapabilitiesFactory();
            var headers = new NameValueCollection();
            capabilitiesFactory.ConfigureBrowserCapabilities(headers, capabilities);
            capabilitiesFactory.ConfigureCustomCapabilities(headers, capabilities);

            return capabilities;
        }
    }
}

 

Then use it, for example in an MVC-view, like so;

@MyProject.Helpers
@{
    var browserCapabilities = BrowserInfoHelper.GetCapabilities();
    var browserIsOldIE = browserCapabilities.Browser.Equals("IE", StringComparison.InvariantCultureIgnoreCase) && browserCapabilities.MajorVersion < 9;
}
@if (browserIsOldIE)
{
    <div>Please upgrade your browser!</div>
}
by Joakim

JavaScript file missing from resource bundle (ASP.NET Web Optimization Framework)

I came across some odd behavior related to the Microsoft ASP.NET Web optimization Framework the other day, when I was creating a JavaScript resource bundle in an application I was working on. When I ran the website with optimizations enabled, the script file showed up as part of the bundle, but if I turned optimizations off the script include for the file in question was nowhere to be found.

For the application I was working on, which was only a proof of concept, I had simply copied the minified version of the JavaScript libraries I needed into the Script manually. I then included my libraries in a script bundle like so;
BundleTable.Bundles.Add(newScriptBundle(“~/bundles/jquery”).Include(“~/Scripts/jquery-1.8.2.min.js”));

If you have used this bundling functionality before, you probably know that if you include a JavaScript file in a bundle and you enable optimizations, the file will be minified and added to a single resource together with any other JavaScript files in the bundle. During the minification process the system will look for other files in the directory called exactly the same filename as the file in question only with .min.js at the end instead of only .js. If it finds a file like this, it assumes that this it is a pre-minified version of the file in question and uses it instead of minifying the original file.

However, if you add a file to your bundle that has a name that ends in .min.js (like I did) and optimizations aren’t enabled (e.g. debug is set to true in web.config and BundleTable.EnableOptimizations has not been set to true), this file will be ignored (i.e no script include will be generated for it in your html).

by Stian

Decimal validation with comma in MVC

20130228DecimalSeparator.svgIn MVC 3, when using jQuery validation plugin with unobtrusive validation, you will see that comma in a textbox that is bound to a decimal value will not be allowed by client side validation.

Instead of going in and changing the jquery.validate.js, you should override the default behavior by adding these lines to your own javascript file (make sure it is loaded after the valiate javascript!)

$.validator.methods.range = function(value, element, param) {
    var globalizedValue = value.replace(",", ".");
    return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
};

$.validator.methods.number = function(value, element) {
    return this.optional(element) || /^-?(?:d+|d{1,3}(?:[s.,]d{3})+)(?:[.,]d+)?$/.test(value);
};

This will fix your problem for the number validation and the range validation!

by Joakim

Validation of required checkbox in Asp.Net MVC

Why would you want to have a required checkbox, i.e. a checkbox that user would have to check? Well, a typical example would be that you have some sort of terms associated with submitting a form that the user has to agree to.

You might think that decorating a boolean property on one of your MVC models with a RequiredAttribute, would mean that if you presented that property in your view as a checkbox, that would require the user to click that checkbox before submitting the form. This is not the case however. If you take a look at the implementation of the RequiredAttribute it is actually casting the value to validate into a string and checking the string length. If it can’t cast the value to a string, it will just return true for IsValid.

It is quite easy however to create your own custom validation attribute that you could decorate your boolean property with. The following code checks if the decorated property is a bool, and if so, requires it to have been set to true;

public class BooleanRequiredAttribute : ValidationAttribute, IClientValidatable
{
    public override bool IsValid(object value)
    {
        if (value is bool)
            return (bool)value;
        else
            return true;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
        ModelMetadata metadata,
        ControllerContext context)
    {
        yield return new ModelClientValidationRule
                            {
                                ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                                ValidationType = "booleanrequired"
                            };
    }
}

 
Notice the GetClientValidationRules-method above where we specify the error message to display if client-side validation fails, and what the validation type is. The value we provide as the validation type will be rendered as the name of the rule in the HTML element, and will be used further down to tell jQuery how to validate this property.

After creating the new validation attribute, we need to apply it to a boolean property on out model;

[BooleanRequired(ErrorMessage = "You must accept the terms and conditions.")]
[Display(Name = "I accept the terms and conditions")]
public bool TermsAndConditionsAccepted { get; set; }

 
Next, create a simple view with a form that includes you boolean property;

@model MyModel@using (Html.BeginForm())
{
    <fieldset>
        <legend>Terms and Conditions</legend>
        @Html.ValidationSummary(true, "Please correct the errors and try again.")
        <p>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            Fusce facilisis ullamcorper consequat. Vestibulum non sapien lectus.
            Nullam at quam eu sapien mattis ultrices.
        </p>
        <ol>
            <li>
                @Html.CheckBoxFor(m => m.TermsAndConditionsAccepted)
                @Html.LabelFor(m => m.TermsAndConditionsAccepted, new { @class = "checkbox" })
                @Html.ValidationMessageFor(m => m.TermsAndConditionsAccepted)
            </li>
        </ol>
        <input type="submit" value="Submit" />
    </fieldset>
}

 
Lastly, in order for client-side validation to work, you need to include the following script in your view (or you can just put line 3 in a javascript file that you reference);

<script type="text/javascript">
    (function ($) {
        $.validator.unobtrusive.adapters.addBool("booleanrequired", "required");
    } (jQuery));
</script>

 
This just registers a new validation adapter for the boolean required attribute, where the first parameter is the adapter name and the second parameter is the name of jQuery validate rule. The adapter name should match the value we specified earlier as the validation type, and the jQuery validation required-rule will require the user to check the checkbox.

That’s it! This will make sure that the checkbox is checked by the user client-side (using jQuery unobtrusive validation*) and that the boolean property is set to true server-side when the form is submitted to the server.

*The ClientValidationEnabled and UnobtrusiveJavaScriptEnabled application settings must be set to true in your web.config for client-side validation to work.

by thorhalvor

My favorite Web Application Architecture

This is my first blogpost and I thought I should start writing about the web application architecture i normally choose these days, –if I am allowed by the client to refactor or start from scratch. Hopefully more blogposts will come out of this.

The first thing I do is splitting the Web Application in a html part and a JSON REST API part. Typically the Visual Studio folder structure looks like this:

  • WebApplication1.WebSite
  • WebApplication1.RestApi
HTML

I use the Razor viewengine in combination with Knockout.js to write the databinding and other than this the HTML is mostly consisting of a bunch of DIVs and script elements with jquery $.get or $.post’s. Normally I am that lucky in projects that I do not have to care about the “colors and stuff”, there is normally another guy taking care of the CSS. My main focus is therefore to get the databinding right and have a nice and clean architecture “downwards”.

REST API

I design the REST API service methods with the view in mind. So instead of having a super-generic method that works for “all web pages” I rather have several specific ones. I want to have as little logic in the client side as possible. I can then design my viewmodel-classes to consist of just the data needed, both translated and formated the way i want.

CQRS style: When working with the REST API I like to split the GET and POSTs as done in CQRS.

  • The GET-methods are fetching data from the database through a simple ORM like for example: EF, Dapper, Massive or Simple.Data. If working with a documentdatabase as for example RAVENDB the queries will go directly to its DocumentSession.
  • The POST-methods will have the business logic and sometimes be long running processes. A reliable service bus as nServiceBus is therefore to prefer. When working with long running and asynchronous processes special care must be taken when designing the UserInterface (HMTL pages). They can normally not be designed as done when working with request-response-based applications.
Environment:

If the team developers are writing tests and the application above is on GitHub using TeamCity with Continuous Deployment, then my day is perfect!

by Stian

jQuery validation not working in IE7 and IE8

jqueryWhen you create a new ASP.NET MVC 3 Project in Visual studio, your script folder will by default contain among others:

jquery-1.5.1.min.js
jquery.validate.min.js (which is version 1.8.0)

One of the first things you might want to do is update the jquery version to the latest version, which today is version 1.7.1

After doing this, your client side validation will stop working in Internet Explorer 7 and Internet Explorer 8.

This is because the jquery.validate version is not compatible with jquery versions > 1.6. The solutions is simple, you need to update your version of jquery.validate as well. You can find the current version 1.9 from Microsoft’s CDN or the latest version from GitHub here:

Microsoft Ajax CDN: http://ajax.aspnetcdn.com/ajax/jquery.validate/1.9/jquery.validate.min.js
GitHub Jquery Validation: https://github.com/jzaefferer/jquery-validation/downloads

Remember that you can always find the latest javascript library in Microsofts CDN, see the complete list of available libraries here: http://www.asp.net/ajaxlibrary/cdn.ashx

by Stian

ASP.NET MVC with Cleaner Data Annotations

 

While using data annotations with localization in your MVC model, you may end up with some untidy looking code like in this example, where all we do is adding display information and validation:

   1:  public class Character {
   2:    [Display(Name="Character_FirstName", ResourceType=typeof(ClassLib1.Resources))]
   3:    [Required(ErrorMessageResourceType=typeof(ClassLib1.Resources), 
   4:      ErrorMessageResourceName="Character_FirstName_Required")]
   5:    [StringLength(50, ErrorMessageResourceType = typeof(ClassLib1.Resources),
   6:      ErrorMessageResourceName = "Character_FirstName_StringLength")]
   7:    public string FirstName { get; set; }
   8:   
   9:    [Display(Name="Character_LastName", ResourceType=typeof(ClassLib1.Resources))]
  10:    [Required(ErrorMessageResourceType=typeof(ClassLib1.Resources), 
  11:      ErrorMessageResourceName="Character_LastName_Required")]
  12:    [StringLength(50, ErrorMessageResourceType = typeof(ClassLib1.Resources),
  13:      ErrorMessageResourceName = "Character_LastName_StringLength")]
  14:    public string LastName { get; set; }
  15:  }

 

While working on a project I found a NuGet Package which helped me to clean up this “mess”.

2012-01-11_0731

The extension is created by Phil Haack and derives from the DataAnnotationsModelMetadataProvider. The beauty of this extension is that you can set up a default resource type in Global.asax like this:

   1:  ModelMetadataProviders.Current = new ConventionalModelMetadataProvider(
   2:    requireConventionAttribute: false,
   3:    defaultResourceType: typeof(MyResources.Resource)
   4:  );

Note that the first argument, requireConventionAttribute, determines whether the conventions only apply to classes with the MetadataConventionsAttribute applied.

After you have configured the default resource type, it is no longer necessary to specify the ResourceType or ErrorMessageResourceType attributes. Also, the custom metadata provider looks up the resource key named the same as your property so that you are no longer required to specifying Display(Name=”FirstName”) as long as you match the property name and the resource name.

The same goes for validation attributes where you can use a resource key of {PropertyName}_{Attributename}, for example, to locate the error message for a RequiredAttribute, the provider finds the resource key FirstName_Required.

The “messy” data annotation code from above could with this extension look something like the code below, and still give the same result as long as you have the resources “FirstName” and “LastName” in your default ResourceType:

   1:  public class Character {
   2:    [Required]
   3:    [StringLength(50)]
   4:    public string FirstName {get; set;}
   5:   
   6:    [Required]
   7:    [StringLength(50)]
   8:    public string LastName {get; set;}
   9:  }

Hello good lookin’ !

You could of course provide some metadata, the metadata that you don’t supply is inferred based on the conventions. You may also like that if a value for a given resource is not found, the code falls back to using the property name as the label, but splits it using Pascal/Camel casing as a guide. Therefore in this case, the label is “First Name” and “Last Name”.

by Stian

ASP.NET MVC 3 Beta

ASP.NET MVC 3ASP.NET MVC 3 Beta was released earlier this month. I tried to install it through the Web Platform Installer, but got the message: “The product you are trying to install is not supported on your operating system“. I run Windows 7 32 bit and that is of course supported. The solution is to download the installation files from this web site.

If you encounter the message: “This product requires Microsoft ASP.NET Web Pages 1.0. Please install the missing component, then try to install this product again.” when running the MVC 3 beta installation, go back to the same website and download the AspNetWebPages.msi file and install this first.

Tags: ,