Bill Barry
Bill Barry

Reputation: 3523

ASP.NET MVC - Controller parameter not being gathered from form?

We are having this problem with a controller right now; the controller looks like this:

public class AccountsController:Controller {
    public ActionResult List(int? page, int? pageSize, string keywords) {...}
}

We are posting to this page via jquery:

$.post("/myapp/Accounts/List",
       {"page":0,"pageSize":10,"keywords":"asdf"}, 
       updategrid,
       "json");
...
function updategrid(result) {...}

Inside the action: Request.Form["keywords"] == "asdf", but keywords=="" and here I am at a loss. Why doesn't keywords have the value we want?

Upvotes: 1

Views: 1774

Answers (7)

Bill Barry
Bill Barry

Reputation: 3523

quoting answer provided as comment from Mark Worth...

How is your controller being instantiated? I had this problem and I found that it was my SpringControllerFactory creating my controllers as singletons (and hence was always using the values from the first request). – Mark Worth May 19 at 8:19

That's it! My controller was registered as a singleton into my Windsor container so my WindsorControllerFactory returned a singleton. – Bill Barry May 19 at 18:14

Upvotes: 1

Taliesin
Taliesin

Reputation: 461

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
    public class JsonParametersFilter : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            string contentType = filterContext.HttpContext.Request.ContentType;

            if (string.IsNullOrEmpty(contentType))
                return;

            if (!contentType.Contains("application/json"))
                return;

            string paramValue;

            using (var reader = new StreamReader(filterContext.HttpContext.Request.InputStream))
                paramValue = reader.ReadToEnd();

            var serializer = new JavaScriptSerializer();
            var rawResult = (IDictionary<string, object>)serializer.DeserializeObject(paramValue);

            var keys = new List<string>(filterContext.ActionParameters.Keys);

            foreach (var key in keys)
            {
                object value = null;

                if (rawResult.TryGetValue(key, out value))
                {
                    filterContext.ActionParameters[key] = value;
                }
            }
        }
    }

This will attempt to populate all parameters values from inputstream.

Upvotes: 0

GuyIncognito
GuyIncognito

Reputation: 1234

That is very odd, because the code on my machine works. What happens when you request the URL directly from a browser (without the jQuery .post() call) like so:

/myapp/Accounts/List?page=0&pageSize=10&keywords=asdf

That generates a GET request (instead of the POST request generated by the jQuery .post() method -- but the action method parameters should be populated nonetheless).

For the purposes of debugging, you might want to change your action method to something like:

public ActionResult List(int? page, int? pageSize, string keywords)
{
     return Content(String.Format("page = {0}, pageSize = {1}, keywords = {2}",
                    page, pageSize, keywords));
}

If that works, the next step would be to test your jQuery call with something like (changing the returned data format from JSON to text):

 $.post('/myapp/Accounts/List',
        { 'page' : 0, 'pageSize' : 10, 'keywords' : 'asdf' },
        function(result) {
           alert(result);
        },
        'text');

Everything above worked correctly for me, so unless I'm missing something... I'm puzzled why it didn't work for you? Where did I go wrong?

Upvotes: 0

feemurk
feemurk

Reputation: 87

Since the attribute specified above works, have you checked to compare the contents of the HttpContext.Request.Form collection against the MVC-Binder-Bound FormCollection object.? Seems like there's a difference between the two sets. Also, try other ways of accessing the FormCollection - Get(), GetValue() ... check AllKeys for the key you're interested in???

Also, with regards to the route-registering idea ...

If you don't have the route myApp/Accounts/List/{page}/{pageSize} registered and the pagesize is still coming through, it stands to reason that there isn't a need to register a myApp/Accounts/List/{page}/{pageSize}/{keywords} route either.

Upvotes: 0

Bill Barry
Bill Barry

Reputation: 3523

Using this attribute:

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
public class StringParamFilterAttribute:ActionFilterAttribute {
    public string Param { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext) {
        filterContext.ActionParameters[Param] = filterContext.HttpContext.Request[Param];
    }
}

I was able to put an attribute on the action in order to give this parameter to the method:

[StringParamFilter(Param="keywords")]
public ActionResult List(int? page, int? pageSize, string keywords) {...}

and now keywords has the desired value

Upvotes: 0

markt
markt

Reputation: 5156

If you are posting the values, you could just use a FormCollection..

public class AccountsController:Controller {
public ActionResult List(int? page, FormCollection collection) {

  string keywords=collection["keywords"];
  ....
}

}

Also, for the post, you could append the id to the url and put the other params in json. I also do not think it is necessary to put your property names in quotes in json..

$.post("/myapp/Accounts/List/"+page,
   {pageSize:10,keywords:"asdf"}, 
   updategrid,
   "json");

... function updategrid(result) {...}

... or as others suggested, you could create a route that has all the possible parameters ..

Upvotes: 0

CSharpAtl
CSharpAtl

Reputation: 7512

Dont you want to pass the values on the URL and let the routing of MVC take care of passing the values? Not pass as actual variables?

Upvotes: 0

Related Questions