Adding values to model and passing trough ModelState vaidation - asp.net-mvc

I am trying to understand what would be the best approach to my problem. Let's say I have a model like this:
public class Customer
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
[Required]
public int StoreId { get; set;}
[Required]
public DateTime UpdatedAt {get; set;}
}
and I have an API controller that will have a method like the following to insert a new customer in the database:
public IHttpActionResult Insert(Customer customer)
{
customer.StoreId = 5; //just for example
customer.UpdatedAt = DateTime.Now; //again, just as example
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Customers.Add(customer);
db.SaveChanges();
return Ok(customer.Id);
}
Now as you can see in the method, let's assume the StoreId and the UpdatedAt fields won't be included in the original http request posted to this method, because those values need to be calculated and assigned at the server side (let's say basically the client is not supposed to send those values to the server side method). In this case, the ModelState won't be valid anymore, as it is missing two required fields.
One way you can get around it, is to clear the errors on the model state one by one, by doing:
ModelState["Store.Id"].Errors.Clear();
ModelState["UpdatedBy"].Errors.Clear();
and then doing the validation, but it doesn't look a good way especially if you have many fields that need to be taken care of on the server side.
What are the better solutions?

The good way ? Create a view model specific to the view and have only properties which the view is supposed to provide.
public class CustomerVm
{
public int Id { get; set; }
public string Name { get; set; }
}
In your GET action, send an object of this to the view
public ActionResult Edit(int id) //Or even Create
{
var vm=new CustomerVm { Id=id, Name="Scott to be edited"};
return View(vm);
}
Now your view will be strongly typed to this
#model CustomerVm
#using(Html.BeginForm())
{
#Html.HiddenFor(s=>s.Id)
#Html.TextBoxFor(s=>s.Name)
<input type="submit" />
}
and in your HttpPost action, Use the same view model as your method parameter, read the property values and use that
[HttpPost]
public ActionResult Create(CustomerVm model)
{
var customerEntity = new Customer { Name= model.Name };
//Stuff server should set goes now
customerEntity.StoreId = 23;
customerEntity.UpdatedAt = DateTime.Now;
db.Customers.Add(customerEntity);
db.SaveChanges();
return Ok(customerEntity.Id);
}

Related

Can user hack values in action parameter?

Example:
I have table Orders and table OrderPositions.
public partial class Orders
{
public Orders()
{
this.OrderPositions = new HashSet<OrderPositions>();
}
public int OrderId { get; set; }
public string Title { get; set; }
public virtual ICollection<OrderPositions> OrderPositions { get; set; }
}
public partial class OrderPositions
{
public int OrderPositionId { get; set; }
public int OrderId { get; set; }
public string Name { get; set; }
public virtual Orders Orders { get; set; }
}
On the view user can modify single record from OrderPositions table.
In controller:
[HttpPost]
public ActionResult Edit(OrderPositions orderPosition)
{
// save orderPosition
}
So parameter orderPosition.Orders should be = null because on the form in view user can modify only order position. But can user hack it? I mean that in parameter orderPosition.Orders won't be null and I update record not only in table OrderPositions but also in table Orders? Or ASP.NET MVC prevent from that situation?
It really depends on what you do here
[HttpPost]
public ActionResult Edit(OrderPositions orderPosition)
{
// save orderPosition
}
If you're saving the whole entity then yes there is nothing stopping a user passing over addition entity properties. There are a few ways to prevent this though, here are a couple...
1.Create a new entity at the point of saving
[HttpPost]
public ActionResult Edit(OrderPositions orderPosition)
{
if(ModelState.IsValid)
{
var order = new OrderPositions
{
OrderPositionId = orderPosition.OrderPositionId,
OrderId = orderPosition.OrderId,
Name = orderPosition.Name
};
//Then save this new entity
}
}
2.Create a Model specific to the entity's action
public class EditOrderPosition
{
[Required]
public int PositionId { get; set; }
[Required]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
[HttpPost]
public ActionResult Edit(EditOrderPosition model)
{
if(ModelState.IsValid)
{
var order = new OrderPositions
{
OrderPositionId = model.PositionId,
OrderId = model.Id,
Name = model.Name
};
//Then save this new entity
}
}
I generally go with the 2nd method as it stops direct user involvement with my entities. As a rule of thumb I never use entity objects as parameters in controller actions.
Hope this helps
Yes they can. This is one reason I do not expose my entities as a parameter to action methods, instead I use DTOs that only have the properties that I expect.
This is an example of the Mass Assignment Vulnerability.
Yes, there is nothing preventing a rogue app calling your endpoint with arbitrary data. Always validate everything serverside.

Entity Framework Create and Update in List

I have an entity that I am updating and the method in the controller has these lines in it;
db.Entry(userdetails).State = EntityState.Modified;
try {
db.SaveChanges();
}
I think this is fine, however the entity has a collection in it and these records needs to be created, not updated. I am therefore getting this error;
"Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries."
I have read that this may be because my AppUserInfo objects have an id of 0 because they need to be added.
The class for the outer entity looks like this;
public class User {
public Guid UserId { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public List<AppUserInfo> InfoList { get; set; }
}
Now in this situation for example, the phone number could have been modified, and the 'InfoList' collection has new items in that need to be created. Some may need to be updated as well. The 'AppUserInfo' class looks like this;
public class AppUserInfo
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AppUserInfoId { get; set; }
public string info { get; set; }
}
How do I handle this in EF? Am I allowed to save collections in this way? I do not know how to say that there are modifications and additions and possible deletions in the list to EF. Do I simply just remove the line;
db.Entry(userdetails).State = EntityState.Modified;
Any info on the correct way to do this would be very helpful.
Thanks,
db.Entry(userdetails).State = EntityState.Modified;
This line of code is used for editing your objects, if you have no trouble creating your User then all you need is another controller to handle AppUserInfo
Lets imagine that you have a AppUserInfo controller, and you have a AppUserInfo viewModel somewhere. Then you can somthing like the following to accomplish what you want to do.
Your view model like this:
puclic class AppUserInfoCreateViewModels
{
public Guid UserId { get; set; }
public string Info { get; set; }
}
Then your Controller like this:
public class AppUserInfoController : Controller
{
private readonly IYourDataSource _db;
public AppUserInfoController(IYourDataSource db)
{
_db = db;
}
[HttpGet]
public ActionResult Create(int userId)
{
var model = new AppUserInfoCreateViewModels();
model.UserId = userId;
return View(model);
}
[HttpPost]
public ActionResult Create(AppUserInfoCreateViewModels viewModel)
{
if(ModelState.IsValid)
{
var user = _db.Users.Single(d => d.UserId == viewModel.UserId);
var appUserInfo= new AppUserInfo();
appUserInfo.Info= viewModel.Infor;
user.AppUserInfos.Add(appUserInfo);
_db.Save();
return RedirectToAction("detail", "user", new {id = viewModel.UserId});
}
return View(viewModel);
}
}
I hope this helps, ask any questions you may have

ASP.NET MVC 5 Model-Binding Edit View

I cannot come up with a solution to a problem that's best described verbally and with a little code. I am using VS 2013, MVC 5, and EF6 code-first; I am also using the MvcControllerWithContext scaffold, which generates a controller and views that support CRUD operations.
Simply, I have a simple model that contains a CreatedDate value:
public class WarrantyModel
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
DateTime CreatedDate { get; set; }
DateTime LastModifiedDate { get; set; }
}
The included MVC scaffold uses the same model for its index, create, delete, details, and edit views. I want the CreatedDate in the 'create' view; I do not want it in the 'edit' view because I do not want its value to change when the edit view is posted back to the server and I don't want anyone to be able to tamper with the value during a form-post.
Ideally, I don't want the CreatedDate to ever get to the Edit view. I have found a few attributes I can place on the model's CreatedDate property (for example, [ScaffoldColumn(false)]) that prevent it from appearing on the Edit view, but then I'm getting binding errors on postback because the CreatedDate ends up with a value of 1/1/0001 12:00:00 AM. That's because the Edit view is not passing a value back to the controller for the CreatedDate field.
I don't want to implement a solution that requires any SQL Server changes, such as adding a trigger on the table that holds the CreatedDate value. If I wanted to do a quick-fix, I would store the CreatedDate (server-side, of course) before the Edit view is presented and then restore the CreatedDate on postback--that would let me change the 1/1/0001 date to the CreatedDate EF6 pulled from the database before rendering the view. That way, I could send CreatedDate as a hidden form field and then overwrite its value in the controller after postback, but I don't have a good strategy for storing server-side values (I don't want to use Session variable or the ViewBag).
I looked at using [Bind(Exclude="CreatedDate")], but that doesn't help.
The code in my controller's Edit post-back function looks like this:
public ActionResult Edit([Bind(Include="Id,Description,CreatedDate,LastModifiedDate")] WarrantyModel warrantymodel)
{
if (ModelState.IsValid)
{
db.Entry(warrantymodel).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(warrantymodel);
}
I thought I might be able to examine the db.Entry(warrantymodel) object within the if block above and examine at the OriginalValue for CreatedDate, but when I try to access that value (as shown next), I get an exception of type 'System.InvalidOperationException':
var originalCreatedDate = db.Entry(warrantymodel).Property("CreatedDate").OriginalValue;
If I could successfully examine the original CreatedDate value (i.e., the one that is already in the database) I could just overwrite whatever the CurrentValue is. But since the above line of code generates an exception, I don't know what else to do. (I thought about querying the database for the value but that's just silly since the database was already queried for the value before the Edit view was rendered).
Another idea I had was to change the IsModified value to false for the CreatedDate value but when I debug then I discover that it is already is set to false in my 'if' block shown earlier:
bool createdDateIsModified = db.Entry(warrantymodel).Property("CreatedDate").IsModified;
I am out of ideas on how to handle this seemingly simple problem. In summary, I do not want to pass a model field to an Edit view and I want that field (CreatedDate, in this example) to maintain its original value when the other Edit fields from the view are posted back and persisted to the database using db.SaveChanges().
Any help/thoughts would be most appreciated.
Thank you.
You should leverage ViewModels:
public class WarrantyModelCreateViewModel
{
public int Id { get; set; }
public string Description { get; set; }
DateTime CreatedDate { get; set; }
DateTime LastModifiedDate { get; set; }
}
public class WarrantyModelEditViewModel
{
public int Id { get; set; }
public string Description { get; set; }
DateTime LastModifiedDate { get; set; }
}
The intention of a ViewModel is a bit different than that of a domain model. It provides the view with just enough information it needs to render properly.
ViewModels can also retain information that doesn't pertain to your domain at all. It could hold a reference to the sorting property on a table, or a search filter. Those certainly wouldn't make sense to put on your domain model!
Now, in your controllers, you map properties from the ViewModels to your domain models and persist your changes:
public ActionResult Edit(WarrantyModelEditViewModel vm)
{
if (ModelState.IsValid)
{
var warrant = db.Warranties.Find(vm.Id);
warrant.Description = vm.Description;
warrant.LastModifiedDate = vm.LastModifiedDate;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(warrantymodel);
}
Furthermore, ViewModels are great for amalgamating data from multiple models. What if you had a details view for your warranties, but you also wanted to see all servicing done under that warranty? You could simply use a ViewModel like this:
public class WarrantyModelDetailsViewModel
{
public int Id { get; set; }
public string Description { get; set; }
DateTime CreatedDate { get; set; }
DateTime LastModifiedDate { get; set; }
List<Services> Services { get; set; }
}
ViewModels are simple, flexible, and very popular to use. Here is a good explantion of them: http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/
You're going to end up writing a lot of mapping code. Automapper is awesome and will do most of the heavy lifting: http://automapper.codeplex.com/
This is not an answer for the questions, but it might be critical for those who is using Bind() and facing different problems. When I was searching "why Bind() clears out all pre-existing but not-bound values", I found this:
(in the HttpPost Edit()) The scaffolder generated a Bind attribute and added the entity created by the model binder to the entity set with a Modified flag. That code is no longer recommended because the Bind attribute clears out any pre-existing data in fields not listed in the Include parameter. In the future, the MVC controller scaffolder will be updated so that it doesn't generate Bind attributes for Edit methods.
from a official page (last updated in 2015, March):
http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application#overpost
According to the topic:
Bind is not recommended and will be removed in the future from the auto-generated codes.
TryUpdateModel() is now the official solution.
You can search "TryUpdateModel" in the topic for details.
It may solve your problem
In Model:
Use ?
public class WarrantyModel
{
[Key]
public int Id { get; set; }
public string Description { get; set; }
DateTime? CreatedDate { get; set; }
DateTime? LastModifiedDate { get; set; }
}
After form submit:
public ActionResult Edit([Bind(Include = "Id,Description,CreatedDate,LastModifiedDate")] WarrantyModel warrantymodel)
{
if (ModelState.IsValid)
{
db.Entry(warrantymodel).State = EntityState.Modified;
db.Entry(warrantymodel).Property("CreatedDate").IsModified=false
db.SaveChanges();
return RedirectToAction("Index");
}
return View(warrantymodel);
}
+1 for cheny's answer. Use TryUpdateModel instead of Bind.
public ActionResult Edit(int id)
{
var warrantymodel = db.Warranties.Find(id);
if (TryUpdateModel(warrantymodel, "", new string[] { "Id", "Description", "LastModifiedDate" }))
{
db.SaveChanges();
return RedirectToAction("Index");
}
return View(warrantymodel);
}
If you want to use View Model, you can use Automapper and configure it to skip null values so the existing data still exists in the domain model.
Example:
Model:
public class WarrantyModel
{
public int Id { get; set; }
public string Description { get; set; }
DateTime CreatedDate { get; set; }
DateTime? LastModifiedDate { get; set; }
}
ViewModel:
public class WarrantyViewModel
{
public int Id { get; set; }
public string Description { get; set; }
DateTime? CreatedDate { get; set; }
DateTime? LastModifiedDate { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="Id,Description,LastModifiedDate")] WarrantyViewModel warrantyViewModel)
{
var warrantyModel = db.Warranties.Find(warrantyViewModel.Id);
Mapper.Map(warrantyViewModel, warrantyModel);
if (ModelState.IsValid)
{
db.Entry(warrantyModel).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(warrantyModel);
}
Automapper:
Mapper.CreateMap<WarrantyViewModel, WarrantyModel>()
.ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));
try to remove the Create date prompt text box in the Edit view. In my application, the scaffold generated Edit and Create Views contain the Primary key which is generated in the database.
Controller:
...
warrantymodel.CreatedDate = DateTime.Parse(Request.Form["CreatedDate"]);
...

Different models for Get and Post - MVC

As I understand from the question below it should be possible to use different models for Get and Post actions. But somehow I'm failing to achieve just that.
What am I missing?
Related question: Using two different Models in controller action for POST and GET
Model
public class GetModel
{
public string FullName;
public string Name;
public int Id;
}
public class PostModel
{
public string Name;
public int Id;
}
Controller
public class HomeController : Controller
{
public ActionResult Edit()
{
return View(new GetModel {Id = 12, Name = "Olson", FullName = "Peggy Olson"});
}
[HttpPost]
public ActionResult Edit(PostModel postModel)
{
if(postModel.Name == null)
throw new Exception("PostModel was not filled correct");
return View();
}
}
View
#model MvcApplication1.Models.GetModel
#using (Html.BeginForm()) {
#Html.EditorFor(x => x.Id)
#Html.EditorFor(x=>x.Name)
<input type="submit" value="Save" />
}
Your models aren't using proper accessors so model binding doesn't work. Change them to this and it should work:
public class GetModel
{
public string FullName { get; set; }
public string Name { get; set; }
public int Id { get; set; }
}
public class PostModel
{
public string Name { get; set; }
public int Id { get; set; }
}
A bit of clarification
GET and POST controller actions can easily use whatever types they need to. Actually we're not talking about models here. Model is a set of classes/types that represent some application state/data. Hence application or data model.
What we're dealing here are:
view model types
action method parameter types
So your application model is still the same. And GetModel and PostModel are just two classes/types in this model. They're not model per-se.
Different types? Of course we can!
In your case you're using a view model type GetModel and then pass its data to PostModel action parameter. Since these two classes/types both have properties with same matching names, default model binder will be able to populate PostModel properties. If property names wouldn't be the same, you'd have to change the view to rename inputs to reflect POST action type property names.
You could as well have a view with GetModel type and then post action with several different prameters like:
public ActionResult Edit(Person person, IList<Address> addresses)
{
...
}
Or anything else. You'll just have to make sure that post data can be bound to these parameters and their type properties...

MVC strongly-typed view, and server side setting properties before sending to lower layers?

I have a layered application that send commands to the business layer (actually, the application is based on ncqrs framework, but I don't think it's important here).
A command looks like this :
public class RegisterUserCommand : CommandBase
{
public string UserName { get; set; }
public string Email{ get; set; }
public DateTime RegistrationDate { get; set; }
public string ApiKey {get; set;} // edit
}
There is no logic in this class, only data.
I want to have the users type their user name, email and I want the system to use the current date to build the command.
What is best between :
create a strongly typed view based on the RegisterUserCommand, then inject the date and the APi Key just before sending it to the business layer ?
create a RegisterUserViewModel class, create the view with this class and create the command object based on the view input ?
I wrote the following code (for the solution n°2) :
public class RegisterController : Controller
{
//
// GET: /Register/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
RegistrationDate = DateTime.UtcNow,
Email= registrationData.Email,
UserName= registrationData.Name,
ApiKey = "KeyFromConfigSpecificToCaller" // edit
}
);
return View();
}
public class RegisterUserViewModel
{
[Required]
[StringLength(16)]
public string Name { get; set; }
[Required]
[StringLength(64)]
public string Email{ get; set; }
}
}
This code is working... but I wonder if I choose the correct way...
thanks for advises
[Edit] As the Datetime seems to cause misunderstanding, I added another property "ApiKey", that should also be set server side, from the web layer (not from the command layer)
[Edit 2] try the Erik suggestion and implement the 1st solution I imagined :
[HttpPost]
public ActionResult Index(RegisterUserCommand registrationCommand)
{
var service = NcqrsEnvironment.Get<ICommandService>();
registrationCommand.RegistrationDate = DateTime.UtcNow;
registrationCommand.ApiKey = "KeyFromConfigSpecificToCaller";
service.Execute(
registrationCommand
);
return View();
}
... Is it acceptable ?
I think you would be better off with option #2, where you would have a separate ViewModel and a Command. While it may seem redundant (to an extent), your commands are really messages from your web server to your command handler. Those messages may not be formatted the same as your ViewModel, nor should they be. And if you're using NCQRS as is, you would then have to map your commands to your AR methods and constructors.
While it may save you a little bit of time, I think you pigeon-hole yourself in to modeling your domain after your ViewModels, and that should not be the case. Your ViewModels should be a reflection of what your user experiences and sees; your domain should be a reflection of your business rules and knowledge, and are not always reflected in your view.
It may seem like a bit more work now, but do yourself a favor and keep your commands separate from your view models. You'll thank yourself later.
I hope this helps. Good luck!
I would recommend putting this into the constructor of the RegisterUserCommand class. That way the default behavior is always to set it to DateTime.UtcNow, and if you need to set it to something explicitly you can just add it to the object initializer. This will also help in scenarios where you're using this class in other parts of your project, and you forget to set the RegistrationDate explicitly.
public class RegisterUserCommand : CommandBase
{
public string UserName { get; set; }
public string Email{ get; set; }
public DateTime RegistrationDate { get; set; }
public RegisterUserCommand()
{
RegistrationDate = DateTime.UtcNow;
}
}
And the Controller
public class RegisterController : Controller
{
//
// GET: /Register/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
Email= registrationData.Email,
OpenIdIdentifier = registrationData.OpenIdIdentifier
}
);
return View();
}
public class RegisterUserViewModel
{
[Required]
[StringLength(16)]
public string Name { get; set; }
[Required]
[StringLength(64)]
public string Email{ get; set; }
}
}
I would use number 1 and use the system.componentmodel.dataannotations.metadatatype for validation.
I created an example (answer) for another SO question Here.
This allows you to keep your model in another library, validate the fields and show the fields like you would internal/private classes with DataAnnotations. I'm not a big fan of creating a completely separate class for a view that has no additional value while having to ORM the data back to another class. (If you had additional values like dropdown list values, or default values then I think it would make sense).
Instead of
[HttpPost]
public ActionResult Index(RegisterUserViewModel registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
service.Execute(
new RegisterUserCommand
{
RegistrationDate = DateTime.UtcNow,
Email= registrationData.Email,
UserName= registrationData.Name,
ApiKey = "KeyFromConfigSpecificToCaller" // edit
}
);
return View();
}
You can have
[HttpPost]
public ActionResult Index(RegisterUserCommand registrationData)
{
var service = NcqrsEnvironment.Get<ICommandService>();
registrationData.ApiKey = "KeyFromConfigSpecificToCaller";
service.Execute(registrationData);
return View();
}

Resources