Mark Everard

Hello, I'm Mark โ€“ a PhD physicist turned technologist / architect.

Archive for July, 2011

Creating a custom ModelBinder allowing validation of injected composite models

with 2 comments

Model Binding – is the ‘auto-magic’ step performed by the ASP.NET MVC framework to convert user submitted data (either http post values, querystring values or url route values) into a strongly typed model, used in your controller actions.

Out of the box, the MVC framework also allows you to set validation attributes on your models which are inspected at the model binding stage, meaning that your controller actions can inspect the ModelState.IsValid property to assess whether the user submitted data meet expectations. This attribute based approach to validation provides a clean way to handle validation (a cross-cutting concern) without introducing additional code into your controller action.

One of the features needed when putting together the Fortune Cookie Personalization Engine for EPiServer was to perform validation on a user submitted criteria value. As a reminder, in the context of the Personalization Engine, a criteria is an editor submitted string which is persisted and used by a ContentProvider to allow for a more granular method of content retrieval. For further background and explanation, check out one of my earlier posts – Personalization Engine – ContentProvider Criteria Models

In the full Personalization Engine domain model, criteria properties belong to IContentModel objects which are used to specify the user interface displayed to an editor to allow them to enter the criteria. Below is an example of a TextBoxCriteriaModel which renders as a textbox in the Admin interface.

criteriamodel-validation-interface

The string value from this criteria input is posted to the controller action.ย  However as the type of IContentModel depends on the value of the ContentProvider dropdown, validation attributes cannot be set directly on the model passed to/from this view as different concrete IContentModel types need to be able to specify different validation rules.

To provide validation of the composite IContentModel using validation attributes, we have to hook in to one of the extension points of the ASP.NET MVC framework and create a custom model binder.

Validating composite models

Our custom ModelBinder needs to perform the following tasks:

  • Bind the incoming data against an AdminViewModel (the model passed from the user interface shown above).
  • Obtain an instance of the specified ICriteriaModel and update the ICriteriaModel’s criteria property with the value posted by the form.
  • Validate the composite (and now populated) ICriteriaModel
  • Update the ModelState with the validation results from the composite model, along with the original parent model validation results

As the custom ModelBinder needs to perform all of the existing validation and binding that the DefaultModelBinder would, I’ve chosen to inherit from it and add the additional composite model validation logic into the overriden BindModel method. An instantiated IContentModel is obtained from the AdminViewModel, and the ModelValidator framework class allows us to validate the composite IContentModel, before updating the bindingContext.ModelState with an validation errors.


public class CriteriaValidationModelBinder : DefaultModelBinder
{
   const string ValidationPropertyName = "Criteria";

   public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   {
      if (bindingContext.Model != null)
         return bindingContext.Model;

      var model = base.BindModel(controllerContext, bindingContext);

      var adminViewModel = model as AdminViewModel;
      if (adminViewModel == null)
         return model;

      var criteriaValue = bindingContext.ValueProvider.GetValue(ValidationPropertyName);
      adminViewModel.CriteriaModel.Criteria = criteriaValue != null ? criteriaValue.AttemptedValue : string.Empty;

      ModelMetadata modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => adminViewModel.CriteriaModel, typeof(ICriteriaModel));
      ModelValidator compositeValidator = ModelValidator.GetModelValidator(modelMetadata, controllerContext);

      foreach (ModelValidationResult result in compositeValidator.Validate(null))
         bindingContext.ModelState.AddModelError(ValidationPropertyName, result.Message);

      return model;
   }
}

Our custom ModelBinder can be hooked into our application in Global.asax, or in an EPiServer IInitializableModule using the following method.


private void AddCriteriaValidationModelBinder()
{
    ModelBinders.Binders.Add(typeof(AdminViewModel), new CriteriaValidationModelBinder());
}

And that’s it…. ModelBinders are an important piece of the MVC framework, and in the majority of scenarios you can rely on the DefaultModeBinder to handle all of your requirements. However creating your own ModelBinder for more advanced requirements is pretty straightforward, depending on your requirements for your binder ๐Ÿ™‚

Written by mark

July 18th, 2011 at 10:00 am

Posted in ASP.NET,C#,EPiServer,MVC

Personalization Engine – User Interface

without comments

One of the things I wanted to provide with the PersonalizationEngine framework was a series of simple User Interface elements , which would provide an easy way to demonstrate the Personalization Engine within front-end templates, and also provide developers with some simple examples of how to use the API.

Lee Crowe has stepped up to the mark and contributed these for me ๐Ÿ™‚ – Thanks Lee

These User Interface elements are contained in a separate NuGet package: FortuneCookie.PersonalizationEngine.UI (dependent on FortuneCookie.PersonalizationEngine) ,which is available to download the EPiServer NuGet feed.

The packages contains:

1) A custom property, (using Lee’sย  multiple selection custom property)ย  – which allow you to select a limited set of defined Personalization Engine content providers from which to return content. This allows you to have PersonalizationEngine rules defined on a page-by-page basis.

2) Two dynamic content controls (using the new DynamicContentPlugin attribute) – one which just displays PersonalizationEngine results from the full rule-set, and one which uses the custom property described above to allow a limited sub-set.

FortuneCookie.PersonalizationEngine 1.1

Additionally there is a new version of the core FortuneCookie.PersonalizationEngine, which contains some minor updates and enhancements

1) Added a new event that you can hook into to filter any results prior to them being returned from the PersonalizatioEngine. A good usage for this may be to filter by PageType definition in the case you don’t want your PersonalizationEngine results to contain a particular PageType

The example below will filter all Article pages from any content returned from a PagesWithPageNameContentProvider. The event is hooked on in Global.asax


protected void Application_Start(Object sender, EventArgs e)
{
 PersonalizationEngine.OnContentProviderGetContent += PersonalizationEngine_OnContentProviderGetContent;
}

 private void PersonalizationEngine_OnContentProviderGetContent(ContentProviderEventArgs e)
 {
     if (e.ContentProviderType == typeof(PagesWithPageNameContentProvider))
          e.ContentProviderPages = e.ContentProviderPages.Where(p => p.PageTypeID != PageTypeResolver.Instance.GetPageTypeID(typeof(ArticlePageType)));
 }

2) Changed the call to DataFactory.Instance.GetPages, to an iterated call to DataFactory.Instance.GetPage in the CachedContentProviderBase class. Although the latter method is less performant – there is an acknowledged bug in CMS6R2 which means that PageTypeBuilder does not hook into the GetPages method result meaning that returned results are unTyped.

Both of these packages are now available in the EPiServer Nuget Feed

Written by mark

July 7th, 2011 at 4:58 pm

Posted in C#,EPiServer

Serving videos to iOS devices from EPiServer VPP folders

with 9 comments

We recently launched an EPiServer site that made moderate, but high-profile usage of video and was also designed to be iOS device friendly – meaning no Flash and Html5 used to display video. Late on in development / integration we came across an issue where the mp4 / m4v videos file that were being served from an EPiServer VPP folder did not play when viewed on an iOS device. A little investigation found that the video files would play correctly when served natively from a static / non-content managed location.

(Note: If you’re serving a large amount of media content, then you’d be better off looking at a full media hosting solution rather than just serving from the EPiServer VPP).

Http-Headers

Obviously the files were identical, so the only difference was in how IIS was serving them. Below are the Http responses (captured using FireBug) for the identical .m4v video files, one served from a VPP, and one from a native path.

The significant difference is theย Accept-Ranges : bytes header. Cool – so we can just add in this header to our response and we’re sorted right? WRONG I’m afraid!

HTTP/1.1 Accept-Ranges header field

The Accept-Ranges response-header field allows the server to indicate its acceptance of range requests for a resource. This means that the server is open to partial downloads of files, and that clients (web-browsers) can download a limited ‘chunk’ of a file by requesting a specific byte-range, for-example bytes 5001-9999.

Going back to our original issue of why the files didn’t play on an iOS device, a quick look on the Apple developer notes (via StackOverflow) confirms that the web server needs to be configured to correctly handle byte-range requests. This explains why our file doesn’t work on an iOS device when served from an EPiServer VPP.

EPiServer.Web.StaticFileHandler

Our site was running using IIS7.5 in integrated pipeline mode, meaning that pretty much the whole Request / Response pipeline is handled by ASP.NET modules. Looking in the EPiServer site web.config you can see that the location path elements for VPP paths (Global /PageFiles etc) contain the HttpHandlers that are responsible for serving VPP filess. This is a bespoke EPiServer file handler implementation (EPiServer.Web.StaticFileHandler), which unlike the default IIS7 StaticFileHandler does not support Range-Requests ๐Ÿ™

The Solution

Now you didn’t think that I’d explain all of this, without heroically coming up with a solution did you? Well I am providing a solution, but its hardly heroic as most of the hard work was done many years ago by Scott Mitchell – see Range-Specific Requests in ASP.NET. In his post Scott explains and provides an example of how to roll a HttpHandler with support for range-specific HTTP requests.

I’ve inherited from the RangeRequestHandlerBase class that Scott posted in the above article, and overidden a few methods adding in some EPiServer specifics, such as mapping the request path to the physical VPP file path and also using the EPiServer.Web.MimeMapping class to handle mapping from an extension to a mime type.


    public class RangeRequestFileHandler : RangeRequestHandlerBase
    {
        ///
        /// Returns a FileInfo object from the mapped VirtualPathProviderFile
        ///
        /// <param name="context" />
        ///
        public override FileInfo GetRequestedFileInfo(HttpContext context)
        {
            UnifiedFile file = GetFileInfoFromVirtualPathProvider(context);

            if (file == null)
                return null;

            PreventRewriteOnOutgoingStream();
            return new FileInfo(file.LocalPath);
        }

        private static UnifiedFile GetFileInfoFromVirtualPathProvider(HttpContext context)
        {
            return GenericHostingEnvironment.VirtualPathProvider.GetFile(context.Request.FilePath) as UnifiedFile;
        }

        ///
        /// Returns the MIME type from the mapped VirtualPathProviderFile
        ///
        /// <param name="context" />
        ///
        public override string GetRequestedFileMimeType(HttpContext context)
        {
            UnifiedFile file = GetFileInfoFromVirtualPathProvider(context);
            return MimeMapping.GetMimeMapping(file.Name);
        }

        ///
        /// Prevents episerver rewrite on outgoing stream.
        ///
        private void PreventRewriteOnOutgoingStream()
        {
            if (UrlRewriteProvider.Module != null)
                UrlRewriteProvider.Module.FURLRewriteResponse = false;
        }

This handler can be used to serve files from your VPP folders by adding the following configuration (IIS7 only folks – which if you’re not using then you really should be pushing for an upgrade!). This handler listed below is set up only to serve files of type .m4v – and remember that it does not provide any of the native EPiServer functionality for setting the staticFile cache expiration time.


<location path="Global">
     <system.webServer>
         <handlers>
              <add name="webresources" path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader"/>
              <add name="videofiles" path="*.m4v" verb="*" type="FortuneCookie.RangeRequestFileHandler.RangeRequestFileHandler, FortuneCookie.RangeRequestFileHandler"/>
              <add name="wildcard" path="*" verb="*" type="EPiServer.Web.StaticFileHandler, EPiServer"/>
         </handlers>
      </system.webServer>
      <staticFile expirationTime="-1.0:0:0"/>
 </location>

The full source code can be downloaded from the Code section at EPiServer World and a NuGet package is available that will auto-magically add in the code and configure your Global and PageFile VPP’s to serve .mp4, .m4v and .mov video files using the RangeRequestFileHandler.

Written by mark

July 5th, 2011 at 10:00 am

Summer 2011 London EPiServer Meetup programme

without comments

Our third London EPiServer Meetup is almost upon us – (this Thursday 7th July). We’ve got a theme of ‘Integration’ this time and three of our members have kindly volunteered to talk:

  • Paul Dunstone (from our hosts Rufus) – who will be talking about an EPiServer / SmartLogic integration that allowed for a rich and automated content classification. Paul previously blogged about this, but this will provide a great opportunity to understand more of the technical details.
  • Raghavendra Bangalore-Govindaiah (aka Rags) – who will be presenting an overview of a COMET based pushed real-time data feed integration with an EPiServer site. For those of you who have never heard of COMET before, this should be interesting as COMET introduces a new model of working across the web. Rather than the traditional request/response cycle it uses long held http requests to allow the server to PUSH content updates directly to client web browsers.
  • Mark Everard -I’ll be giving anย  overview of NuGet, Microsoft’s package management tool for ASP.NET / Visual Studio, including a look at how to create your own packages and integrate the creation process with your project’s build script using MSBuild.

It again should be a great night and I hope to see you a lot of you there.

~ Mark

Written by mark

July 2nd, 2011 at 2:37 pm

Posted in Community,EPiServer