ASP.Net Web API - How can I make it so that prefixes are not required when model binding from the query string? - asp.net-web-api

In ASP.Net Web API (RC) I have a test model class like so:
[ModelBinder]
public class TestRequest
{
public string Foo { get; set; }
public string Bar { get; set; }
}
My controller looks like this:
public class TestController : ApiController
{
public TestRequest Get(TestRequest model)
{
return model;
}
}
Now if I invoke the action via:
http://.../test?foo=abc&bar=xyz
neither values bind, because the model binder is expecting model prefixes, such that I actually need to call:
http://.../test?model.foo=abc&model.bar=xyz
I can understand that this is so that other action parameters can bind correctly, but in my case the model is a clean way of encapsulating all the possible action parameters so that I don't need to have a nasty action method signature with a whole lot of optional parameters. It also allows for easy model validation.
Is there any easy way to cause model binding to behave the same way as it would in MVC, or in a POST request?

Removing the ModelBinder attribute from your model class should work in the example you've posted. You'll run into issues for more complex method signatures, see Rick Strahl's comment: http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx#10302750

Related

read Asp.Net Web api GET values from url

I am trying to map /{Controller}/{Variable1}/{Variable2}/{Variable3} to a GET method in controller
public TestController{
public ActionResult Get([FromUrl] Entity instance){}
}
So I need to map variables to the entity.
To put it into an example
/Product/{category}/{filter1}/{filter2}/
Entity
public class ProductSearchRequest
{
public string Category{get;set;}
public string filter1 {get;set;}
public string filter2 {get;set;}
}
Controller
public ProductController: Controller {
public ActionResult GET([FromUri] ProductSearchRequest productSearchRequest){
}
}
[EDITED]
Had to do following changes to get this working
Instead of RouteCollection.MapHttpRoute use HttpConfiguration.Routes.MapHttpRoute as this is API routing not MVC routing.
Inherit controller from ApiController rather than Controller which I was before.
Basically you are not going to be able to do that. Complex types are not compatible with the routing mechanism.
Take a read of this article. But this paragraph explains why the routing mechanism cannot do what you are asking.
A complex type can only bind to the URI through a custom binding. But
in that case, the framework cannot know in advance whether the
parameter would bind to a particular URI. To find out, it would need
to invoke the binding. The goal of the selection algorithm is to
select an action from the static description, before invoking any
bindings. Therefore, complex types are excluded from the matching
algorithm.
Therefore the basic rule is:
For every parameter of the action, if the parameter is taken from the
URI, then the parameter name must be found either in the route
dictionary or in the URI query string. (Optional parameters and
parameters with complex types are excluded.)
Which means you need to define your action like so:
public ActionResult GET(string Category, string filter1, string filter2){
}
And your route template:
/{controller}/{category}/{filter1}/{filter2}/

ASP.NET MVC 3 - Passing variable model to controller

I have this controller
public class DownloadController : Controller
{
[HttpPost]
public FileContentResult GetFile(MyModel model)
{
// Action code
}
}
And this model
public class MyModel
{
public string Test { get; set; }
}
Passing model from the View to the controller works fine like this
#using (Html.BeginForm("GetFile", "Download", FormMethod.Post))
{
#Html.HiddenFor(m => m.Test)
<button type="submit" name="submit" class="submit">Download</button>
}
The model is correctly passed to the controller and I can do what I need to do with it.
Now, what I'm trying to achieve is to make this GetFile() controller action to be generic, so I can pass it any model, without strongly typing the model class in method signature, like I did in the example above.
I know I can achieve this by overriding GetFile() method once for each model that I have, but I'm wondering is there a better way to do this, to stay DRY as much as possible?
Thank you.
I'd suggest using a base class:
public class BaseGetFileModel {}
which various models will derive from.
[HttpPost]
public FileContentResult GetFile(BaseGetFileModel model)
EDIT:
OK, if you want a generic way of doing this, then you could do this:
[HttpPost]
public FileContentResult GetFile()
{
var someValue = Request["SomeValue"];
}
You don't accept any model parameter, you simply pick up POST'd values from the request. Or you could iterate through the request values collection, if you want to avoid hard-coding key names.

ASP.NET WebApi modelbinding to a complex type containing DateTime

This question regards ASP.NET WebApi in MVC 4, not ASP.NET MVC.
Consider the following WebApi Model
public class MyComplex
{
public DateTime? Date { get; set; }
}
bound by model binding as parameter to the following ApiController method
public class MyController : ApiController
{
[HttpPost]
public HttpResponseMessage PostMyComplex(MyComplex id){ .. }
}
How do I declare what format the model binder should expect for MyComplex.Date? As I see it, Modelbinders in WebApi does the trick when the parameter is of type DateTime itself, but I haven't seen a good way of handling date formats when they are part of a complex type and I want to restrict what is acceptable for the api. I have gone down the route of experimenting with both Modelbinders and ValueProviders, none of which seems suitable.
I want to explicitly restrict acceptable HttpPosts on format dd-MM-yyyy.

MVC 3 split parameters in HttpPost action

I have an MVC 3 app and I have created a generic wrapper object, which has some navigation properties and the wrapped object of T, whose values I'm editing/displaying.
public class NavigationViewModel<T>
{
public T Model { get; set; }
public NavigationHelper NavigationHelper { get; set; }
public NavigationViewModel() { }
public NavigationViewModel(T model, NavigationHelper helper)
{
this.Model = model;
this.NavigationHelper = helper;
}
}
My controller resolves this object nicely in an action like this:
public ActionResult Foo(NavigationViewModel<Bar> viewModel)
Code in my view looks like this:
#Html.EditorFor(model => model.Model.SomeProperty)
My colleague said that that code is not nice to read. I already have a strongly typed view, the Model and this Model has another property called Model. He suggested to rename the Model property to ViewModel and I agreed with his reasoning.
Now, the code with the renamed properties does not work anymore: NavigationViewModel viewModel is null. So I changed the signature of the HttpPost method to the following and it works again:
[HttpPost]
public ActionResult Foo(NavigationHelper helper, Bar viewModel)
I like this very much! I can directly access my viewModel in code, the code in the view makes sense and the helper object does not get in the way. I haven't seen this convention before and I guess it worked before because of the naming convention. Using a property called Model hinted at how to resolve the object. Without that property, it couldn't resolve it anymore.
I would like to adopt this for other kinds of helpers that contain view-specific properties, like select-lists or other properties that I otherwise might have put in my ViewBag. Would you guys recommend this approach or will I run into trouble later on using this?
I think I have a really simple answer for you, just don't name your action parameter viewModel, so change:
public ActionResult Foo(NavigationViewModel viewModel)
public ActionResult Foo(NavigationViewModel model)
Or any other parameter name that does not collide with your ViewModel property on your NavigationViewModel class.

Constructor injection of a View Model instance used as an Action method parameter

When a view model is created you can populate the options (e.g. used in a dropdown list) into a setter property of the view model.
The problem is that when that view model is later passed as a parameter (by the framework!) into an action method, those property values has not become automagically
repopulated, so if you need to redisplay the form because of validation errors, you need to repopulate those options again.
One potential solution, which I am asking for specifically in this question, is how to make the MVC framework instantiate the view model with constructor injection, which would provide the view model constructor with an implementation of some kind of data access object (e.g. a repository) that can be used for retrieving the options when they are requested by the view (e.g. in the helper method "DropDownListFor") ?
I think the solution might have something to do with implementations of IModelBinderProvider or IModelBinder but after having experimented with these things from example code snippets here and there on the net, I am still looking for a completely working example, with downloadable executable code without any missing piece of how putting all things together.
If you are looking for some alternative discussion about how to populate a select list, e.g. with "Dependecy Lookup" instead of "Dependecy Injection" you may want to check out the following discussion:
Best way to populate SelectList for ViewModel on GET/POST
Best way to populate SelectList for ViewModel on GET/POST
Some days ago I wrote the following follow-up-question in that thread about the "Dependecy Injection" I am now looking for in this thread:
https://stackoverflow.com/a/8674525/310457
(which provides a code example about the problem I am looking for a solution of)
But instead of hoping that someone will find that old thread with a less specific title, I have created this new question with a more specific subject about what I am looking for.
And I will also provide a link from that thread into this new question for anyone that want to follow-up regarding this specific solution I am looking for.
I'm assuming you want to have your ViewModels automatically injected with something via their Constructor - for example some kind of configuration object that the View will use to determine what to show. I'm also assuming that this approach is causing a "No parameterless constructor defined for this object" error when MVC tries to automatically create and bind a model instance, from the arguments of your Controller Action. Let's also then assume that we will use a DI framework to inject the SiteConfig object into our Controllers automatically at runtime.
This means that the only problem we have to solve is how to get the injected object from our Controller into its Actions' ViewModels when they are automatically bound.
So let's define a base model for others to inherit from.
BaseViewModel
public class BaseViewModel
{
public ISiteConfig SiteConfig { get; set; }
public BaseViewModel(ISiteConfig siteConfig)
{
this.SiteConfig = siteConfig;
}
}
And now let's create a model that inherits from it.
IndexViewModel
public class IndexViewModel : BaseViewModel
{
public string SomeIndexProperty { get; set; }
public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {}
}
And now let's define a Base Controller that our Controllers will inherit from.
BaseController
public abstract class BaseController : Controller
{
protected BaseController(ISiteConfig siteConfig)
{
_siteConfig = siteConfig;
}
private readonly ISiteConfig _siteConfig;
public ISiteConfig SiteConfig
{
get
{
return _siteConfig;
}
}
}
Now we define our actual controller.
HomeController
public HomeController: BaseController
{
public HomeController(ISiteConfig siteConfig): base(siteConfig) {}
}
Assuming we're using Ninject for DI, Ninject would be configured to automatically create the Controller and pass a concrete ISiteConfig object into its Constructor at runtime.
Now we add our Action to the Controller.
Index Action
public ActionResult Index(IndexViewModel model)
{
return View(model);
}
And so this is the point where without doing anything else, MVC will explode with a "Parameterless Constructor" error if you try to call the Index Action, because MVC can't find a ViewModel constructor that takes no arguments.
And so, the answer. We need to override the default ModelBinder.
BaseViewModelBinder
public class BaseViewModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel)))
{
var baseControl = controllerContext.Controller as BaseController;
if (baseControl == null)
{
throw new Exception("The Controller must derive from BaseController");
}
var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType);
return instance;
}
else
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
}
}
And we need to set this as the default model binder in global.asax.cs :
protected void Application_Start()
{
...
ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder();
}
That's all. As you can see, when you view the Index Action now, MVC will use our custom model binder. It will realise that the IndexViewModel derives from BaseViewModel, and so will attempt to spin up an IndexViewModel instance using the ISiteConfig it can find in the Action's Controller (because the Controller derives from BaseController).

Resources