Noah Blumenthal's Blog

June 17, 2009

jEditable note: don’t return JSON (and how to return strings from ASP.NET MVC Actions)

Filed under: ASP.NET MVC,jQuery — noahblu @ 5:42 pm

jEditable is a cool jQuery plugin for inline editing.  I was toying around with it today and found an interesting ‘feature’: it expects a string response from the server.  Now that’s a problem because I was returning a JSON object so my string had quotes around it (Test became “Test”).  Ok, so I have to return a regular string.  I guess I just didn’t expect that because I’m used to my jQuery plugins expecting json (or at least accepting it).

Well, my Action returns an ActionResult because not always will it be accessed via jQuery (yup, this does not actually require javascript!).  I check the request (if (Request.IsAjaxRequest())) and proceed…
So, what I did was create a StringResult.  Much like the JsonResult class, the StringResult outputs the data you give it but it just outputs text.  Simple but effective, here’s the code:

public class StringResult : ActionResult
 {
 private string _output;
 public StringResult(string output)
 {
 _output = output;
 }
 public override void ExecuteResult(ControllerContext context)
 {
 HttpResponseBase response = context.HttpContext.Response;
 response.ContentType = "text/html";
 response.Write(_output);
 }
 }

April 23, 2009

Advanced databinding: determining (sub) type at runtime

Filed under: DataBinding — noahblu @ 3:07 pm

This is something I haven’t seen anyone talk about:

I’m working on a “coupon” site — merchants post offers on the site for visitors to see.  There are a few different types of offers: Coupons, Rebates, Free Stuff, and Deals.  In terms of code, these all implement an IOffer interface (and they all are children of OfferBase).

The site has a single page for posting offers — the merchant chooses the offer type (Coupon, Rebate, Free Stuff, or Deal) and the form accomodates for the necessary information, swapping out certain input elements for others.

I wanted a way to post the form to a single Action and have a databinder determine the exact type at runtime.  And it was actually pretty easy.  I’m still working on some specific details, so I’ll let you know if there are any caveats, but as far as I can see this works pretty well.

Here’s what I did:

First I created an Action that takes an IOffer parameter

public ActionResult Create(IOffer offer)
{

}

Now the MVC framework is going to have to bind the posted data to an IOffer, so I created an IOfferModelBinder.

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IOffer offer;
ValueProviderResult offerType;
bindingContext.ValueProvider.TryGetValue(“OfferType”, out offerType);
if (offerType == null)
return null;

// Instantiate correct offer type based on selection
string assemblyName = typeof(IOffer).Assembly.FullName;
string typeName = string.Concat(offerType.AttemptedValue, “Offer”);
string fullTypeName = “Picselle.BunchOfSavings.DomainModel.” + typeName;
var obj = Activator.CreateInstance(assemblyName, fullTypeName);
offer = obj.Unwrap() as IOffer;

// Bind everything else
bindingContext.Model = offer;
bindingContext.ModelType = offer.GetType();
var bindResult = ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);

return offer;
}

I didn’t want to limit myself to the current offer types, so I figured that we’ll use a pretty standard naming convention to name our classes (this is how it’s done now)– [offer_type_name]Offer (e.g. CouponOffer or RebateOffer).  So the code uses the Activator to create an object of the type the merchant wants by adding “Offer” to the end of the posted deal type string (which is the deal type selected by the merchant).  Now I can add a BuyOneGetOneFreeOffer later and not have to change anything…

Pretty straightforward, and powerful…

March 23, 2009

UriModelBinder for ASP.NET MVC RTM

Filed under: ASP.NET MVC — noahblu @ 4:54 pm
Tags: ,

I blogged a while ago about creating a UriModelBinder to bind a url string to a Uri object (see https://noahblu.wordpress.com/2008/11/07/uri-modelbinders-and-updatemodel/) but things have changed since then (I think that was RC1, but I don’t recall).

Anyway, here’s the same gist updated for the MVC RTM release:

public class UriModelBinder : IModelBinder
{
#region IModelBinder Members

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
ValueProviderResult result;
Uri url = null;
bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName, out result);
if (result != null && string.IsNullOrEmpty(result.AttemptedValue))
{
if (Uri.TryCreate(result.AttemptedValue, UriKind.RelativeOrAbsolute, out url))
return url;
}
return null;
}

#endregion
}

As you can see, the code is pretty simple.  I’m returning a NULL Uri if the Url is not valid.  I’m also accepting Relative OR Absolute Uris.  This might not be what you want to do.  But as you can see, custom model binders are pretty easy.

———

A friend asked me how this works, so I’m gonna post it here:

Take a look at this line:

bindingContext.ValueProvider.TryGetValue(bindingContext.ModelName, out result);

What we’re doing here is attempting to get  a ValueProviderResult based on the the bindingContext.ModelName. Keep in mind that the ValueProvider is a way of getting a value and in our website is most likely in the form of a FormCollection.

Now I’m using the bindingContext.ModelName to pull the value because a Uri is made up of only a single field — i.e. I have a website with textbox called website url that takes an entire string like “http://www.google.com” .  So if we’re traversing an object tree and one of the properties that we’re binding is called “WebsiteUrl”, we’ll expect that a field called “WebsiteUrl” is posted with the rest of the data and that’s the bindingContext.ModelName.  Now this might change if we were building a more complex object or an object that’s based on multiple fields.

January 14, 2009

MVC ModelBinders, complex types, Enumerables, and partial binding

Filed under: ASP.NET MVC — noahblu @ 5:15 pm

If you’ve got an object that’s got some sort of enumerable, you’re going to have to write your own ModelBinder.

For example, suppose you have a Person object with a List<string> containing friends’ names.  Now suppose you have an HTML page that has 10 textboxes and the user can add 10 friends at a time (better yet, give them a JS widget that adds textboxes as necessary).  You could post back 10 or 20 friends at a time, right?

Now the Person object might just have a FirstName & LastName (which are strings) and maybe a phone number (also a string), and, aside from this list of friends, be nicely updated using the default ModelBinder included in MVC.  However, you really want these friends to be updated with the Person object, right?  Well here’s an easy way to do it.

I’m going to call this partial binding even though that’s NOT what it is.  I mean it’s partial binding relative to my programming effort — I’m only going to bind part of my Person object and someone else (ie the MVC framework’s built in binders) will bind the rest.

Here goes:

internal class PersonModelBinder : IModelBinder
{
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
ModelBinderResult result = ModelBinders.DefaultBinder.BindModel(bindingContext);
Person p = result.Value as Person;

// What we just did was bind the Person’s non-complex types: e.g. the string of FirstName, LastName, and PhoneNumber
// all the stuff MVC would do anyway without any intervention on our part.


// Now all you have to do is add logic to populate the list here

return result;
}
}

Ok, so that’s easy.  How about populating the list?  Well one way of doing it would be this:

Suppose your textboxes were named like “txtFriend_1”, “txtFriend_2”, “txtFriend_3”, etc, then you could easily loop through the form variables:

foreach (System.String key in bindingContext.HttpContext.Request.Form.AllKeys)
{
if (System.Text.RegularExpressions.Regex.IsMatch(key, @”^txtFriend_\d+$”, System.Text.RegularExpressions.RegexOptions.IgnoreCase))
{
p.Friends.Add(bindingContext.HttpContext.Request.Form[key]);
}
}

You don’t have to use regular expressions of course.  Pretty easy, huh?

November 7, 2008

Uri, ModelBinders, and UpdateModel()

Filed under: ASP.NET MVC — noahblu @ 12:25 am

So you’re developing an application using ASP.NET MVC and you got a Person class with properties:

string FirstName;
string LastName;
string Email;
Uri Webpage;

And of course you have a form that collects these 4 pieces of information.  And you build an Action in your PersonController called Create like so:

public ActionResult Create(FormCollection form){
Person p = new Person();
UpdateModel(p, form);
}

and you run it thinking you’re done and ready to go home and…. get an Exception on the line UpdateModel(p, form).  Why?

The answer has to do with ModelBinders and the IModelBinder interface and how UpdateModel() works.  That was something I couldn’t find too much information about so I thought I would write this post.

Basically, the MVC framework provides you with a cool method called UpdateModel that will take all the data from an IValueProvider (not gonna get into this right now) and update the model you send it with that data.  If the model is a complex type, it will iterate over all the properties and try to set them automagically.  But it isn’t actually magic.  The reason why your UpdateModel(p, form) line is crapping out is because the ModelBinder doesn’t know how to transform a string (e.g. “http://www.google.com&#8221;) into an object of type Uri.

The nice thing is that you can add your own custom binders by extending IModelBinder.  Here’s the code I used for UriModelBinder:

internal class UriModelBinder : IModelBinder
{
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != null && !string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
{
Uri url = new Uri(valueProviderResult.AttemptedValue);
return new ModelBinderResult(url);
}
return new ModelBinderResult(null);
}
}

* keep in mind that this will throw an error if the url is not formatted properly (because that’s what happens when you try instantiating a new Uri with an improperly formatted url) but will set the Uri to null if the string representing it is blank.  This is intentional, but not necessarily what YOU want.

Very simple here.  Just get the string value that we will use to set the property value and check if it’s null or empty.  If so, just return null.  If not, create a new Uri using the attempted value and return that.

Now to get this to work we just need one more thing — an entry in the Global.asax to register this Binder (otherwise it just sits there and nobody knows about it):

ModelBinders.Binders.Add(typeof(Uri), new UriModelBinder());

that means — “use this UriModelBinder to convert values to type Uri”.

Pretty cool.  You could also create a binder for the entire Person object using this same basic idea.  Google that and you’ll see numerous examples.

Here’s a free one — I also built a binder for DateTime? (that’s Nullable DateTime):

internal class NullableDateTimeModelBinder : IModelBinder
{
public ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
ValueProviderResult valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (valueProviderResult != null && !string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
{
DateTime date = DateTime.Parse(valueProviderResult.AttemptedValue);
return new ModelBinderResult(date);
}
return new ModelBinderResult(null);
}
}

Pretty simple, huh?

November 4, 2008

Getting the requested Action name

Filed under: ASP.NET MVC — noahblu @ 10:54 pm
Tags:

Today I needed to retrieve the name of the current Action from within a View.

Shebam: ViewContext.RouteData.GetRequiredString(“action”) .  That’ll get you the name of the currently executed Action.  You can then use that with Html.ActionLink to make links Action sensitive!  Awesome!

October 28, 2008

ASP.NET MVC Captcha

Filed under: ASP.NET MVC — noahblu @ 4:56 pm
Tags: , ,

I was looking for an ASP.NET MVC Captcha control and stumbled upon Nick Berardi’s (http://www.coderjournal.com/2008/06/mvc-captcha-for-preview-release-3/).

First of all I’m using IIS7 and had trouble getting the HttpHandler to register properly.  I ended up with the following steps to get it to work:

  1. Add <add verb=”GET” path=”captcha.ashx” validate=”false” type=”ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha” /> to <httpHandlers> in Web.config
  2. Add <add name=”CaptchaImageHandler” verb=”GET” path=”captcha.ashx” type=”ManagedFusion.Web.Handlers.CaptchaImageHandler, ManagedFusion.Web.Captcha” /> to the <handlers> section within <system.webserver> in Web.config
  3. Add routes.IgnoreRoute(“{handler}.ashx”); to Global.asax

So that got it working, but there were a few pieces of functionality I felt were missing:

1) I wanted the ability to style the CaptchaTextBox

The site I’m working with is pretty design intensive and I needed a way to inject CSS information into the CaptchaTextBox Html Helper.  I reworked the CaptchaHelper and modified the CaptchaTextBox as well as added some overloads:

        public static string CaptchaTextBox(this HtmlHelper helper, string name)
        {
            return helper.CaptchaTextBox(name, null);
        }

        public static string CaptchaTextBox(this HtmlHelper helper, string name, Object htmlAttributes)
        {
            return helper.CaptchaTextBox(name, ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes)));
        }

        public static string CaptchaTextBox(this HtmlHelper helper, string name, IDictionary<String, Object> htmlAttributes)
        {
            ModelState state;

            TagBuilder builder = new TagBuilder("input");
            builder.MergeAttributes<string, object>(htmlAttributes);
            builder.MergeAttribute("type", "text");
            builder.MergeAttribute("name", name);
            builder.MergeAttribute("id", name);
            builder.MergeAttribute("value", "");
            builder.MergeAttribute("maxlength", ManagedFusion.Web.Controls.CaptchaImage.TextLength.ToString());
            builder.MergeAttribute("autocomplete", "off");

            if (helper.ViewData.ModelState.TryGetValue(name, out state) && (state.Errors.Count > 0))
            {
                builder.AddCssClass("input-validation-error");
            }

            return builder.ToString(TagRenderMode.SelfClosing);
        }

I got the TagBuilder idea from checking out the Reflector on System.Web.MVC.  Pretty cool stuff there.  So now I can use the CaptchaTextBox like so:

<%= Html.CaptchaTextBox(“captcha”, new { @class = “field” })%>

2) I wanted the CaptchaValidationAttribute to invalidate my Model if the captcha isn’t valid

(instead of inject a captchaValid with a value of false into my routedata which is what it does off the shelf)

For this I modified the CaptchaValidationAttribute class.  The first thing I did was make add an ErrorMessage string property.  Then I modified the OnActionExecutingContext method to look like this:

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            // get the guid from the post back
            string guid = filterContext.HttpContext.Request.Form["captcha-guid"];

            // check for the guid because it is required from the rest of the opperation
            if (String.IsNullOrEmpty(guid))
            {
                filterContext.Controller.ViewData.ModelState.AddModelError(Field, ErrorMessage);
                return;
            }

            // get values
            CaptchaImage image = CaptchaImage.GetCachedCaptcha(guid);
            string actualValue = filterContext.HttpContext.Request.Form[Field];
            string expectedValue = image == null ? String.Empty : image.Text;

            // removes the captch from cache so it cannot be used again
            filterContext.HttpContext.Cache.Remove(guid);

            // validate the captch
            if (String.IsNullOrEmpty(actualValue) || String.IsNullOrEmpty(expectedValue) || !String.Equals(actualValue, expectedValue, StringComparison.OrdinalIgnoreCase))
            {
                filterContext.Controller.ViewData.ModelState.AddModelError(Field, ErrorMessage);
                return;
            }
        }

Now I can use the CaptchaValidationAttribute like this:

[CaptchaValidationAttribute()]
public ActionResult Register(FormCollection form)
        {
// INCREDIBLY OVER-SIMPLIFIED BUT YOU GET THE IDEA
            if (!ViewData.ModelState.IsValid)
            {
                return View();
            }
        }

Create a free website or blog at WordPress.com.