I want to be able to send JSON as opposed to the standard QueryStrings when making a post to my controllers in ASP.Net MVC. I have the Front-End stuff working fine (building and then submitting my JSON objects).
The problem is on the controller side where the default ModelBinders that ship with the MVC framework do not support this.
I have seen a combination of ways around this, one of them is to apply a filter which takes the object as a parameter, uses a JSON library to de-serialise it, and adds that to the action parameters. This is not ideal.
The other, better, way is to use a custom Model Binder. All the ones I have seen though presume you will have only one model and that will be a class rather than a variable. If you have multiple ones it breaks down.
Has anyone else encountered this? One idea I had was if I could simply override how MVC deals with the FormCollection and intercept there, adding the values to the collection myself and hoping MVC can do the rest in it's normal fashion. Does anyone know if that is possible?
The key issue, I think, is that my problem is not with binding because my view models are no different to how they where before. The problem is getting the values from the JSON Post.
If I am correct MVC get's the values from the QueryString and puts it into the form collection which is then used for ModelBinding. So shouldn't the correct method be to change the way the FormCollection gets assigned?
Example of an action:
public ActionResult MyFirstAction(Int32 ID, PersonObject Person, ClassObject ClassDetails)
{
//etc
}
The normal binding works, JSON doesn't and all the example of Model Binders will not work either. My best solution so far is to convert the object to a dictionary and loop though each param and match it up. Doesn't seem ideal.
I use a custom model binder for json like this:
public class JsonModelBinder<T> : IModelBinder {
private string key;
public JsonModelBinder(string requestKey) {
this.key = requestKey;
}
public object BindModel(ControllerContext controllerContext, ...) {
var json = controllerContext.HttpContext.Request[key];
return new JsonSerializer().Deserialize<T>(json);
}
}
And then wire it up in Global.asax.cs like this:
ModelBinders.Binders.Add(
typeof(Product),
new JsonModelBinder<Product>("ProductJson"));
You can read more about this here: Inheritance is Evil: The Epic Fail of the DataAnnotationsModelBinder
EDIT
The JsonModelBinder should be used on the controller action parameter typed as Product only. The Int32 and ClassObject should fall back to the DefaultModelBinder. Are you experiencing a different result?
Related
Assume we have update form.
This form requires two actions. One of them for GET request to show data and second for POST request to save changes.
[HttpGet]
public ActionResult Update(int id)
{
var viewModel = GetViewModel(id);
viewModel.Dictionaries = GetUpdateDictionaries(id);
return View(viewModel);
}
[HttpPost]
public ActionResult Update(UpdateViewModel viewModel)
{
if(Model.IsValid)
{
Save(viewModel);
return RedirectToHome();
}
viewModel.Dictionaries = GetUpdateDictionaries(id);
return View(viewModel);
}
It is a classical approach. We pass to POST update the same view model. But with this view model we pass many unnecessary data. For example dictionaries. Someone tells that it is a bad practice and suggest two alternative ways:
1) Use view model only for form data and pass dictionaries and other stuuf through ViewBug
2) Create special class only for necessary form data and use it in post update action.
Are there standart approach?
If your dictionaries are not part of the Model then the alternatives which MVC provide for passing information to the View is:
ViewData["MyDictionaries"]= GetUpdateDictionaries(id);
or
TempData["MyDictionaries"]= GetUpdateDictionaries(id);
or for .Net 4:
ViewBag.MyDictionaries= GetUpdateDictionaries(id);
Is your concern that viewModel.Dictionaries will all get sent to the client browser and then get posted back to the server? It won't. Only things that you render in markup e.g. with Html.TextBoxFor() get sent to the browser. So your current code is okay from that point of view.
The real objection to your dictionaries is, if the dictionaries are not a part of the thing your Model is a model of. The point of OO is that your models are models of something; so should only have properties corresponding to the something that it models.
If your models are not models of something, but only ViewModels, then putting the dictionaries on them in fine. But in that case, Save(model) would not be fine.
I'm working on an ASP.NET MVC project that will allow users to perform batch edits on the attributes of objects. The implementation is in a sort of "wizard" like form with four phases to the process as follows:
"Select the attributes you want to edit" - the first page will present the user with a list of checkboxes representing each of the attributes they want to edit. The user should check the attributes they wish to edit and select "Continue".
"Edit the selected attributes" - the second page will present the user with a list of distinct "editors" which will be unique for each of the attributes they selected on the first page.
"Review your changes" - this page will allow the user to review the changes they've made to the attributes they selected.
"Submit your changes" - this page will actually submit the information about the edits the user wishes to make to the selected attributes against the selected collection of objects.
Fairly straight-forward.
As I mentioned, the "editor" will be unique to each attribute, and could have any combination of different controls on it. Once a user has made their edits and the application posts that information to the "Review" page is where I'm currently having my problem.
We've developed the concept of an "EditorWorker" class that is unique to each attribute, which is responsible for generating the ViewModel necessary for each editor, but is also responsible for creating/returning (within the "Review" page controller action) an object that is the "model" object for the editor that the post data can be bound to, which can then be use to display the edited data for review. This object should have properties that match up with the IDs of the controls in the editor so that model binding can occur.
I've got the "EditorWorker" creating and returning the class needed, but for some reason, when I call TryUpdateModel and pass in that class, its properties aren't getting populated as a result of that method call as I would expect them to. I have verified that the values are in the posted FormCollection. Below is the code for my controller action where I'm attempting to do this. If someone can help me understand why TryUpdateModel isn't working in this scenario, I would be very appreciative.
[HttpPost]
public virtual ActionResult Review(ReviewBatchViewModel model)
{
var selectedAttributes = GetSelectedAttributes(model.SelectedAttributeIds.Split(',').Select(i => Int64.Parse(i)).ToArray());
var workers = new List<IEditorWorker>();
var reviewData = new Dictionary<ViewAttribute, IEditData>();
foreach (var attribute in selectedAttributes)
{
if (!string.IsNullOrEmpty(attribute.EditorWorker)) // If there is no EditorWorker defined for this object, move on...
{
var worker = ServiceLocator.Current.GetInstance(Type.GetType(string.Format("{0}.{1}", EditorWorkerNamespace, attribute.EditorWorker)));
var attributeEditData = ((IEditorWorker)worker).LoadEditData();
if (TryUpdateModel(attributeEditData))
model.EditData.Add(attributeEditData); // model.EditData is a List<IEditData> that will be iterated on the Review page
reviewData.Add(attribute, attributeEditData);
}
}
return View(model);
}
// ReviewBatchViewModel.cs
public class ReviewBatchViewModel : BaseViewModel
{
public ReviewBatchViewModel() { EditData = new List<IEditData>(); }
public string SelectedAttributeIds { get; set; }
public List<ViewAttribute> SelectedAttributes { get; set; }
public List<IEditData> EditData { get; set; }
}
// IEditData.cs
public interface IEditData
{
}
// BroadcastStatusEditData.cs
public class BroadcastStatusEditData : IEditData
{
public int BroadcastStatus { get; set; }
}
I totally understand that this controller action is incomplete in its current state. I'm presently working on just trying to get those EditData objects populated correctly before I move on. As mentioned, any thoughts would be greatly appreciated. Thanks.
UPDATE: With regards to #mare's comment, I should have explained that part more clearly, sorry. The call to TryUpdateModel actually is returning true, but the fields on the model object being passed into it aren't actually being populated from the values that have been confirmed present in the posted form data. The model object being passed into the call is not a List, its just a poco. The resulting, ultimately hopefully populated model object is then being added to a List collection of model objects that will then be used for displaying the posted data for review on the Review page. I'm not loading anything from a datastore at all. Unique editors for each selected attribute are being rendered to the Edit screen, and I'm attempting to capture the edit values for display on a Review screen prior to submitting the batch of edits to a service. Hopefully that's more clear. Thanks.
UPDATE 2: I've included the definition of the ReviewBatchViewModel class as requested by #mare in the comments. The use of the var keyword in most cases in this code sample is largely due to the fact that the methods that are populating those variables is going to be returning an object of a different type for each attribute selected, so I never know exactly what its going to be at runtime (although it will always implement an interface, in this case either IEditorWorker and/or IEditData). There is a single class in the Model called "Attribute". The provided code sample has three variables relative that class: 1) SelectedAttributeIds is a comma-separated list of the Id's of the attributes that the user has selected to edit, which gets passed from the Edit page to the Review page via hidden field, 2) selectedAttributes is a collection of the actual Attribute objects that correspond to those Ids that I can work with, and 3) attributeEditData is an instance of the IEditData class specific to each given attribute that I'm attempting to bind the posted data from the Edit page to.
Hopefully this additional information clears things up even more.
TryUpdateModel is a generic method, and therefore attempts to infer all type information based on the Generic Type Parameter.
From what I understand in your example above, you are always passing in a IEditData correct?
In effect you are saying:
TryUpdateModel<IEditData>(attributeEditData)
This is most likely the cause for not seeing any properties being set, since IEditData doesn't have any properties ;)
To do what you want you will probably have to create a custom ModelBinder.
As a quick code review side note, your solution seems overly complicated. I had to stare at your solution for a good while just to figure out where to start. Creating a custom model binder may solve your immediate problem, but you might be looking at a big time maintenance headache here. I'm willing to bet there is a simpler approach that will lead to fewer problems down the road.
Based on your comments I have changed the code around from System.Object to your IEditData interface, but everything still holds. I noticed in an earlier comment you mentioned using var because you didn't know the type until runtime. However, there is nothing magic about the var keyword. The only thing it does is give you implicit typing, but it is still statically typed.
The nice thing about MVC is that you can just pop over to Codeplex and have a look at the source for TryUpdateModel if you want. Digging down a few layers you will eventually find a call to this internal method:
protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class {
if (model == null) {
throw new ArgumentNullException("model");
}
//valueProvider is passed into this internal method by
// referencing the public ControlerBase.ValueProvider property
if (valueProvider == null) {
throw new ArgumentNullException("valueProvider");
}
Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
//Binders is an internal property that can be replaced by
// referencing the static class ModelBinders.Binders
IModelBinder binder = Binders.GetBinder(typeof(TModel));
ModelBindingContext bindingContext = new ModelBindingContext() {
Model = model,
ModelName = prefix,
ModelState = ModelState,
ModelType = typeof(TModel),
PropertyFilter = propertyFilter,
ValueProvider = valueProvider
};
binder.BindModel(ControllerContext, bindingContext);
return ModelState.IsValid;
}
Notice the use of typeof(TModel) everywhere... in your case that is getting translated into typeof(IEditData), which isn't very useful since it is only a marker interface. You should be able to adapt this code for your own use, making sure to use GetType() in order to get the actual type at runtime.
I hope this helps out!
P.S. I've added some comments to the above code to help out a little
#Josh, you were very helpful in helping me understand why TryUpdateModel wasn't working for me, and I appreciate that. Unfortunately, I think the larger issue here was that fact that I (not exactly sure which) was either unable or unwilling to try to document all of the details of the requirements for the problem I'm trying to solve here, which I think made it difficult for anyone to be able to provide much meaningful input. The biggest problem for us is that, because we have no idea until runtime which attributes a user has selected for editing, we don't know which objects we'll be working with in the context of these controller actions, or what their types will be. The one place that we safely can work with known data and types, is within the context of each of the unique EditorWorker objects, which is where I've chosen to do the heavy lifting here.
I was hoping and attempting to take advantage of all of the heavy lifting that MSFT has done for us within the MVC framework to handle model binding, but I've come to the conclusion at this point that I don't think that's going to work for us. The solution that I've come up with at this point, is to allow the LoadEditData method of the EditorWorker classes handle loading up the EditData classes for for me. As each EditorWorker class is unique to, and has knowledge of the attribute that it is associated with. The problem I was having originally was that I was letting the EditorWorker.LoadEditData method just return an empty instance of the specific type of EditData class that I needed for the attribute I was currently working with, and let the MVC framework handle model binding to that object for me. That wasn't working because that method is designed to return an object of type IEditData, and I never really knew exactly what type it was that I was currently working with, so I had no way of specifying the type in the call to either of the typed methods: TryUpdateModel<T> or UpdateModel<T>.
So the solution I've come up with, and am going with at least for now (re-education and/or refactoring may very well change this in the future, who knows) is to just pass the Request.Form object into the call to EditorWorker.LoadEditData and let that method handle actually loading up the EditData object that it knows it needs to return for the attribute it's responsible for, which it can do as it knows what information should be in the posted form collection to load up its EditData object.
So that's where I'm at for now. Thanks for the help.
I'm begginer in asp.net mvc and i have some doubts.
P.S: I'm using DDD to learn
I have an ACtion in a Controller and it'll save an entity (from my model) by a repository (for a database).
My doubts is, How can I get the informations from the View and save it by a repository in my Controller ?
Is it correct to get an entity of my Model in Save method of controller, like this:
public ActionResult Save(Product product)
{
// validate object
// save data in repository
return View("Success");
}
Or Need I get an DTO (with a structure similar to my entity) and create an object passing property by property to an entity ?
I didnt' like of FormCollection and I'd like to know, What is recommended architecturally ?
Thanks a lot guys
Cheers
I you want to follow DDD practices as described by the Blue Book you should bind your views to DTO's which can be forwarded to a thin 'Application' layer where Domain objects are created or retrieved from database. This application layer can be a simple facade with methods or can leverage command pattern.
For a live demo, you can see my project -- DDDSample.NET
This kind of problem can be fixed by adding so called view model.
Basically - a view model is DTO that provides data for particular view. In similar fashion - view models are used to get data back from view via model binding. Then - controller just forwards necessary data to domain model.
Typically, in ASP.NET MVC your controller actions will receive strongly typed objects returned by the DefaultModelBinder when editing entity types. Using this pattern you can pass a "Product" to your GET view either by itself or as part of a DTO and then your "Save" method would receive a "Product" object in its parameter list.
So long as you are using either editor templates or fields with matching names (i.e. Html.TextBox("Name") corresponds to Product.Name) then the DefaultModelBinder should be able to correctly fill the typed entity object passed to the action method. You shouldn't have to mess with the FormCollection except in certain edge cases.
[HttpGet]
public ActionResult Create() {
return View("Create", new Product());
}
[HttpPost]
public ActionResult Create(Product product) { //or Save(Product)
...
}
As long as your form has fields which match the fields in Product, they should automatically be populated for you based on the values. How you save the entity depends on the data model, whether you're creating a new record or editing an existing one, etc.
I am trying to find some examples of building a custom model binder for a unique binding scenario I need to handle, but all of the articles I found were for older versions of MVC which are no longer relevant in MVC2. I've been referencing the DefaultModelBinder source code to try to get a general feel for what I need to do, but it's entirely more complicated than my scenario and I'm having trouble isolating the specific logic I need to implement.
My goal is to take a collection of Checkbox/Textbox pairs and for all of the Checked pairs I would like to create a key/value pair of the Checkbox's value and the associated Textbox's value. After aggregating this data I need to do some string serialization on the collection so I can store it in a string property of the desired Model type. I already the data being sent from the form in a manageable format which will allow me to relate a given Checkbox to a specific Textbox, it's just a matter of figuring out how to get all the pieces where I need them.
Does anyone know of some up-to-date tutorials that can get me started with building a custom model binder?
I don't know why you think a lot has changed since MVC 1 regarding custom model binders. But If I understand what you are trying to do, it should be fairly easy.
public class CustomModelBinder : DefaultModelBinder {
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext) {
NameValueCollection form = controllerContext.HttpContext.Request.Form;
//get what you need from the form collection
//creata your model
SomeModel myModel = new SomeMode();
myModel.Property = "value";
//or add some model errors if you need to
ModelStateDictionary mState = bindingContext.ModelState;
mState.Add("Property", new ModelState { });
mState.AddModelError("Property", "There's an error.");
return myModel; //return your model
}
}
And your action :
public ActionResult Contact([ModelBinder(typeof(CustomModelBinder))]SomeModel m){
//...
}
Was that the kind of information you are looking for?
Take a look at several examples of Custom MVC Model binders on my blog.
I need to create something similar to how MVC invokes an Method(Action) and also uses the Model Binder to map a NamedValueCollection to the parameters on that method. Basically I have a Controller action that needs to dynamically call a method on a class, the controller has any information sent in a form or query string plus the name of the method to invoke as a string.
As far as I am concerned it's the same as invoking an action. I am essentially passing it down another level (there is a good reason I can't have these methods on the controller btw).
I downloaded the ASP.Net MVC 1.0 Source but there are a bunch of classes, I am having trouble finding the code that handles this.
I know how to invoke an method whose name is contained in a string, but maybe there is a better way that MVC uses. I also need to know how to use the Model Binders to make Request.Form + Query Strings to that methods parameters.
If anyone could point me to either the code in the MVC source that does this or point me in the right direction with regards to using the default Model Binder in MVC manually I would be grateful.
Let me know if I can make this clearer.
Thanks
It's complicated. FindAction is called on ControllerActionInvoker. This calls, eventually, ReflectedControllerDescriptor.FindAction, which in turn calls ActionMethodSelector.FindActionMethod, which calls RunSelectionFilters on the same type. That method takes a list of methods passed him by the collar, and iterates them, examining the arguments on each method and comparing them with the values in the request. Because this has to run quickly on a request comes in, this is all cached, and because it is designed to be extensible, there are some abstract types in between the layers I've described. Hence, it can be a little difficult to follow at first, and it would probably be difficult to repurpose it for non-controller logic. However, you could use it as a model for implementing your own system. I think it's a little too complicated for a domain-specific application. The number of extension points is probably appropriate for the MVC framework, but for your own code, YAGNI.
I hope this gives you enough to get started, however.
Regarding using a model binder without the web stack: Well, you still need MVC, but not necessarily a web server. Here's how we do it in a unit test:
internal static T Bind<T>(string prefix, FormCollection collection, ModelStateDictionary modelState) where T:BaseTimeRecordPresentationModel
{
var mbc = new ModelBindingContext()
{
ModelName = prefix,
ModelState = modelState,
ModelType = typeof(T),
ValueProvider = collection.ToValueProvider()
};
IModelBinder binder = new TimeRecordModelBinder();
var cc = new ControllerContext();
return binder.BindModel(cc, mbc) as T;
}
internal static T BindAndAssertValid<T>(string prefix, FormCollection collection) where T:BaseTimeRecordPresentationModel
{
var msd = new ModelStateDictionary();
var result = Bind<T>(prefix, collection, msd);
if (!msd.IsValid)
{
Assert.Fail(ModelStateValidationSummary(msd));
}
return result;
}