I was getting the query string back using:
public ActionResult Index(int id)
{
var queryString = Request["myQueryString"];
}
Then I looked at:
help-testing-mvc3-controller-that-accesses-querystring
Which states:
It is against MVC's design pattern to use HttpRequest directly. You can access the query string variables on your action as parameters.
I don't really understand this. Is what I've done against the design pattern? If it is why is that and how could it be done?
It breaks the concept of model binding. It also gets complicated with unit testing and trying to new up a new HttpContext for a test. If it was just a parameter, you could just pass the value.
The preferred (and easier to read) method would be:
public ActionResult Index(int id, string myQueryString)
{
...
}
Your action method should take most of the data submitted from your form. One of the strengths of MVC is the model binding it has within it. Check out this page, as it has a good example of this:
http://www.codeproject.com/Articles/159749/ASP-NET-MVC-Model-Binding-Part1
You can accept literals (string, bool, etc.) but also strongly typed objects in your action methods.
Related
I read here that I can't overload actions in MVC because of routing confusion
I tried to overload Index() in HomeController and I got the exception as article said, but I noticed that
microsoft has overloaded the actions in AccountController
public ActionResult Login(string returnUrl){}
public ActionResult Login(LoginModel model, string returnUrl){}
Please need clarification, thanks
Microsoft has overloaded this by setting HttpGet and HttpPost. One for GET request and another for POST request. What about your code?
[HttpGet]
public ActionResult Login(string returnUrl){}
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl){}
Till today you cannot overload your controller's Action method with same name but different parameters.
The only possibility is to have two overload, and for that you need to set the method's property to HttpGet and HttpPost. For example
[HttpGet]
public ActionResult foo(string myString)
{
return View();
}
[HttpPost]
public ActionResult foo(MyViewModelClass object)
{
return View();
}
And regarding your confusion,
From general convention, first method should be of type Get which gets called when someone sends request to access that page.
Second method is called when user submits a form with his login details.
In AccountController first method works with GET method, second with POST one. It was realized by attribute [HttpGet] and [HttpPost].
Read more about get and post here.
In addition to above answer we can add name attributes along with HTTPGET and HTTPPOST as
[HttpPost]
[ActionName("Edit")]
public ActionResult Edit_Post(some parameters)
{
//code over here
}
After this we can call it as :- /MVC/EmployeeController/Edit/1
Some definitions first:
Overloading is a form of polymorphism, in particular an interface, in the sense of a class publicly visible part, related one.
When we speak about inheritance we mean overriding.
Action is a segment of a URL.
Back to your question...
It is the ControllerActionInvoker which is responsible for finding the method to which an action is mapped. Given GET and POST we see polymorphic methods in a class mapped to the same action, but serving different HTTP methods (any action, that is, URL segment polymorphism here?!). And again yes, we may use ActionNameAttribute (MVC v5) to map an action to a class method, but again, this has nothing to do with any sort of polymorphism. Simply put, all that happens is in the ControllerActionInvoker and has nothing to do with overloading or overriding, and finally with any sort of polymorphism -- it is just mapping.
Conclusion.
A simple question arises:
What in a string (a segment of a URL, 3.) relates to any one of the OOP definitions (1. and 2.) above? Or, in other words, can any of the OOP concepts above be transformed to a (part of a) string?
The fact that a transformation between URL segment, which happen to be called "action", and a class method exits does not imply that all we know about the first mechanically can be applied to the second and vice-verse. The question is misleading and its main intention is to confuse someone, not to test his/her knowledge (I suspect that this is an artificial interview questions, not a real one).
I am using a third party service that does an async callback to a URL I provide to them.
So I tell them to use http://www.mysite.com/Status/Incoming.
This must obviously map to an Incoming() method on my StatusController.
However, what I don't have control over is the format of the parameters they call my URL with.
E.g. They will do a callback such as: http://www.mysite.com/Status/Incoming?param1=val1¶m2=val2¶m3=val3
I want to map this to the parameters of my action method: Incoming(string param1, string param2, int param3)
How do I do this?
I have found a lot of stuff about custom routing, but nothing about legacy QueryString parameters.
There is no such thing as legacy query string parameters. There are query string parameters and they are part of the HTTP specification. And assuming that the http://www.mysite.com/Status/Incoming?param1=val1¶m2=val2¶m3=val3 url is called you don't need any route to make it map to the following action (the default route will do just fine):
public ActionResult Incoming(string param1, string param2, string param3)
{
...
}
The default model will take care of binding those values.
Why not use a catch all?
routes.MapRoute(
"Incoming",
"Status/Incoming/{*path}", // URL with parameters
new { controller = "Status", action = "Incoming"}
);
then in your controller,
public ActionResult Incoming(string path){
// the next line would probably be better off in a model binder, but this works:
var dictionary = path
.Substring(path.IndexOf("?")+1)
.Split("&")
.Select(x =>
{
var kvArray = x.Split("=");
return new KeyValuePair<string, string>(kvArray[0], kvArray[1]);
})
.ToDictionary(x=>x.Key,x=>x.Value);
return Incoming(dictionary);
}
public ActionResult Incoming(Dictionary<string,string> dictionary){
//do stuff
}
All that being said, I think using the Request.QueryString is probably a better approach. As long as you are using MVC, it is accessible from your controller. However, if you can guarantee that the correct parameters will be passed then Darin's approach is going to be the best choice.
When I've had to deal with this before now, I just use the "legacy" call of Request.QueryString. It still works, even if it isn't very graceful.
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?
Most of the actions in my controller have statically-defined parameter lists and so they correspond nicely with the standard tutorial examples:
public ActionResult SomeAction(string id, string arg1, string arg2)
{
// use arg1, arg2...
}
But I have one troublesome case where the view puts together a form dynamically, so the set of parameters is completely dynamic. I'd be happy with a mapping of string names to string values.
I've tried this:
public ActionResult TroublesomeAction(string id, IDictionary<string, string> args)
{
// loop through args...
}
But args is passed a null. What's the easiest way in an action to get hold of the famous "parameter dictionary" that we hear so much about in error messages these days?
And if there isn't an easy way, how would I do it the hard way?
From a controller, we have access to this:
Request.Form
Which contains exactly what I need, so no need to map to an argument on the action.
I have multiple controller actions that takes an id
public ActionResult Get(int? id) {
...
}
public ActionResult Delete(int id) {
...
}
public JsonResult GetJson(int? id) {
...
}
I'm thinking its best practice to use a ModelBinder (SomeObjectFromIDModelBinder) on each action, so the obtaining of the object is separated from the controller, and keeps the action methods smaller.
The reason I don't want it to be called SomeObjectModelBinder is because I also have a need to recreate Models from JSON, so have a SomeObjectFromJsonModelBinder which handles recreating the 'SomeObject' from a JSON string.
I'm thinking this is a suitable usage of ModelBinders (naming convention), but just wanted clarification. Thoughts?
You don't need to do anything to get the ID. THe MVC handler will look for simple values of the same name as the method parameters in the Form data, the Query String, and the Route.
So you could have a route /{controller}/{action}/{id} with defaults of action="Get", id=1.
then the id could be specified in the URL as either /Home/Get/3 or /Home/Delete?id=6.
Alternately, you could have a textbox with an id of "id" and a submit button in a form that posts to "/Home/Get".
ModelBinders are intended to allow you to have action methods that are classes by creating the instance and populating the properies from the Form data, Query string, or Route data.
I've decided that it is acceptable to do what I was asking, and having multiple ModelBinders that will bind different data to a Model.