tags:

views:

29

answers:

1

Suppose I have this model:

public class ViewModel
{
  [Required]
  public string UserInput { get; set; }
  [Required]
  public Guid EntityId { get; set; }
}

Now, when UserInput is wrong, I want to re-display the same page but with validation errors (e.g. /Edit). However, when EntityId is wrong, I want to redirect to some other page (e.g. /Create).

I can do this manually inside each controller...

if (!ModelState.IsValidField("EntityId")) { redirect }
//or
if (string.IsNullOrEmpty(data.EntityId)) { redirect }

but it's kind of boring and violates DRY. Imaging several entities, nested view models with entities... too cumbersome. I'd better have something like ModelState.IsValidUserData and ModelState.IsValidCriticalData. But there's no such thing.

Now, EntityId is actually bound using my custom model binder, that knows that it is mission-critical. So there's this solution:

  1. Usual fields do populate ModelState with errors as usual.
  2. (a) Critical fields are bound using custom model binder that throws special "CriticalModelErrorException". Controller actions have [HandleCrirticalError("action", "controller')] attribute - which handles critical errors and redirects to the given action.
  3. (b) Critical fields are bound using custom model binder that sets BaseController.CriticalModelErrors property (obviously all controllers are derived from supercontroller base class). Each action is free to check both ModelState.IsValid and base.CriticalModelErrors and behave freely based on that.
  4. (c) Critical fields are bound using custom model binder that sets special-format model state errors, e.g. AddModelError(name, "!CRITICAL! text"; Then base controller have method that detects such strings.

2a example:

[HandleCriticalError("Create")] // uses the same controller
[HandleModelStateError("Edit")] // redisplays page with validation errors
public ActionResult Edit(ViewModel data)
{
  // here we know both our data entities and user data are valid and safe
}

2b example

public ActionResult Edit(ViewModel data)
{
  if (!ModelState.IsValid)
     return View(data);
  if (base.CriticalModelErrors.Count > 0)
     return RedirectToAction("Create");
  // here we know both our data entities and user data are valid and safe
}

2c example

protected bool HasCriticalErrors()
{
   return ModelState.Any(x => x.Value.Errors.Any(x => x.ErrorMessage.StartsWith("!CRITICAL!")))
}
// then same as 2b

Now, the questions: how it's handled by other apps and developers (you and your apps)? Which one would you prefer? Are there any drawbacks or better solutions?

+1  A: 

Use the one that introduces the most amount of automation for you, so you won't have to repeat the same code on my places. As you have done already I'd go with 2a but make it a bit different so I wouldn't put those attributes on every single action but rather on the whole Controller class. If at all possible (if required by at least majority actions). If you need to exclude certain actions, create a separate action filter that will disable redirecting.

Robert Koritnik