I have a Validation Framework set up to automatically validate parameters of Web Api action methods (including OData action methods). However, this does not work for PATCH requests that store the changed properties in a Delta<Entity> type.
I've done some digging around and, as you can see in the ASP.NET source code of Delta, it has the NonValidatingParameterBinding attribute, which means that Delta's are not subjected to validation.
Now an obvious solution would be to apply the delta and then perform manual validation of the resulting entity.
But are there other solutions that do not require applying the patch? Ideally it should happen automatically before the action method is called...
Thanks.
It was an explicit design decision to not validate Delta<Entity>. Reason being, a Delta<Entity> is only a partial entity and by definition could be invalid. As you have guessed, the proper approach is to validate the entity after the Delta<TEntity> is applied on top of it.
Sample code,
public void Patch(Delta<Customer> delta)
{
Customer c = new Customer();
delta.Patch(c);
Validate(c, typeof(Customer));
}
private void Validate(object model, Type type)
{
var validator = Configuration.Services.GetBodyModelValidator();
var metadataProvider = Configuration.Services.GetModelMetadataProvider();
HttpActionContext actionContext = new HttpActionContext(ControllerContext, Request.GetActionDescriptor());
if (!validator.Validate(model, type, metadataProvider, actionContext, String.Empty))
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState));
}
}
Complete sample here.
We too arrived at this problem. However, we are using a custom version of Delta<T> (heavily based on OData).
The core issue (apart from [NonValidatingParameterBinding]) is that the entity is hidden (behind delta.GetEntity()) and hence will not be validated. Also we only want to validate changed properties.
We solved this by adding the IValidateObject interface to our custom Delta class, making the signature as follows:
[NonValidatingParameterBinding]
public class Delta<TEntityType> : DynamicObject, IDelta, IValidatableObject
where TEntityType : class
This can probably also be done in a class inheriting from OData's Delta<T>, but that is not something we have tried.
Implementation of the interface looks like this:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var entity = this.GetEntity();
var innerValidationContext = new ValidationContext(entity);
List<ValidationResult> validationResults = new List<ValidationResult>();
foreach (var propertyName in this.GetChangedPropertyNames())
{
innerValidationContext.MemberName = propertyName;
Validator.TryValidateProperty(EntityType.GetProperty(propertyName).GetValue(entity), innerValidationContext, validationResults);
}
return validationResults;
}
Hope this helps!
/Victor
Related
I heard that it is possible to create async validation attributes in ASP.NET WEB API 2, but I didn't find any examples. I would like to create a validation class like this one, but async way:
public class UserNameAvailable : ValidationAttribute
{
public override bool IsValid(object value)
{
var username = value as string;
if(username != null)
{
return !Database.UserExists(username); // must be async
}
return true;
}
}
By validation attribute, I am assuming you are referring to something like an ActionFitler attribute. In Web API 2, we do not yet have async version of methods in ActionFilter attributes yet. However, in the coming release we are supporting this feature.
Its still possible to create custom action filter attributes yourself which implement System.Web.Http.Filters.IActionFilter, but I am wondering if you would rather wait for the next release. if this cannot wait, let me know and I can try something out for you.
Is there any useful hook in ASP.NET MVC (MVC4) which can let you access the Action method parameter (View model) before the action method becomes invoked, and then also (e.g. depending on the value of something you checked in the action method parameter) let you prevent the action method from being invoked, i.e. instead either forward the view model object (action method parameter) to another action method or directly to some view (i.e. without any further processing in an action method) ?
If you do not understand the question, please see the code example below which should illustrate the kind of code I am looking for...
(though I do not know if there actually exists such kind of interface and a possibility to hook an implementation into the MVC framework)
If this is indeed possible, I would like to see an answer with code example about how to do it (and not just a response with someone claiming that e.g. "try using method 'ActionFilterAttribute.OnActionExecuting' or 'IModelBinder.BindModel' " because I have already tried those and could not make it work).
Also, please respect that I do not want this thread to become a discussion about WHY to do it, but want to see HOW to do it.
(i.e. I am not interested in getting into discussions with responses such as "What are you actually trying to achieve?" or "There are probably better things of doing what you want to do...")
The question can be split into three subquestions/code examples as my own code samples below try to illustrate:
(but would like them "refactored" into REAL code with usage of real existing types)
(obviously, every type below which includes the substring "Some" is something I have made up, and I am looking for the corresponding real thing ...)
(1) Example of how to get access to (and potentially modify) view model objects (action method parameters) in a generic place before the actual action method is invoked with the view model object parameter.
The kind of code example I am looking for would probably be similar to below but do not know what kind of interface to use and how to register it to be able to do something like below:
public class SomeClass: ISomeInterface { // How to register this kind of hook in Application_Start ?
public void SomeMethodSomewhere(SomeActionMethodContext actionMethodContext, object actionMethodParameterViewModel) {
string nameOfTheControllerAboutToBeInvoked = actionMethodContext.ControllerName;
string nameOfTheActionMethodAboutToBeInvoked = actionMethodContext.MethodName;
// the above strings are not used below but just used for illustrating that the "context object" contains information about the action method to become invoked by the MVC framework
if(typeof(IMyBaseInterfaceForAllMyViewModels).IsAssignableFrom(actionMethodParameterViewModel.GetType())) {
IMyBaseInterfaceForAllMyViewModels viewModel = (IMyBaseInterfaceForAllMyViewModels) actionMethodParameterViewModel;
// check something in the view model:
if(viewModel.MyFirstGeneralPropertyInAllViewModels == "foo") {
// modify something in the view model before it will be passed to the target action method
viewModel.MySecondGeneralPropertyInAllViewModels = "bar";
}
}
}
}
(2) Example of how to prevent the targeted action method from being executed and instead invoke another action method.
The example might be an extension of the above example, with something like below:
public void SomeMethodSomewhere(SomeActionMethodContext actionMethodContext, object actionMethodParameterViewModel) {
... same as above ...
if(viewModel.MyFirstGeneralPropertyInAllViewModels == "foo") {
actionMethodContext.ControllerName = "SomeOtherController";
actionMethodContext.MethodName = "SomeOtherActionMethod";
// The above is just one example of how I imagine this kind of thing could be implemented with changing properties, and below is another example of doing it with a method invocation:
SomeHelper.PreventCurrentlyTargetedActionMethodFromBecomingExecutedAndInsteadExecuteActionMethod("SomeOtherController", "SomeOtherActionMethod", actionMethodParameterViewModel);
// Note that I do _NOT_ want to trigger a new http request with something like the method "Controller.RedirectToAction"
}
(3) Example of how to prevent the normal action method from being executed and instead forward the view model object directly to a view without any further processing.
The example would be an extension of the first above example, with something like below:
public void SomeMethodSomewhere(SomeActionMethodContext actionMethodContext, object actionMethodParameterViewModel) {
... same as the first example above ...
if(viewModel.MyFirstGeneralPropertyInAllViewModels == "foo") {
// the below used razor view must of course be implemented with a proper type for the model (e.g. interface 'IMyBaseInterfaceForAllMyViewModels' as used in first example above)
SomeHelper.PreventCurrentlyTargetedActionMethodFromBecomingExecutedAndInsteadForwardViewModelToView("SomeViewName.cshtml", actionMethodParameterViewModel);
}
You could use an action filter and override the OnActionExecuting event:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
...
}
}
Now let's see what useful information you could extract from this filterContext argument that is passed to this method. The property you should be looking for is called ActionParameters and represents an IDictionary<string, object>. As its name suggests this property contains all the parameters that are passed to the controller action by name and value.
So let's suppose that you have the following controller action:
[MyActionFilter]
public ActionResult Index(MyViewModel model)
{
...
}
Here's how you could retrieve the value of the view model after model binding:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var model = filterContext.ActionParameters["model"] as MyViewModel;
// do something with the model
// You could change some of its properties here
}
}
Now let's see the second part of your question. How to shortcircuit the controller action and redirect to another action?
This could be done by assigning a value to the Result property:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
... some processing here and you decide to redirect:
var routeValues = new RouteValueDictionary(new
{
controller = "somecontroller",
action = "someaction"
});
filterContext.Result = new RedirectToRouteResult(routeValues);
}
}
or for example you decide to shortcircuit the execution of the controller action and directly render a view:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var viewResult = new ViewResult
{
ViewName = "~/Views/FooBar/Baz.cshtml",
};
MyViewModel someModel = ... get the model you want to pass to the view
viewResult.ViewData.Model = model;
filterContext.Result = viewResult;
}
}
or you might decide to render a JSON result:
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MyViewModel someModel = ... get the model you want to pass to the view
filterContext.Result = new JsonResult
{
Data = model,
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
}
So as you can see the possibilities are unlimited of what you can do.
I have experimented with the code in the answer provided by the user Darin Dimitrov, and the first and third parts of the answer are correct.
(Though, for others who might find this thread and be interested, I can clarify that in the first answer the "model" does not seem to
be a hardcoded keyword always used for the model but seems to have to correspond to the chosen name of the action method parameter.
In other words, if you instead have the method signature
public ActionResult Index(MyViewModel myViewModel)
then in your action filter you have to use
var model = filterContext.ActionParameters["myViewModel"] as MyViewModel;
)
Regarding the second answer, the usage of 'RedirectToRouteResult' will trigger a new http request (which was not desired as I mentioned in the second code example of mine).
I found another way of "changing" action method by actually invoking it explicitly:
var controller = new SomeController();
ActionResult result = controller.SomeAction(model);
filterContext.Result = result;
The above code actually seems to prevent the originally targeted action method from becoming invoked, i.e. when I put a breakpoint in the method annotated with '[MyActionFilter]' the execution never got into that method.
Typically, it is probably not desired to hardcode a controller like above, but instead reflection might be used, for example as below with the thirdpart library "fasterflect":
string nameOfController = ...
string nameOfActionMethod = ...
// both above variables might for example be derived by using some naming convention and parsing the refering url, depending on what you want to do ...
var theController = this.GetType().Assembly.CreateInstance(nameOfController);
ActionResult result = (ActionResult)theController.CallMethod(nameOfActionMethod, model);
filterContext.Result = result;
(for those who want to extract the names of the current target controller and action method, when implementing logic to determine the controller you want to invoke, you can use this code in the filter:
var routeValueDictionary = filterContext.RouteData.Values;
string nameOfTargetedController = routeValueDictionary["controller"].ToString();
string nameOfTargetedActionMethod = routeValueDictionary["action"].ToString();
)
I think it feels a bit awkward to instantiate and invoke controllers like above, and would prefer to change the target controller and action method in another way if possible ?
So, the remaining question is if there is still (in MVC 4 final version) no way of redirecting/forwarding execution "internally" (without a new http request being fired as with 'RedirectToAction') at the server ?
Basically, I think I am here just looking for something like "Server.Transfer" which was used with ASP.NET Web Forms (and also the old classic ASP I believe could use the same thing).
I have seen older question/answers on this issue with people implementing this behaviour themselves with some "TransferResult" class of their own, but it seems to tend to become broken i different MVC versions.
(for example, see here for MVC 4 beta: How to redirect MVC action without returning 301? (using MVC 4 beta) ).
Is there really still not a simple standard solution (implemented in MVC 4 final) about how to do an "internal redirect" without a new http request (as RedirectToAction does) ?
I have a Data Access Layer, a Service Layer, and a Presentation Layer. The Presentation Layer is ASP.NET MVC2 RTM (web), and the Service Layer is WCF (services). It's all .NET 3.5 SP1.
The problem is that in the services, the objects being returned are marked with the [DataContract] attribute. The web is using the AppFabric Cache (a.k.a Velocity) SessionStateProvider to store session state. Due to this, anything I store in the session must be serializable.
Here comes the problem: the DataContracts aren't marked with [Serializable] and as far as I can remember, by introducing it onto a class already marked with [DataContract] some issues arise, and so I don't believe this is a solution.
I was initially planning on using the DataContracts right in the web layer, using them as models to views related to rendering the DataContracts (probably nested inside a higher level ViewModel class). But due to the session state provider requiring all objects stored inside it to be serializable, I'm starting to rethink this strategy. It would be nice to have though, since they contain validation logic using the IDataErrorInfo interface, and the same validation logic could be re-used in MVC as part of model binding.
What do you believe is the best way to allow me to reduce the work needed?
I've currently thought of the following different ways:
A. Create a 'ServiceIntegration' part in the web project.
This would be a middle man between my controllers and my WCF service layer. The ServiceIntegration part would speak to the service layer using DataContracts, and to the Web layer using ViewModels, but would have to transform between the DataContracts and ViewModels using a two-way Transformer.
Also, since the IDataErrorInfo Validation wouldn't be re-usable, it would be necessary to create a Validator per DataContract too, that uses the Transformer to convert from ViewModel to DataContract, perform validation using IDataErrorInfo and return its results. This would then be used inside action methods of Controllers (e.g. if (!MyValidator.IsValid(viewModel)) return View();)
Different classes required: xDataContract, xViewModel, xTransformer, xValidator
B. Create a 'SessionIntegration' part in the web project
This would be a middle-man between the controllers (or anything accessing the session) and the session itself. Anything requiring access to the session would go through this class. DataContracts would be used in the entire application, unless they are being stored into the session. The SessionIntegration part would take the responsibility of transforming the DataContract to some ISerializable form, and back. No additional Validator is needed because of the use of of IDataErrorInfo interface on the DataContract.
Different classes required: xDataContract, xTransformer, xSerializableForm
Note: there would still be ViewModels around in both scenarios, however with (B) I'd be able to compose ViewModels from DataContracts.
(B) has the benefit of not needing an extra validator.
Before I go off and implement (A)/(B) fully, I'd like some feedback. At the moment, I'm starting to lean towards (B), however, (A) might be more flexible. Either way, it seems like way too much work for what it's worth. Has anyone else come across this problem, do you agree/disagree with me, and/or do you have any other way of solving the problem?
Thanks,
James
Without going the full blown route of A or B, could you just make a generic ISerializable wrapper object and put those in your SessionState?
[Serializable]
public class Wrapper : ISerializable
{
public object Value { get; set; }
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (Value != null)
{
info.AddValue("IsNull", false);
if (Value.GetType().GetCustomAttributes(typeof(DataContractAttribute), false).Length == 1)
{
using (var ms = new MemoryStream())
{
var serializer = new DataContractSerializer(Value.GetType());
serializer.WriteObject(ms, Value);
info.AddValue("Bytes", ms.ToArray());
info.AddValue("IsDataContract", true);
}
}
else if (Value.GetType().IsSerializable)
{
info.AddValue("Value", Value);
info.AddValue("IsDataContract", false);
}
info.AddValue("Type", Value.GetType());
}
else
{
info.AddValue("IsNull", true);
}
}
public Wrapper(SerializationInfo info, StreamingContext context)
{
if (!info.GetBoolean("IsNull"))
{
var type = info.GetValue("Type", typeof(Type)) as Type;
if (info.GetBoolean("IsDataContract"))
{
using (var ms = new MemoryStream(info.GetValue("Bytes", typeof(byte[])) as byte[]))
{
var serializer = new DataContractSerializer(type);
Value = serializer.ReadObject(ms);
}
}
else
{
Value = info.GetValue("Value", type);
}
}
}
}
As an extension to the provided answer, I added these two methods to ease storing/retrieving the data.
public static void Set<T>(HttpSessionStateBase session, string key, T value)
{
session[key] = new Wrapper(value);
}
public static T Get<T>(HttpSessionStateBase session, string key)
{
object value = session[key];
if (value != null && typeof(T) == value.GetType())
{
return (T) value;
}
Wrapper wrapper = value as Wrapper;
return (T) ((wrapper == null) ? null : wrapper.Value);
}
This makes it a little easier to set/get values from the session:
MyDataContract c = ...;
Wrapper.Set(Session, "mykey", c);
c = Wrapper.Get<MyDataContract>(Session, "mykey");
To make it even easier, add extension methods:
public static class SessionWrapperEx
{
public static void SetWrapped<T>(this HttpSessionStateBase session, string key, T value)
{
Wrapper.Set<T>(session, key, value);
}
public static T GetWrapped<T>(this HttpSessionStateBase session, string key)
{
return Wrapper.Get<T>(session, key);
}
}
And use as below:
MyDataContract c = ...;
Session.SetWrapped("mykey", c);
c = Session.GetWrapped<MyDataContract>("mykey");
I am exploring Sharp Architecture and I would like to know how to
access the validation results after calling Entity.IsValid().
I have two scenarios e.g.
1) If the entity.IsValid() return false, I would like to add the
errors to ModelState.AddModelError() collection in my controller.
E.g. in the Northwind sample we have an EmployeesController.Create()
action when we do employee.IsValid(), how can I get access to the
errors?
public ActionResult Create(Employee employee) {
if (ViewData.ModelState.IsValid && employee.IsValid()) {
employeeRepository.SaveOrUpdate(employee);
}
// ....
}
[I already know that when an Action method is called, modelbinder
enforces validation rules(nhibernate validator attributes) as it
parses incoming values and tries to assign them to the model object
and if it can't parse the incoming values then it register those as
errors in modelstate for each model object property. But what if i
have some custom validation. Thats why we do ModelState.IsValid
first.]
2) In my test methods I would like to test the nhibernate validation
rules as well. I can do entity.IsValid() but that only returns true/
false. I would like to Assert against the actual error not just true/
false.
In my previous projects, I normally use a wrapper Service Layer for
Repositories, and instead of calling Repositories method directly from
controller, controllers call service layer methods which in turn call
repository methods. In my Service Layer all my custom validation rules
resides and Service Layer methods throws a custom exception with a
NameValueCollection of errors which I can easily add to ModelState in
my controller. This way I can also easily implement sophisticated
business rules in my service layer as well. I kow sharp architecture
also provides a Service Layer project. But what I am interested in and
my next question is:
How I can use NHibernate Vaidators to implement sophisticated custom
business rules (not just null,empty, range etc.) and make
Entity.IsValid() to verify those rules too ?
"How I can use NHibernate Vaidators to implement sophisticated custom business rules (not just null,empty, range etc.) and make Entity.IsValid() to verify those rules too ?"
You have to create a custom validation attribute:
here is an example:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class LoginUniqueAttribute : ValidationAttribute
{
private static readonly string DefaultErrorMessage = MUI.LoginNameInUse;
public LoginUniqueAttribute()
: base(DefaultErrorMessage)
{
}
public override string FormatErrorMessage(string name)
{
return DefaultErrorMessage;
}
public override bool IsValid(object value)
{
if (string.IsNullOrEmpty((string)value))
{
return true;
}
var userService = IoC.Resolve<IUserService<User>>();
return userService.GetByLogins(new[] { (string)value }).Count() == 0;
}
}
and usages into a UserInput dto
[Required]
[LoginUniqueAttribute]
[RegularExpression("^[A-Za-z0-9.'\\s]+$", ErrorMessage = "Only characters and digits are allowed")]
[DisplayNameLocalized(typeof(MUI), "LoginName")]
public string LoginName { get; set; }
also do not forget to initialize the validation in your global.asax.cs file on Application_Start:
private void InitializeValidator()
{
var provider = new NHibernateSharedEngineProvider();
NHibernate.Validator.Cfg.Environment.SharedEngineProvider = provider;
}
I'm having trouble trying to think what the best way is to recreate a database object in a controller Action.
I want to make use of ModelBinders so in my action I have access to the object via a parameter, rather than having to repeat code to get an object from the database based on an identifier parameter. So I was thinking of having a ModelBinder that performs a call to the dataaccess layer to obtain the original object (or creates a new one if it doesn't exist in the database), then binds any properties to the database object to update it. However I've read that the ModelBinders shouldn't make database queries (first comment of this article).
If the ModelBinder shouldn't perform a database query (so just using the DefaultModelBinder) then what about database objects that have properties that are other db objects? These would never get assigned.
Saving an object after the user has edited it (1 or 2 properties are editable in the view) the ModelBinded object would be missing data, so saving it as it is would result in data in the database being overwritten with invalid values, or NOT-NULL constraints failing.
So, whats the best way to get an object in a controller action from the database bound with the form data posted back from the view?
Note im using NHibernate.
I get the model object from the database, then use UpdateModel (or TryUpdateModel) on the object to update values from the form parameters.
public ActionResult Update( int id )
{
DataContext dc = new DataContext();
MyModel model = dc.MyModels.Where( m => m.ID == id ).SingleOrDefault();
string[] whitelist = new string[] { "Name", "Property1", "Property2" };
if (!TryUpdateModel( model, whitelist )) {
... model error handling...
return View("Edit");
}
ViewData.Model = model;
return View("Show");
}
Unfortunately you don't have control over the construction of the model binder, so you can't inject any repository implementation.
You can reach out directly into a service locator to pull in your repository & fetch the item:
public class ProductBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
if(modelType != typeof(Product))
return null;
var form = controllerContext.HttpContext.Request.Form;
int id = Int32.Parse(form["Id"]);
if(id == 0)
return base.CreateModel(controllerContext, bindingContext, modelType);
IProductRepository repository = ServiceLocator.Resolve<IProductRepository>();
return repository.Fetch(id);
}
}
You might even make this work for all of your entities if you can use a base class or interface that provides the Id of the class.
You'll have to set this up in Global.asax:
ModelBinders.Binders.Add(typeof(Product), new ProductBinder());
and then you can do this:
public ActionResult Save([Bind] Product product)
{
....
_repository.Save(product);
}
Let me first state that I don't recommend to access database from ModelBinders, as from perspective of Separation Of Concern ModelBinders should only be responsible of interpretting client request, obviously database is not.
If you dont want to repeat your self (DRY), use repositories/services
However if u really want to do it like that, then
In global.asax.cs Register a custom MyModelBinderProvider to MVC
ModelBinderProviders.BinderProviders.Add(new EntityModelBinderProvider
{
ConnectionString = "my connection string"
));
Cunstruct the custom ModelBinderProvider to contain database settings
public class EntityBinderProvider: IModelBinderProvider
{
public string ConnectionString { get; set; }
public IModelBinder GetBinder(Type modelType)
{
if (Is known entity)
return new EntityBinder(ConnectionString);
else
return null;
}
}
Follow further instructions from Ben Scheirman
You don't actually have to hit the database. Simply setting the Id of the objects will be enough to set the relationship up, but watch your cascades. Make sure your cascde settings won't update the related object as it will clear the values.