How to build query string for Web API? - c#

Assume that I have Web API method:
[HttpGet]
public string SomeAction([FromUri] ObjectA a, [FromUri] ObjectB b)
{
return Ok("test");
}
How to call it using HttpClient? What is the best way to prepare appropriate query string for objects A and B?
UPDATE
ObjectA and ObjectB are complex objects... e.g.
public class ObjectA
{
public string Prop1 { get; set; }
public int Prop2 { get; set; }
public double Prop3 { get; set; }
}
I can prepare query string for every specific case, but I'm interested to get any universal method that would allow to work with any objects...
For now I see only one possible solution - using reflection go through the list of properties and build query string. I assume that there should be some already implemented mechanism... am I wrong?

If you're including complex objects on the query string, I will assume they are being encoded in some manner, from what you're trying to do, it would have to be a default encoding or you won't be able to put them on the query string.
Normal routing would be:
/{controller}/{action}/{a}/{b}
or
/{controller}/{action}?a={content}&b={content}
Dependent upon how you have your routes configured and your preference on using query string parameters as opposed to "friendly" urls.
Alternately, the MVC framework will attempt to populate your object from values present in any available data location (i.e. query string, cookies, et. al.) where variable names = property names. In this case, you could use something like this:
/SomeController/SomeAction?objectaProp1=abc&objectaProp2=def...
Hope this helps.

Related

Adding conditions on complex properties with ServiceStack AutoQuery

I need to add filtering to my API requests that support AutoQuery, so based on this SO answer, used q.And to add conditions. The issue is that one of the POCO properties is a List<string> and it seems doing a simple Contains() won't work. Here's a simple example of what I have:
public class PocoObject
{
public int Id { get; set; }
public List<string> Names { get; set; }
}
My service looks like this:
public object Get(PocoObjects request)
{
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
if (someCondition)
{
q.And(x => x.Names.Contains(request.TargetName));
}
return AutoQuery.Execute(request, q);
}
Problem is, I get an error like this:
variable 'x' of type 'TestProject.ServiceModel.Types.PocoObject' referenced from scope '', but it is not defined
If I change the Contains to a simpler equality comparison on another property, the AutoQuery works. Any ideas how to accomplish this?
You can't do a server side SQL query on a blobbed complex type property like List<string>. Any queries need to be applied on the client after the results are returned from the db and its deserialised back into a typed POCO.

WebApi complex type uri/ get multiple parameters

I’m developing a WebApi rest revice solution and would like to pass multiple complex type as url parameters.
I need to pass two objects of personal data. Personal data contains FullName and Id.
public class Foo
{
public string FullName { get; set; }
public string Id { get; set; }
}
I was expecting that I could call my REST service with url similar to this:
http://localhost:53088/values/GetByFoo/?foo1={"FullName":"Name 1","Id":"1"}&foo2={"FullName":"Name 2","Id":"2"}
So I’ve coded ValuesController.cs like this:
[HttpGet]
public List<Foo> GetByFoo2([FromUri] Foo foo1, [FromUri] Foo foo2)
{
List<Foo> foos = new List<Foo>();
foos.Add(foo1);
foos.Add(foo2);
return foos;
}
Unfortunately parameters in [HttpGet] methods are always NULL
As it was not working I’ve tried to start with one parameter and this is what I have achieved so far:
ValuesController.cs
[HttpGet]
public Foo GetByFoo([FromUri] Foo foo)
{
return foo;
}
I could only get right values in this case:
/Values/GetByFoo?FullName=NAME1&Id=1
In this case foo parameter is always NULL
/values/GetByFoo/?foo={"FullName":"Name 1","Id":"1"}
Then I’ve thought of using one parameter, containing both foo1 and foo2 properties and make this get request:
/values/GetByFooPair/?foopair={"Foo1":{"FullName":"Name 1","Id":"1"},"Foo2":{"FullName":"Name 2","Id":"2"}}
public class FooPair
{
public Foo Foo1 { get; set; }
public Foo Foo2 { get; set; }
}
But it’s not working either, controller method parameter are always null.
By the other hand , if I use HttpPost method and make an ajax request using jQuery, parameters are correct and contains expected values.
[HttpPost]
public FooPair GetByFooPairPost([FromBody] FooPair foopair)
{
return foopair;
}
Is it possible to pass several complex type parameters through url?
What am I missing? What am I doing wrong?
You need to change it to a [HttpPost] if you want to handle complex objects.
You can do it like this:
http://localhost:53088/values/GetByFoo?foo1.fullName=Fullname1&foo1.id=ID1&foo2.fullName=Fullname2&foo2.id=ID2
Tested on WebAPI 2.2 (nuget package version 5.2.3) in .NET framework 4.5.2.
However, just as Shyju and codeMonkey have pointed out, I would not recommend this if the objects have any bit of complexity. HttpPost accepts any amount of content in the body, whereas the querystring length is limited. Also you'll get automatic serialization to a correct JSON format from Javascript when using the body, as in comparison here, you'll have to do the serialization yourself.

ResponstDTO with complex Property in ServiceStack

Havin a Response with a complex property, i want to to map to my responseDTO properly. For all basic types it works out flawlessly.
The ResponseDTO looks like this:
public class ResponseDto
{
public string Id {
get;
set;
}
public struct Refs
{
public Genre GenreDto {
get;
set;
}
public Location LocationDto {
get;
set;
}
}
public Refs References {
get;
set;
}
}
Genre and Location are both for now simple classes with simple properties (int/string)
public class GenreDto {
public string Id {
get;
set;
}
public string Name {
get;
set;
}
}
Question:
Is there any way, without changing/replacing the generic unserializer ( and more specific example) (in this example JSON ) to map such complex properties?
One specific difference to the GithubResponse example is, that i cant use a dictionry of one type, since i have different types under references. Thats why i use a struct, but this seems not to work. Maybe only IEnumerable are allowed?
Update
There is a way using lamda expressins to parse the json manually github.com/ServiceStack/ServiceStack.Text/blob/master/tests/ServiceStack.Text.Tests/UseCases/CentroidTests.cs#L136 but i would really like to avoid this, since the ResponseDTO becomes kinda useless this way - since when writing this kind of manual mapping i would no longer us Automapper to map from ResponseDto to DomainModel - i though like this abstraction and "seperation".
Thanks
I used lambda expressions to solve this issue, a more complex example would be
static public Func<JsonObject,Cart> fromJson = cart => new Cart(new CartDto {
Id = cart.Get<string>("id"),
SelectedDeliveryId = cart.Get<string>("selectedDeliveryId"),
SelectedPaymentId = cart.Get<string>("selectedPaymentId"),
Amount = cart.Get<float>("selectedPaymentId"),
AddressBilling = cart.Object("references").ArrayObjects("address_billing").FirstOrDefault().ConvertTo(AddressDto.fromJson),
AddressDelivery = cart.Object("references").ArrayObjects("address_delivery").FirstOrDefault().ConvertTo(AddressDto.fromJson),
AvailableShippingTypes = cart.Object("references").ArrayObjects("delivery").ConvertAll(ShippingTypeDto.fromJson),
AvailablePaypmentTypes = cart.Object("references").ArrayObjects("payment").ConvertAll(PaymentOptionDto.fromJson),
Tickets = cart.Object("references").ArrayObjects("ticket").ConvertAll(TicketDto.fromJson)
});
So this lamda exprpession is used to parse the JsonObject response of the request and map everything inside, even nested ressources. This works out very well and flexible
Some time ago i stumbled upon a similar problem. Actually ServiceStack works well with complex properties. The problem in my scenario was that i was fetching data from a database and was passing the objects returned from the DB provider directly to ServiceStack. The solution was to either create DTOs out of the models returned by the DB provider or invoke .ToList() on those same models.
I'm just sharing some experience with SS but may be you can specify what's not working for you. Is there an exception thrown or something else.

How to create a single drop down for multiple data types?

I am using ASP.Net MVC 3 and I need to create a single drop down list which contains items that relate to multiple database tables.
Normally, if I need to do a drop down list for a single data type I can easily use the ID as the "value" for each drop down option and would do something like this:
#Html.DropDownListFor(x => x.SelectedID, Model.GetMyList())
But now I want to mix up multiple data types. So lets say for this example I want to create a single list to represent something like "Owner" and this can be either a "User" or a "Customer". In this example, both User and Customer are separate database tables and therefore the ID value alone is not enough to identify them correctly.
So what are the best ways to achieve such functionality?
Straight off the top of my head, my first thoughts are to create a "custom" value string which could then be parsed server side to work out the ID and data type, something like...
"USER|1"
"CUSTOMER|1"
I know I can make this work, but am I making this more complicated than it needs to be? Is there a built-in or advised way of doing this?
In your Model can you not do something like this:-
public class Model
{
public string Owner { get; set; }
public List<MyList> ListCollection { get; set; }
public class MyList
{
public int Id { get; set; }
public string Value { get; set; }
}
}
So then when you are checking which list item is selected you also have access to the "Owner" field which will tell you what table it belongs to ?
As nobody has come up with anything better, I can confirm that my original idea (as unwanted as it was) did the job.
When setting the value of the select options, a custom string should be created that can easily be parsed server side, this was achieved using a pipe separating the TYPE of entity, and the ID, for example:
"USER|1"
"USER|2"
"CUSTOMER|1"
"CUSTOMER|2"
Once the selected value is passed to the server, it can then be parsed something like the following:
string option = "USER|1";
string[] values = option.Split('|');
string entityType = values[0];
int entityId = Int.Parse(values[1]);
which can then be used something like this:
if(entityType == "USER")
UpdateUser(entityId);
else//CUSTOMER
UpdateCustomer(entityId);

Orderby complex type using OData

I am starting to use OData in my MVC4 application and the problem that I am having, is that I cannot perform any sort or filter operations on my IQueryable because I am using complex objects. Below is a simple example of something I am trying to accomplish:
My API Controller is attempting to return a collection of MyObjects as IQueryable.
public IQueryable Get()
{
List<MyObject> myObjects = GetMyObjects();
return myObjects.AsQueryable() ;
}
Each MyObject contains an InnerObject that has the properties I want to sort and/or filter on.
public class MyObject
{
[Key]
public MyInnerObject innerObject{ get; set; }
public MyObject(Dictionary<string, object> value)
{
innerObject= new MyInnerObject(){
item = value["item"].ToString(),
itmdesc = value["itmdesc"].ToString()
};
}
}
public class MyInnerObject
{
public string item { get; set; }
public string itmdesc { get; set; }
}
I can successfully execute the top commands through the url
localhost:5050/Test/Get?$top=10
But I really want to be able to sort my results using
localhost:5050/Test/Get?$top=10&$orderby=innerObject.item
I have tried
localhost:5050/Test/Get?$top=10&$orderby=innerObject.item
localhost:5050/Test/Get?$top=10&$orderby=innerObject/item
localhost:5050/Test/Get?$top=10&$orderby=item
Any suggestions?
EDIT:
I should mention that it works if I put the item and itmdesc properties within MyObject, but for my purposes (this is just a minified version of my complex entities), they will need to be wrapped in a complex type.
In my Api controller, I have also tried IQueryable< MyObject> but that doesnt make a difference
In general, OData as a protocol allows the second thing you tried (localhost:5050/Test/Get?$top=10&$orderby=innerObject/item). It is likely that this is a temporary limitation of the Web API implementation of OData (assuming that's what you're using based on the rest of your environment and the returning of IQueryable).
Aspnet Web API OData doesn't support ordering by nested properties or expressions. There is an issue open on codeplex for supporting ordering by nested properties.
However if you are slightly adventurous you can use ODataQueryOptions to model bind the individual odata query options and then translate $orderby AST to a linq expression and apply it manually to your IQueryable.

Categories

Resources