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.
Related
I'm making a (restful) Web API in .NET Core and stumbled among some problems.
I cannot seem to find how to pass multiple subscription ID's... I need to be able to show multiple periods(invoices) of multiple subscriptions.
My route at the moment is
[Route("tenants/{tenantId:long}/subscriptions/{subscriptionId:long}/invoices/{invoiceId:long}/categories")]
From this way it seems impossible for me to pass more subscription IDs.
Some terms I found but not fully understand are:
Model Binding
[FromQuery]
My classes:
public class Subscription
{
public string Name { get; set; }
public long TenantId { get; set; }
public string Guid { get; set; }
}
public class Invoice
{
public long SubscriptionId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public long PortalId { get; set; }
}
My controllers with routes [Route("tenants/{tenantId:long}/subscriptions")] and [Route("tenants/{tenantId:long}/subscriptions/{subscriptionId:long}/invoices")]
[HttpGet]
public IEnumerable<SubscriptionViewModel> Find(long tenantId)
{
var subscriptionList = _subscriptionManager.Find(tenantId);
...
return subscriptionViewModels;
}
[HttpGet]
public IEnumerable<InvoiceViewModel> Find(long subscriptionId)
{
var invoiceList = _invoiceManager.Find(subscriptionId);
...
return invoiceViewModels;
}
Please note that i'm using a Mapper for my data (which is why i'm using ViewModels).
The currently written code is for a specific subscription.
I am looking for a Route like /api/invoices?subscriptionId=x,y,z
I understand(?) I need the [FromQuery] for that, but I cannot seem to find out how, especially if my parameter (subscriptionId) stays the same.
for the requirement which you have mentioned as:
I am looking for a Route like /api/invoices?subscriptionId=x,y,z
You can do couple of things:
pass the subscriptionIds one after the other separated by & in the query string of the URL and change the input parameter of action method to accept array of subscriptionIds
example of route:
/api/invoices/find?subscriptionId=x&subscriptionId=y&subscriptionId=z
example of action method parameter accepting array of subscriptionIds:
public IEnumerable<InvoiceViewModel> Find([FromQuery]long[] subscriptionId)
pass the comma separated string as querystring in the URL and write a piece of logic in the action method to split the string based on comma to get an array of subscriptionIds
example of route:
/api/invoices/find?subscriptionIds=x,y,z
example of action method:
public IEnumerable<InvoiceViewModel> Find([FromQuery]string subscriptionIds)
{
var ids = subscriptionIds.Split(',').Select(int.Parse).ToArray();
// do the logic on multiple subscriptionIds
}
Apart from this, you can go for creating custom model binders as well as suggested in other answers.
Hope this helps.
There can be many ways to achieve this task (I can think of two-three for now).
1) instead of long subscriptionid take a string as an input and validate it before proceeding further.
[HttpGet]
public IEnumerable<InvoiceViewModel> Find(string subscriptionIds)
{
var list = validateInput(subscriptionIds);
var invoiceList = _invoiceManager.FindList(list);
...
return invoiceViewModels;
}
public IList<long> validateInput(string subscriptionIds)
{
var list = subscriptionIds.Split(",");
... // Code to convert each element in long and throw if it is not long
return longlist;
}
2) Create custom model binders.
Steps are mentioned here:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
=> [FromUri] attribute can be used to bind the Complex types from query string parameters but i am not sure how i would use that.
If you ask me, i would go for approach-1 (not to increase complexity).
You can create a specific Request view model which accepts a collection of invoice ids:
public class InvoiceRequestModel
{
IEnumerable<long> InvoiceIDS { get; set; }
}
and use it for your action method:
[Route("tenants/{tenantId:long}/subscriptions/{subscriptionId:long}/invoices")]
[HttpGet]
public IEnumerable<InvoiceViewModel> Get(InvoiceRequestModel requestModel)
{
}
In the case you want to use query parameters, mark your action parameter with the [FromQuery] attribute:
[Route("tenants/{tenantId:long}/subscriptions/{subscriptionId:long}/invoices")]
[HttpGet]
public IEnumerable<InvoiceViewModel> Get([FromQuery]IEnumerable<long> invoiceIDs)
{
}
and on creating the request, pass each value with the same key in the query string:
invoiceIDs=1&invoiceIDs=2&invoiceIDs=3
Finally, it will look like this:
tenants/{tenantId}/subscriptions/{subscriptionId}/invoices?invoiceIDs=1&invoiceIDs=2&invoiceIDs=3
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
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>
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
What's the URL syntax for passing an object with a nested object to my ASP.NET Web API GET method? Is this possible?
http://mydomain/mycontroller?...
Mycontroller GET method:
public void Get([FromUri]MyType myType) { ... }
C# types:
public class MyType
{
public string Name { get; set; }
public NestedType Foo { get; set; }
}
public class NestedType
{
public int Bar { get; set; }
}
It is possible -- try passing the URL in this format:
?myType.Foo.Bar=3&myType.Name=Maggie
If you're trying to implement a get that performs the following:
1) get by name
2) get by Foo.Bar
Then you could use querystring parameters.
REST pass multiple inputs to GET method
If you are not really trying to do a GET and instead you are trying to POST data to the server, then you should use a POST.