Answered by:
Passing multiple or complex parameters to a GET handler

Question
-
User349337957 posted
I feel like I'm trying to do a really simple thing, but I can't figure out how to do it. Basically, I have a Web API controller that I am using to query data using complex and arbitrary data. For example, let's pretend my Controller is called YelpController and its intent is to allow the consumer to find restaurants. I would want to do something like this:
// here's my search criteria; these are all optional public class SearchCriteria { public int Latitude { get; set; } public int Longitude { get; set; } public string Cuisine { get; set; } public ExpenseCategory { get; set; } } public IEnumerable<Restaurant> GetByCriteria(SearchCriteria filters) { // do stuff return matches; }
I'd like to issue this via an ajax request, like this:
$.ajax( { url: "/api/Yelp/GetByCriteria", type: "GET", contentType: "application/json", data: JSON.stringify({ Latitude: 47.6097, Longitude: -122.3331 }), success: function (result) { alert(result.Result); } });
Clearly, this doesn't work since I can't send a "data" payload along with a GET request. I could do this with a POST, but that seems hacky.
Instead of using one wrapper class as a single input parameter, I could split it out and have 4 parameters to the method and pass them in via a query string (eg /api/Yelp/GetByCriteria?latitude=xxx&longitude=yyy), but that is REALLY brittle and would require my API to change every time I add a new parameter.
Also, I clearly cannot use OData/IQueryable; my datastore is NoSQL and, besides, the parameters I want to pass aren't even supported currently.
So, what's the PROPER solution for this scenario? This seems like a super common use case, but I can't find anyone else with the same problemFriday, May 25, 2012 12:58 AM
Answers
-
User401360897 posted
A custom filter will be help you here. You need to pass just a single json querystring in url, for example,
?json={"Latitude"%3A+47.6097%2C+"Longitude"%3A+-122.3331}
Edit: you can improve this method by doing,
public class BindJson : System.Web.Http.Filters.ActionFilterAttribute { Type _type; string _queryStringKey; public BindJson(Type type, string queryStringKey) { _type = type; _queryStringKey = queryStringKey; } public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { var json = actionContext.Request.RequestUri.ParseQueryString()[_queryStringKey]; var serializer = new JavaScriptSerializer(); actionContext.ActionArguments[_queryStringKey] = serializer.Deserialize(json, _type); } } public class YelpController : ApiController { [BindJson(typeof(SearchCriteria), "json")] public IEnumerable<Restaurant> GetByCriteria(SearchCriteria json) { // do stuff return new List<Restaurant>(); } }
You can construct this with JSON.stringify method. Here is a possible filter,
public class BindJson : System.Web.Http.Filters.ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { var json = actionContext.Request.RequestUri.ParseQueryString()["json"]; var serializer = new JavaScriptSerializer(); actionContext.ActionArguments["filters"] = serializer.Deserialize(json, typeof(SearchCriteria)); } } public class YelpController : ApiController { [BindJson] public IEnumerable<Restaurant> GetByCriteria(SearchCriteria filters) { // do stuff return new List<Restaurant>(); } }
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, May 25, 2012 3:37 AM
All replies
-
User401360897 posted
A custom filter will be help you here. You need to pass just a single json querystring in url, for example,
?json={"Latitude"%3A+47.6097%2C+"Longitude"%3A+-122.3331}
Edit: you can improve this method by doing,
public class BindJson : System.Web.Http.Filters.ActionFilterAttribute { Type _type; string _queryStringKey; public BindJson(Type type, string queryStringKey) { _type = type; _queryStringKey = queryStringKey; } public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { var json = actionContext.Request.RequestUri.ParseQueryString()[_queryStringKey]; var serializer = new JavaScriptSerializer(); actionContext.ActionArguments[_queryStringKey] = serializer.Deserialize(json, _type); } } public class YelpController : ApiController { [BindJson(typeof(SearchCriteria), "json")] public IEnumerable<Restaurant> GetByCriteria(SearchCriteria json) { // do stuff return new List<Restaurant>(); } }
You can construct this with JSON.stringify method. Here is a possible filter,
public class BindJson : System.Web.Http.Filters.ActionFilterAttribute { public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext) { var json = actionContext.Request.RequestUri.ParseQueryString()["json"]; var serializer = new JavaScriptSerializer(); actionContext.ActionArguments["filters"] = serializer.Deserialize(json, typeof(SearchCriteria)); } } public class YelpController : ApiController { [BindJson] public IEnumerable<Restaurant> GetByCriteria(SearchCriteria filters) { // do stuff return new List<Restaurant>(); } }
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, May 25, 2012 3:37 AM -
User349337957 posted
Excellent. Passing URL encoded stringified JSON in a query string is probably not the most elegant solution, but so far it seems to work. Thanks!
Friday, May 25, 2012 11:10 AM -
User-356739632 posted
Hi Thanks for the solution
Thursday, February 21, 2013 10:23 AM -
User1115704286 posted
Interesting question. I have the same dilemma - what's the best way to pass a json query object as a parameter.
However, cramming it in the URL doesn't seem quite right to me - isn't it better to just POST the thing, and return the result in the response body? That's also a bit non-standard as the response bodies of POST requests are not usually used to transfer data but as far as I'm aware it's supported.Thursday, October 31, 2013 3:39 PM -
User-1588480722 posted
Instead of using one wrapper class as a single input parameter, I could split it out and have 4 parameters to the method and pass them in via a query string (eg /api/Yelp/GetByCriteria?latitude=xxx&longitude=yyy), but that is REALLY brittle and would require my API to change every time I add a new parameter.
What is wrong with this? As I see, this is the CORRECT way to do. It is not brittle. Let me guess why you think it is brittle - you are assuming that you need to have multiple input parameters in the web api method. No!!! You can maintain the same complex object as the input parameter and add [FromUri] attributes to each of the members.
Sunday, November 3, 2013 11:53 PM