Bind to dynamic object from Query String in ASP.NET Web API - c#

I'd like to bind to a dynamic object from the request querystring in ASP.NET Web API. Whilst decorating the action parameter with [FromUri] works with a normal class, it does not appear to work with dynamic (the dynamic object is empty).
public dynamic Get(string id, [FromUri]dynamic criteria)
{
return Ok();
}
Note that this needs to work for GET requests so there is no body.

You might be interested in the GetQueryNameValuePairs extension method (docs).
While it doesn't bind the query parameters to a model, it does allow you to access query parameters in a dynamic way (which sounds like your ultimate goal) via a dictionary-like object.
Also, see this answer.
var dict = new Dictionary<string, string>();
var qnvp = this.Request.GetQueryNameValuePairs();
foreach (var pair in qnvp)
{
if (dict.ContainsKey(pair.Key) == false)
{
dict[pair.Key] = pair.Value;
}
}

No it can't work.
The [FormUri] attribute tries to bind the object properties to the query string properties by name.
A dynamic object has no properties so it can't bind.
You can create your own model binder to achieve this goal. I don't suggest for you to go that way, but it is possible.
The "problem" with dynamics in this case is that it is not compiler safe and you can get errors at runtime if the parameters you expect are not part of the request.

While Web API complains that Multiple actions were found that match the request when overriding the Get method with a single parameter, you can "trick" the default model binder into binding the model you want by adding another parameter.
public class House
{
public string Color { get; set; }
public double SquareFeet { get; set; }
public override string ToString()
{
return "Color: " + Color + ", Sq. Ft.:" + SquareFeet;
}
}
public class Car
{
public string Color { get; set; }
public double EngineSize { get; set; }
public override string ToString()
{
return "Color: " + Color + ", cc: " + EngineSize;
}
}
public class ValuesController : ApiController
{
public string Get([FromUri] bool house, [FromUri] House model)
{
return model.ToString();
}
public string Get([FromUri] bool car, [FromUri] Car model)
{
return model.ToString();
}
}
Using the above code, the following URLs produce the respective output:
~/api/values?house=true&color=white&squarefeet=1500
<string>Color: white, Sq. Ft.:1500</string>
~/api/values?car=true&color=black&enginesize=2500
<string>Color: black, cc: 2500</string>

Related

Refit - Use refit parameters (string) to pass as controller parameters

There is a way to use Refit to input parameters in a dynamic way?
I have this code in my Refit`s Interface:
[Get("/click?{parm}")]
Task<ApiResponse<TAny>> SaveClick(string parm);
the value of parm is:
"id=1234&x=567"
my route:
[HttpGet]
[Route("click")]
public void test ([FromQuery] string id)
{
Ok("Ok");
}
All Im getting is that the value from id parameter is null. The expected result would be a string with value "1234"
Any help :D?
https://github.com/reactiveui/refit#dynamic-querystring-parameters
You can either use a custom POCO class with the parameters you need, or you can use a Dictionary<string,string> to pass the values. The dictionary seems to best fit your usecase.
public class MyQueryParams
{
[AliasAs("order")]
public string SortOrder { get; set; }
public int Limit { get; set; }
public KindOptions Kind { get; set; }
}
public enum KindOptions
{
Foo,
[EnumMember(Value = "bar")]
Bar
}
[Get("/group/{id}/users")]
Task<List<User>> GroupList([AliasAs("id")] int groupId, MyQueryParams params);
[Get("/group/{id}/users")]
Task<List<User>> GroupListWithAttribute([AliasAs("id")] int groupId, [Query(".","search")] MyQueryParams params);
params.SortOrder = "desc";
params.Limit = 10;
params.Kind = KindOptions.Bar;
GroupList(4, params)
>>> "/group/4/users?order=desc&Limit=10&Kind=bar"
GroupListWithAttribute(4, params)
>>> "/group/4/users?search.order=desc&search.Limit=10&search.Kind=bar"
A similar behavior exists if using a Dictionary, but without the advantages of the AliasAs attributes and of course no intellisense and/or type safety.
edit: You can probably use HttpUtility.ParseQueryString to parse your string into a NameValueCollection, which is basically a Dictionary.

How to pass dictionary parameter to web-api method?

I've read all similar questions here, but i can't come up with solution. I'm trying to call web-api method:
[HttpGet]
public SearchViewModel Get(SearchTypes type, string text, [FromUri]Dictionary<string, string> toyParams)
{
//Code here
}
and i want to get last parameter from uri. I've tried
http://localhost:39101/#!/search/toys/fox?someParameter=123
and
http://localhost:39101/#!/search/toys/fox?toyParams[0].Key=someParameter&toyParams[0].Value=123
but toyParams Dictionary always empty.
Just found out it is implicitly answered at another question here.
The solution it points at redirects to Model Binding in ASP.NET Core.
Or, short answer, you just compose your request like so:
http://localhost:39101/#!/search/toys/fox?toyParams[someParameter1]=123&toyParams[someParameter2]=456
Even though its quite late but following method is available to return query parameters as a name/value pair -
this.Url.Request.GetQueryNameValuePairs()
I have following Url -
http://localhost:49980/api/MyRestAPI/postSomeData/?a=v&d=e
Following is my method, written in Web API 2 -
[HttpPost]
public IHttpActionResult SaveDataFromRestToDB(string dataKey) {
var parameters = this.Url.Request.GetQueryNameValuePairs();
//parameters returns KeyValuePair<string, string>[2], containing values passed in Url Query
}
Hope this helps!!
One Simple way, instead of dictionary:
//DTO
public class SearchDTO
{
public int MyProperty1 { get; set; }
public int MyProperty2 { get; set; }
public int MyProperty3 { get; set; }
}
Where MyProperty1, MyProperty2, MyProperty3 are the params based on which something has to be searched.
//GET Method
public string Get([FromUri]SearchDTO searchDTO)
{
return "search result";
}
Below is the Calling URL :
http://localhost:56880/api/values?myproperty1=1&myproperty2=2&myproperty3=3

ServiceStack support for conditionally omitting fields from a REST response on a per-call basis

<TL;DR>
At a minimum, I'm looking for a way to conditionally exclude certain properties on the resource from being included in the response on a per-call basis (See fields below).
Ideally, I'd like to implement a REST service with ServiceStack that supports all the major points below.
UPDATE
While I really like ServiceStack's approach in general and would prefer to use it if possible, if it isn't particularly well suited towards these ideas I'd rather not bend over backwards bastardizing it to make it work. If that's the case, can anyone point to another c# framework that might be more appropriate? I'm actively exploring other options myself, of course.
</TD;DR>
In this talk entitled Designing REST + JSON APIs, the presenter describes his strategy for Resource References (via href property on resources) in JSON. In addition to this, he describes two query parameters (fields and expand) for controlling what data is included the response of a call to a REST service. I've been trying without success to dig into the ServiceStack framework to achieve support for fields in particular but have thus far been unsuccessful. Is this currently possible in ServiceStack? Ideally the solution would be format agnostic and would therefore work across all of ServiceStack's supported output formats. I would imagine expand would follow the same strategy.
I'll describe these features here but I think the talk at the link does a better job of explaining them.
Lets say we have an Profiles resource with the following properties: givenName, surname, gender, and favColor. The Profiles resource also includes a list of social networks the user belongs to in the socialNetworks property.
href - (42:22 in video) Every resource includes a full link to it on the REST service. A call to GET /profiles/123 would return
{
"href":"https://host/profiles/123",
"givenName":"Bob",
"surname":"Smith",
"gender":"male",
"favColor":"red",
"socialNetworks": {
"href":"https://host/socialNetworkMemberships?profileId=123"
}
}
Notice that the socialNetworks property returns an object with just the href value populated. This keeps the response short and focused while also giving the end user enough information to make further requests if desired. The href property, used across the board in this manor, makes it easy (conceptually anyway) to reuse resource data structures as children of other resources.
fields - (55:44 in video) Query string parameter that instructs the server to only include the specified properties of the desired resource in the REST response.
A normal response from GET /profiles/123 would include all the properties of the resource as seen above. When the fields query param is included in the request, only the fields specified are returned. 'GET /propfiles/123?fields=surname,favColor' would return
{
"href":"https://host/profiles/123",
"surname":"Smith",
"favColor":"red"
}
expand - (45:53 in video) Query string parameter that instructs the server to flesh out the specified child resources in the result. Using our example, if you were to call GET /profiles/123?expand=socialNetworks you might receive something like
{
"href":"https://host/profiles/123",
"givenName":"Bob",
"surname":"Smith",
"gender":"male",
"favColor":"red",
"socialNetworks": {
"href":"https://host/socialNetworkMemberships?profileId=123",
"items": [
{
"href":"https://host/socialNetworkMemberships/abcde",
"siteName":"Facebook",
"profileUrl":"http://www.facebook.com/..."
},
...
]
}
}
So...in my opinion ServiceStack's best feature is that it makes sending, receiving and handling POCOs over HTTP super easy. How you set up the POCOs and what you do in between (within the 'Service') is up to you. Does SS have opinions? Yes. Do you have to agree with them? No. (But you probably should :))
I think expanding on something like below would get you close to how you want to handle your api. Probably not the best example of ServiceStack but the ServiceStack code/requirements are barely noticeable and don't get in your way (AppHost configure not shown). You could probably do something similar in other .NET Frameworks (MVC/Web API/etc) but, in my opinion, won't look as much like straight C#/.NET code as with ServiceStack.
Request classes
[Route("/Profiles/{Id}")]
public class Profiles
{
public int? Id { get; set; }
}
[Route("/SocialNetworks/{Id}")]
public class SocialNetworks
{
public int? Id { get; set; }
}
Base Response class
public class BaseResponse
{
protected virtual string hrefPath
{
get { return ""; }
}
public string Id { get; set; }
public string href { get { return hrefPath + Id; } }
}
Classes from example
public class Profile : BaseResponse
{
protected override string hrefPath { get { return "https://host/profiles/"; } }
public string GivenName { get; set; }
public string SurName { get; set; }
public string Gender { get; set; }
public string FavColor { get; set; }
public List<BaseResponse> SocialNetworks { get; set; }
}
public class SocialNetwork: BaseResponse
{
protected override string hrefPath { get { return "https://host/socialNetworkMemberships?profileId="; }}
public string SiteName { get; set; }
public string ProfileUrl { get; set; }
}
Services
public class ProfileService : Service
{
public object Get(Profiles request)
{
var testProfile = new Profile { Id= "123", GivenName = "Bob", SurName = "Smith", Gender = "Male", FavColor = "Red",
SocialNetworks = new List<BaseResponse>
{
new SocialNetwork { Id = "abcde", SiteName = "Facebook", ProfileUrl = "http://www.facebook.com/"}
}
};
if (!String.IsNullOrEmpty(this.Request.QueryString.Get("fields")) || !String.IsNullOrEmpty(this.Request.QueryString.Get("expand")))
return ServiceHelper.BuildResponseObject<Profile>(testProfile, this.Request.QueryString);
return testProfile;
}
}
public class SocialNetworkService : Service
{
public object Get(SocialNetworks request)
{
var testSocialNetwork = new SocialNetwork
{
Id = "abcde",
SiteName = "Facebook",
ProfileUrl = "http://www.facebook.com/"
};
if (!String.IsNullOrEmpty(this.Request.QueryString.Get("fields")) || !String.IsNullOrEmpty(this.Request.QueryString.Get("expand")))
return ServiceHelper.BuildResponseObject<SocialNetwork>(testSocialNetwork, this.Request.QueryString);
return testSocialNetwork;
}
}
Reflection Helper Class
public static class ServiceHelper
{
public static object BuildResponseObject<T>(T typedObject, NameValueCollection queryString) where T: BaseResponse
{
var newObject = new ExpandoObject() as IDictionary<string, object>;
newObject.Add("href", typedObject.href);
if (!String.IsNullOrEmpty(queryString.Get("fields")))
{
foreach (var propertyName in queryString.Get("fields").Split(',').ToList())
{
//could check for 'socialNetwork' and exclude if you wanted
newObject.Add(propertyName, typedObject.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(typedObject, null));
}
}
if (!String.IsNullOrEmpty(queryString.Get("expand")))
{
foreach (var propertyName in queryString.Get("expand").Split(',').ToList())
{
newObject.Add(propertyName, typedObject.GetType().GetProperty(propertyName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance).GetValue(typedObject, null));
}
}
return newObject;
}
}
Usually you can control the serialization of your DTOs by setting the DataMember attributes. With those attributes you can control if the property should have defaults or not.
Meaning if you simply do not define the property of the object you want to return, it should not be serialized and therefore will not be shown in the resulting Json.
ServiceStack internally uses the standard DataContract...Serializer, so this should be supported
Otherwise you could also make use of dynamic objects and simply compose your object at runtime, serialize it and send it back.
Here is a very very basic example:
var seri = JsonSerializer.Create(new JsonSerializerSettings() { });
using (var textWriter = new StringWriter())
{
var writer = new JsonTextWriter(textWriter);
dynamic item = new { Id = id };
seri.Serialize(writer, item);
return textWriter.ToString();
}

Deserialize a JavaScript object as Dictionary<string, string> via ModelBinding in Web API

I have a simple JavaScript string and object:
var name = "Scarlett Johansson";
var args = { arg1: "foo", arg2: "bar" };
And I want to pass them via $.ajax to a Web API controller:
public string Get([FromUri]TestClass input) {
// would like Args model-bound
}
And my TestClass is:
public class TestClass
{
public string Name { get; set; }
public Dictionary<string, string> Args { get; set; }
}
The Name property is bound as expected, but I haven't found a way to bind Args. I've tried JSON.stringify(args), $.param(args), using a List<KeyValuePair<string,string>> on TestClass instead of Dictionary, nothing has worked.
I was hoping I could achieve this via model binding instead of manually de-serializing the JSON. Is this possible?
Clarification: the number of keys/values would vary in "args" from call to call, hence my need for a Dictionary.
the default model binding wont work like that, it attempts to bind to public properties on objects. in this example, you would need a class containing like :
public class ArgClass
{
public string Arg1 { get; set; }
public string Arg2 { get; set; }
}
public class TestClass
{
public string Name { get; set; }
public List<ArgClass> Args { get; set; }
}
the alternative, which seems like you would want to do, is write a custom model binder, or a quick google search turns up this DefaultDictionaryBinder someone seems to have implemented already
https://github.com/loune/MVCStuff/blob/master/Extensions/DefaultDictionaryBinder.cs
Update: just realized you are using web api, which is i guess slightly different. Here's a blog post explaining how the binding works for web api: http://blogs.msdn.com/b/jmstall/archive/2012/04/16/how-webapi-does-parameter-binding.aspx
Let's extend your method with implementation (to see the result of what we've passed) like this:
public HttpResponseMessage Get([FromUri]TestClass input)
{
return Request.CreateResponse<TestClass>(HttpStatusCode.OK, input);
}
And if we would like to see this:
{
"Name":"MyName",
"Args":
{
"FirstKey":"FirstValue",
"SecondKey":"SecondValue"
}
}
Other words the testClass.Name == "MyName" and testClass.Args["FirstKey"] == "FirstValue"... we can call the API like this:
api/MyService/?name=MyName&args[0].key=FirstKey&args[0].value=FirstValue&args[1].key=SecondKey&args[1].value=SecondValue
The params on separated lines, just for clarity (URI will be without line breaks!):
api/MyService/
?name=MyName
&args[0].key=FirstKey
&args[0].value=FirstValue
&args[1].key=SecondKey
&args[1].value=SecondValue

JSON.Net in Api controllers w/ param containing Dictionary is always null

I've seen some tutorials out there that claim to work, but they are outdated or simply do not work.
How can I use JSON.Net to serialize and deserialize the data received to and sent from my API controllers?
We are using VS2012.
Update
I have a model like this
public class SearchModel
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
And an Api controller like this
public class ModelSearchApiController : ApiController
{
public List<Model> Get([FromUri] SearchModel search)
{
return new List<Model>();
}
}
However, search provides the correct value set in the Ajax request, the property Terms is always an empty dictionary.
I know we can provide a value like [ { Key:"foo", Value:123 } ] but why can't I just pass a normal JSON object (ie { foo:123 }) ??? Why can it serialize a Dictionary into a nice standard JSON object, but cannot take that exact same object and recreate a Dictionary. This is beyound me.
Edit
In other words, if the browser sends these arguments :
pageIndex: 0
pageSize: 100
terms[foo]: Bar
terms[buz]: 1234
What would be the required object signature? Because the object mentionned above does not work and the dictionary is just empty.
JSON.NET is the default serializer for ASP.NET Web API - it can convert between JSON and CLR objects, and does so for all JSON input. However, you're not trying to convert a JSON input to your SearchModel - you're trying to convert from the URI-based format which is similar to application/x-www-form-urlencoded, into the CLR type SearchModel, and that is not supported by JSON.NET (it's not JSON!). In general, the serializers are used to convert (on incoming requests) from the request body to the action parameter.
Let's look at this (complete) example below (assuming the default route, to "api/{controller}"). It's very similar to your question, but I also added a Post method in addition to the GET method.
public class ModelSearchApiController : ApiController
{
public List<Model> Get([FromUri] SearchModel search)
{
return new List<Model>
{
new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
};
}
public List<Model> Post(SearchModel search)
{
return new List<Model>
{
new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
};
}
}
public class Model
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
public class SearchModel
{
public int PageIndex { get; set; }
public int PageSize { get; set; }
public Dictionary<string, object> Terms { get; set; }
}
If you send this request to the server:
POST http://localhost:64699/api/ModelSearchApi HTTP/1.1
User-Agent: Fiddler
Host: localhost:64699
Content-Type: application/json
Content-Length: 65
{"PageIndex":1,"PageSize":10,"Terms":{"foo":"bar","foo2":"bar2"}}
It will be bound, as you expect, to the SearchModel parameter - the Terms property will be a dictionary with two entries (foo=bar, foo2=bar2).
Now, for the GET parameter. ASP.NET Web API has a concept of model binders and value provider, which would be the component which would convert between the query string into the action parameters. The default binder / provider do not support the "arbitrary" name/value pair syntax *for dictionary inside complex types. You can, as you pointed out, use the key/value pair syntax, and that will be understood, as shown below.
GET http://localhost:64699/api/ModelSearchApi?PageIndex=1&PageSize=10&Terms[0][key]=foo&Terms[0][value]=bar HTTP/1.1
User-Agent: Fiddler
Host: localhost:64699
Now, for your problem you have two options. You can change your API to use a custom model binder or value provider which knows how to understand the "simple" name/value syntax, as shown below:
public class ModelSearchApiController : ApiController
{
public List<Model> Get([ModelBinder(typeof(MySearchModelBinder))] SearchModel search)
{
return new List<Model>
{
new Model { PageIndex = search.PageIndex, PageSize = search.PageSize, Terms = search.Terms }
};
}
}
public class MySearchModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
{
SearchModel value = new SearchModel();
value.Terms = new Dictionary<string,object>();
foreach (var queryParams in actionContext.Request.GetQueryNameValuePairs())
{
if (queryParams.Key == "PageIndex")
{
value.PageIndex = int.Parse(queryParams.Value);
}
else if (queryParams.Key == "PageSize")
{
value.PageSize = int.Parse(queryParams.Value);
}
else if (queryParams.Key.StartsWith("Terms."))
{
value.Terms.Add(queryParams.Key.Substring("Terms.".Length), queryParams.Value);
}
}
bindingContext.Model = value;
return true;
}
}
Another option is to pre-process your input data on the client prior to sending to the server, using a function similar to the one below.
function objToKVPArray(obj) {
var result = [];
var k;
for (k in obj) {
if (obj.hasOwnProperty(k)) {
result.push({ key: k, value: obj[k] });
}
}
return result;
}
You can take reference the link below. Hope this help.
And here is sample using Json.net with web API.

Categories

Resources