MVC3 Model binding in HTTP GET request?

2019-06-20 11:16发布

问题:

Without customization, can I do something like this in MVC 3?

    [HttpGet]
    public ViewResult MyAction(ViewModel model)
    {
        // Do stuff
        return View("ViewName", model);
    }

The reason I am doing this is to pass data between different pages as part of a work flow. (I.e. when user fnishes what's needed in step 1, pass the form data to step 2...)

回答1:

It will work as long as you have the same parameter Name as of the Property name of your Model class

Assuming your class is like this

public class ViewModel
{
  public string Name { set;get;}
  public string Loc{ set;get;}
}

You can do a Get request like this

MyAction?Name=jon&Loc=America


回答2:

You can do it; it will automatically bind any values in the query string to properties with matching names.

That said, it's not something that's generally done; it's the [HttpPost] method where you see the model binding performed, as the interfaces for the two actions need to be different somehow. You can solve that by posting back to a different action name, but you may still trigger model validation errors on the (partial) load of the model, which would be really confusing to a user.



回答3:

Shyju's answer only works if the members of class in the endpoint's method signature contains only scalar properties. But what if you have nested classes? Let's assume that your ViewModel class looks like this:

public class ViewModel
{
    public string Name { get; set; }
    public string Title { get; set; }
    public Address MyAddress { get; set; }
}

And the Address class looks like this:

public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
}

Now let's say the GET request was done via AJAX and you did something like this in JavaScript:

var address = {
  Line1: "123 Nowhere St.",
  Line2: "Apt. B5"
}
var getRequestData = {
  Name: "Joe",
  Title: "Manager",
  MyAddress: address
}
var uriString = $.param(getRequestData); //the parameters for the GET request
$.get("/ViewResult?" + uriString, function (data) { /*callback function*/ });

Even though the shape of your address object in JavaScript perfectly matches the C# Address class in the endpoint's method signature, the Line1 and Line2 sub-properties will NOT bind. Their values will come through as null.

There are two workarounds to this.

Workaround 1:

The first is to use dot notation when naming the parameters in the GET request instead of nested JavaScript objects. Using this method, the GET request data in AJAX would look like this:

var getRequestData = {
  Name: "Joe",
  Title: "Manager",
  MyAddress.Line1: "123 Nowhere St.",
  MyAddress.Line2: "Apt. B5"
}

MVC model binding will know how to do this, as long as all your property names all match up (they are case-sensitive, so be careful).

If you're not using AJAX, but just a plain HTML form submit, it's even easier. Just name the input elements with that same dot notation. Razor syntax makes this really easy with helper methods like TextBoxFor(), but here's an example in plain HTML:

<form method="get" action="/ViewResult">
  <input type="text" name="Name" />
  <input type="text" name="Title" />
  <input type="text" name="MyAddress.Line1" />
  <input type="text" name="MyAddress.Line2" />
  <button type="submit">Submit GET request</button>
</form>

Workaround 2:

The other way around this is to simply use a POST request instead of a GET. Beware that it's technically bad practice to perform a POST request without the intent of actually changing some data on the server side, but it is an option.



回答4:

You can post a form to a get by setting the PostMethod attribute to get. If the form's input fields match any of the accepting ViewModel then they will be filled. These matches are determined by the name field in an input (<input name="MatchedField"> -> public string MatchedField { get; set; }).

What you should do is pass the form from a post, and then redirect to the get from the post action. This pattern is best practice and is known as the Post-Redirect-Get pattern.



回答5:

I would advise against this approach. Best solution to just use POST, because if you use GET, once you click back from step 3 to step 2 and the browser cache is not available, you will perform actions on an old version of the ViewModel. Is there a particular reason why you want to use GET?



回答6:

I can not suggest to use QueryString to pass values. You can use one of below: This code will render a partial view with the given model.Be sure you add model to your view. And your view should be placed in Shared folder

public ActionResult myaction(ViewModel model)
        {

            return PartialView("anotherView", model);
        }

Another way to do almost the same thing:

 public ActionResult myaction(ViewModel model)
        {
            return View("someAnotherView", model);
        }

if your view is not in the same controller , use the path for view name like "../Controller/viewName"

There is also a different approach which can be done by using TempData:

public ActionResult myaction(ViewModel model)
        {
            TempData["model"] = model;
            return RedirectToAction("someAnotherView");
        }

but you should reach your data in the view with the code as shown below:

   @{
    ViewModel model=(ViewModel)TempData["model"];

    }

Hope one of above helps..

Regards



回答7:

For Web API 2:

[HttpGet]
public ActionResult Get([FromUri]ViewModel model)
{
    // Do stuff
    return View("ViewName", model);
}