MVC Model Binding with Attribute Routing - asp.net-mvc-routing

Converting an MVC 3 app to MVC 5 and relying exclusively on Attribute Routing. All is good but I am unable to define a route for a controller method that accepts a (not so) complex object as a parameter.
If I omit the parameter in the route, my controller/method is never invoked and I receive a 404 (as I would expect). When I include the viewModel parameter as part of the route, my method is invoked but the parameter is always null.
This code worked fined before with "classic" routing and no specific route was needed to model bind my viewModel parameter. I never defined it as part of any route other than including the controller and method names. The view is unchanged.
[RoutePrefix("Text")]
[Route("{action}")]
public class TextController : Controller
{
[HttpGet]
[Route("{jobTextID}")]
public ViewResult Edit(Guid jobTextID)
{
// this (get) works fine
}
[HttpPost]
[Route("{viewModel}"]
public RedirectToRouteResult Edit(TextEditViewModel viewModel)
{
// viewModel is always null
}
}

I think you should add the id to the POST action:
[HttpPost]
[Route("{jobTextID}")]
public RedirectToRouteResult Edit(Guid jobTextID, TextEditViewModel viewModel) { }
The resource is identified by the URL, not by a parameter in the body of the request. Use jobTextID to retrieve the entity from the database and ignore the id from the viewModel.

Related

Why does [FromBody] ignore model binders?

I'm working on an REST API in ASP.NET. One of the first things I made was a Model Binder for the model, binded with the model by a code like this:
[ModelBinder(typeof(MyModelModelBinder))]
public class MyModel { ... }
And used it in an action
public IHttpActionResult Post(MyModel model) { ... }
I realised, quite suddenly, that after I added the [FromBody] before the parameter type...
public IHttpActionResult Post([FromBody]MyModel model) { ... }
...the Model Binder gets ignored. I tried to debug it by puting a breakpoint on the beginning of the BindModel method and at the beginning of the action and realised that when I call the action I get straight to the action. When I delete the FromBody Attribute the breakpoint in the ModelBinder becomes active again.
Why can't I use both? Why is the FromBody attribute ignoring the ModelBinder?

Web Api 2 Inheritance No route providing a controller name was found to match request URI

Basically I cannot get my Web Api 2 application to work.
First of all here are my requirements.
In my application I am creating a dozen of controllers ( ProductController, ItemController, SalesController...etc). There are 2 actions which are absolutely common in all my controllers:
FetchData, PostData
(Each controller then may implement a number of other methods which are sepcific to its business domain )
Instead of repeating these actions in every controllers like:
public class ProductController:ApiController{
[HttpPost]
public MyReturnJson FetchData( MyJsonInput Input){
....
return myJsonResult;
}
}
public class SalesController:ApiController{
[HttpPost]
public MyReturnJson FetchData( MyJsonInput Input){
....
return myJsonResult;
}
}
I decided to create a base controller MyBaseController:
public class MyBaseController : ApiController{
[HttpPost]
public MyReturnJson FetchData( MyJsonInput Input){
....
return myJsonResult;
}
}
with the 2 methods so every other controller would inherit them (It saves me from repeating them in every controller). The common base class has been defined and implemented in a separate assembly which is then referenced in my web project.
Then in my javascript client (using breeze) I call a specific controller like
breeze.EntityQuery.from('FetchData')
where my serviceName is 'my_api/product/'
(in the WebApiConfig, the routing table has been defined like:
config.Routes.MapHttpRoute(
name: "my_api",
routeTemplate: "my_api/{controller}/{action}"
);
But when the javascript code is executed I get the error message:
No route providing a controller name was found to match request URI
http://localhost:xxxxx/my_api/product/FetchData
If I don't use a common base class but instead repeat this method (FetchData) in every class (basically ProductController inherits directly from ApiController and not from MyBaseController) every thing works fine and my method is hit. I thing there is a problem with the inheritance scheme. Maybe there is something I don't get (first time using Web Api 2) or some constraints (routing, configuration...) I do not respect. Right now I am stuck and I would appreciate any suggestion which might point me to the right direction. Is inheritance allowed in Web Api 2?
I am not sure why your code is not working. But in the next link (http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22#ARI) you can see an example of inheritance using attribute routing.
This is the code example:
public class BaseController : ApiController
{
[Route("{id:int}")]
public string Get(int id)
{
return "Success:" + id;
}
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
(inherit: true);
}
}
I hope that it helps.

Optional route parameters and action selection

I use the default route definition:
{controller}/{action}/{id}
where id = UrlParameter.Optional. As much as I understand it this means when id is not being part of the URL this route value will not exists in the RouteValues dictionary.
So this also seems perfectly possible (both GET):
public ActionResult Index() { ... } // handle URLs: controller/action
public ActionResult Index(int id) { ... } // handle URLs: controller/action/id
When id is missing the first action would be executed, but when id is present, the second one would execute. Fine, but it doesn't work. It can't resolve actions.
How can I accomplish this?
I'm thinking of writing a custom action method selector attribute like:
[RequiresRouteValue(string valueName)]
This would make it possible to use this kind of action methods. But is this the only way of doing it?
Is there something built-in I can hang on to?
Use either:
[HttpGet]
public ActionResult Index() { ... } // handle URLs: controller/action
[HttpPost]
public ActionResult Index(int id) { ... } // handle URLs: controller/action/id
Or just have one with a nullable param:
public ActionResult Index(int? id) { ... } // handles both instances
EDIT:
Would something like this work?
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/", // URL with parameters
new { controller = "Login", action = "Index" } // Parameter defaults
);
routes.MapRoute(
"DefaultWithValue", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Login", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Well from the exception that action can't be determines is pretty clear that actions are resolved first then data binder comes into play and examines action's parameters and tries to data bind values to them. Makes perfect sense.
This makes perfect sense. There would be no point in first trying to data bind values to all possible types and see what we get and then look for an appropriate action. That would be next to impossible.
So. Since action selection is the problem here I guess the best (and only) way to solve this (if I don't want to use a multifaceted single action method) is to write a custom action method selector attribute.
You can read all the details and get the code on my blog:
Improving Asp.net MVC maintainability and RESTful conformance

In asp.net mvc 2 : how to access http post data inside constructor of any controller

My controller has abstract base controller. I want to access the form post data inside abstract base class constructor. How can we do that ?
public abstract class AppController : Controller
{
public AppController()
{
// request post data required here
}
}
public class ProductController : AppController
{
public ProductController() { }
}
Purpose : Updating second dropdown on change of first dropdown. Both are on MASTER page.
Code given above is one of the 2 options to pass data to master page:
Add using ViewData in ALL the action methods.
Do it in only one place using abstract base controller - add the required data using ViewData inside its constructor and make our main controller class implement this abstract base controller class. So that we don't have to add the viewdata for master page in all action methods.
I don't know what is your final goal with this but this is something which is not recommended to be done in MVC. The Request object is not yet initialized in the constructor of the controller. You could try to use the native HttpContext object:
string foo = System.Web.HttpContext.Current.Request["foo"];
but that's something extremely bad and I would never recommend you doing this as now your controller is coupled to the static native HttpContext instance without any chance of unit testing it.
Instead of using the constructor you could override the Initialize method of your controller where you will have access to the request context and you could read posted data:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
string foo = requestContext.HttpContext.Request["foo"];
}

Dependency Injection & Model Binding (ASP MVC, Autofac), When to use what?

This is more like a conceptual question. When to use Model Binding (in ASP.NET MVC Framework) and when to inject objects using IoC (lets say Autofac here) ?
One specific scenario is like lets say, I have the following action method
public ActionResult EditProfile(string UserId)
{
// get user object from repository using the the UserId
// edit profile
// save changes
// return feedback
}
In the above scenario, is it possible to inject a user object to action method such that it automatically gets the user object using the UserId ? The resulting signature being:
public ActionResult EditProfile(UserProfile userObj) //userObj injected *somehow* to automatically retreive the object from repo using UserId ?
Sorry if it all doesn't makes sense. It`s my first time using IoC.
EDIT:
This is the way to do it > http://buildstarted.com/2010/09/12/custom-model-binders-in-mvc-3-with-imodelbinder/
You can do what you need using a custom action filter. By overriding OnActionExecuting, we have access to the route data, and the action parameters of the action that will be executed. Given:
public class BindUserProfileAttribute : ActionFilterAttribute
{
public override OnActionExecuting(FilterContext filterContext)
{
string id = (string)filterContext.RouteData.Values["UserId"];
var model = new UserProfile { Id = id };
filtextContext.ActionParameters["userObj"] = model;
}
}
This attribute allows us to create the parameters that will be passed into the action, so we can load the user object at this point.
[BindUserProfile]
public ActionResult EditProfile(UserProfile userObj)
{
}
You'll probably need to get specific with your routes:
routes.MapRoute(
"EditProfile",
"Account/EditProfile/{UserId}",
new { controller = "Account", action = "EditProfile" });
In MVC3 we get access to the new IDepedencyResolver interface, which allows us to perform IoC/SL using whatever IoC container or service locator we want, so we can push a service like a IUserProfileFactory into your filter, to then be able to create your UserProfile instance.
Hope that helps?
Model binding is used for your data. Dependency injection is used for your business logic.