Reputation: 14967
I have the following action in my controller:
[HttpGet]
public ActionResult Office(GestionOffice model)
{
ModelState.Clear();
model.Initialize();
return View(model);
}
I don't need in action by GET, validations are made. This would help me in the performance of the call by GET.
This would be my ideal case:
[HttpGet]
[NotValidateModel]
public ActionResult Office(GestionOffice model)
{
model.Initialize();
return View(model);
}
Thank you.
Edit
NotValidateModel
clarified that does not exist, would attribute the case to avoid validations.
The reason to move the model in action is to MOCK
the model
Edit II
I have my action with POST, I need is to receive in my action by GET, the model without validation, to successfully complete the testing of the action in the controller
[HttpGet]
[NotValidateModel]
public ActionResult Office(GestionOffice model)
{
model.Initialize();
return View(model);
}
[HttpPost]
[ActionName("Office")]
[NotValidateModel]
public ActionResult OfficePost(GestionOffice model)
{
if(ModelState.IsValid)
{
model.Save();
return RedirectToAction("List");
}
model.Initialize();
return View(model);
}
Edition on the solution of @Mark
As I in my view I have a few calls to actions, I had to create a key with the action and the controller.
Custom ModelMetaData
public class CustomModelMetaData : ModelMetadata
{
public CustomModelMetaData(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
: base(provider, containerType, modelAccessor, modelType, propertyName)
{
}
public override IEnumerable<ModelValidator> GetValidators(ControllerContext context)
{
var itemKey = this.CreateKey(context.RouteData);
if (context.HttpContext.Items[itemKey] != null && bool.Parse(context.HttpContext.Items[itemKey].ToString()) == true)
{
return Enumerable.Empty<ModelValidator>();
}
return base.GetValidators(context);
}
private string CreateKey(RouteData routeData)
{
var action = (routeData.Values["action"] ?? null).ToString().ToLower();
var controller = (routeData.Values["controller"] ?? null).ToString().ToLower();
return string.Format("NoValidation_{0}_{1}", controller, action);
}
}
Filter
public class NoValidationAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var itemKey = this.CreateKey(filterContext.ActionDescriptor);
filterContext.HttpContext.Items.Add(itemKey, true);
}
private string CreateKey(ActionDescriptor actionDescriptor)
{
var action = actionDescriptor.ActionName.ToLower();
var controller = actionDescriptor.ControllerDescriptor.ControllerName.ToLower();
return string.Format("NoValidation_{0}_{1}", controller, action);
}
}
Edit filter
there may be a case of having a foreach in the main view to call several partial views with the attribute NoValidation
. in this case, includes a control to check for the key. since it includes the name of the controller and action in the key, this key is almost unique, it can only be repeated in the case described
public class NoValidationAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var itemKey = this.CreateKey(filterContext.ActionDescriptor);
if (!filterContext.HttpContext.Items.Contains(itemKey))
{
filterContext.HttpContext.Items.Add(itemKey, true);
}
}
private string CreateKey(ActionDescriptor actionDescriptor)
{
var action = actionDescriptor.ActionName.ToLower();
var controller = actionDescriptor.ControllerDescriptor.ControllerName.ToLower();
return string.Format("NoValidation_{0}_{1}", controller, action);
}
}
Upvotes: 2
Views: 1348
Reputation: 32758
[SEE THE UPDATES BELOW ALSO THE EDITS MENTIONED BY @ANDRES IN QUESTION]
What you are trying to do is little difficult to achieve and I'm not sure why you want this basically for. I've tried to accomplish through custom ModelMetaData
and filter (not tested completely).
We can't read the attributes that are decorated over the actions from a model binder or a metadata provider so we need a filter. The filter put some value in the HttpContext.Items
and we can retrieve that from the custom metadata provider (http://stackoverflow.com/questions/6198155/asp-net-mvc-modelbinder-getting-action-method).
Filter
public class NoValidationAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// please see the edits in question to see the key generation
filterContext.HttpContext.Items.Add("NoValidation", true);
}
}
Custom ModelMetaData
public class CustomModelMetaData : ModelMetadata
{
public CustomModelMetaData(ModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) :
base(provider, containerType, modelAccessor, modelType, propertyName)
{
}
public override System.Collections.Generic.IEnumerable<ModelValidator> GetValidators(ControllerContext context)
{
if (context.HttpContext.Items["NoValidation"] != null && bool.Parse(context.HttpContext.Items["NoValidation"].ToString()) == true)
return Enumerable.Empty<ModelValidator>();
return base.GetValidators(context);
}
}
Custom ModelMetaDataProvider
public class CustomModelMetaDataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(System.Collections.Generic.IEnumerable<Attribute> attributes,
Type containerType, Func<object> modelAccessor,
Type modelType,
string propertyName)
{
return new CustomModelMetaData(this, containerType, modelAccessor, modelType,
propertyName);
}
}
Global.asax.cs
ModelMetadataProviders.Current = new CustomModelMetaDataProvider();
Usage
[HttpGet]
[NoValidation]
public ActionResult Office(GestionOffice model)
{
...
}
================================= UPDATES =====================================
Instead of creating a CustomModelMetaData
by inheriting ModelMetadata
I think it would be good to inherit from DataAnnotationsModelMetadata
.
public class CustomModelMetaData : DataAnnotationsModelMetadata
{
public CustomModelMetaData(DataAnnotationsModelMetadataProvider provider, Type containerType,
Func<object> modelAccessor, Type modelType, string propertyName,
DisplayColumnAttribute displayColumnAttribute) :
base(provider, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute)
{
}
public override IEnumerable<ModelValidator> GetValidators(ControllerContext context)
{
var itemKey = this.CreateKey(context.RouteData);
if (context.HttpContext.Items[itemKey] != null &&
bool.Parse(context.HttpContext.Items[itemKey].ToString()) == true)
{
return Enumerable.Empty<ModelValidator>();
}
return base.GetValidators(context);
}
private string CreateKey(RouteData routeData)
{
var action = (routeData.Values["action"] ?? null).ToString().ToLower();
var controller = (routeData.Values["controller"] ?? null).ToString().ToLower();
return string.Format("NoValidation_{0}_{1}", controller, action);
}
}
In the CustomModelMetaDataProvider
you have to set some properties.
public class CustomModelMetaDataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes,
Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var displayColumnAttribute = new List<Attribute>(attributes).OfType<DisplayColumnAttribute>().FirstOrDefault();
var baseMetaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
// is there any other good strategy to copy the properties?
return new CustomModelMetaData(this, containerType, modelAccessor, modelType, propertyName, displayColumnAttribute)
{
TemplateHint = baseMetaData.TemplateHint,
HideSurroundingHtml = baseMetaData.HideSurroundingHtml,
DataTypeName = baseMetaData.DataTypeName,
IsReadOnly = baseMetaData.IsReadOnly,
NullDisplayText = baseMetaData.NullDisplayText,
DisplayFormatString = baseMetaData.DisplayFormatString,
ConvertEmptyStringToNull = baseMetaData.ConvertEmptyStringToNull,
EditFormatString = baseMetaData.EditFormatString,
ShowForDisplay = baseMetaData.ShowForDisplay,
ShowForEdit = baseMetaData.ShowForEdit,
Description = baseMetaData.Description,
ShortDisplayName = baseMetaData.ShortDisplayName,
Watermark = baseMetaData.Watermark,
Order = baseMetaData.Order,
DisplayName = baseMetaData.DisplayName,
IsRequired = baseMetaData.IsRequired
};
}
}
Upvotes: 2
Reputation: 1609
If you want to use without validation in production code, you will need to disable the client side validation, since on the server side it will be disabled.
modify web.config as fallows:
<appSettings>
<add key="ClientValidationEnabled" value="false" />
</appSettings>
Upvotes: 0
Reputation: 1609
The validation happens in POST so you must remove Model.IsValid
and add [ValidateInput(false)]
[HttpPost]
[ValidateInput(false)]
[ActionName("Office")]
public ActionResult OfficePost(GestionOffice model)
{
model.Save();
return RedirectToAction("List");
}
Now the post will succeed but you must be careful if you insert into a database.
Upvotes: 0
Reputation: 1609
Yes, you use the [ValidateInput(false)]
attribute like this:
[ValidateInput(false)]
[HttpGet]
public ActionResult Office(GestionOffice model)
{
model.Initialize();
return View(model);
}
Upvotes: 1