You can use custom model binder.
public ActionResult AnAction(Claim claim)
{
if (!ModelState.IsValid)
return View("Invalid claim");
}
public class ClaimModelBinder: IModelBinder
{
public object BindModel(BindingContext context) // don't remember exactly
{
var id = context.ValueProvider.GetRawValue(context.ModelName);
if (string.IsNullOrEmpty(id))
{
context.ModelState.AddModelError(context.ModelName, "Empty id");
return null;
}
var claim = GetClaim(id);
if (claim == null)
{
context.ModelState.AddModelError(context.ModelName, "Invalid claim");
return null;
}
return claim;
}
}
// in global.asax.cs
ModelBinders.Binders.Add(typeof(Claim), new ClaimCustomerBinder());
Why it's better than action filter:
- binder automatically picked up, you don't need attribute
- you can have several Claim parameters
works for Claim properties of some other class, and even IList in case you want to pass multiple Claim objects
But you can't handle specific actions, i.e. return View("NotFound") for this error. However, it's easy to do if you develop your own convention: for example your ModelBinder can do
context.ModelState.AddModelError(context.ModelName, new NotFoundException("Invalid claim", "ClaimNotFound"));
public override void OnActionExecuting(ActionExecutingContext context)
{
var notfound = from o in ModelState
from e in o.Value.Errors where
where e.Exception is NotFoundException
select e.Exception;
if (notfound.Count() == 1)
context.Result = View { Name = notfound.First().ViewName };
}
Alternative just do
if (!ModelState.IsValid)
// this view will just show all ModelState errors
return View("IncorrectInput");
It may seem harder, but it is a good separation of concerns - model binder maps the data from HTTP to your classes, then you are free to either check ModelState manually or rely on some automatic handling. It may look an overkill for simple apps, but if you start getting duplicates here and there, and your models become complex, model binders can greatly simplify your life, since your controller actions will get ready-to-use objects, and you can concentrate on the real business code instead of the infrastructure/plumbing.
This is (I believe) what ASP.NET MVC is for - you're free to build your own set of conventions around it.