How to pass dictionary parameter to web-api method? - c#

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

Related

Is it possible to use polymorphism in and ASP.NET MVC [FromBody] controller method parameter?

The question title might be a bit difficult to understand, but I'll try to explain what I am asking here.
I have this sort of controller method in an ASP.NET MVC controller:
public IActionResult DoStuff([FromBody] RequestObject reqObj)
{
// do stuff
ReplyObject repObj = ProcessObject(reqObj);
// do more stuff
return Json(repObj);
}
where RequestObject looks like this:
public class RequestObject
{
public string Field { get; set; }
}
I am calling it from an Angular service like this:
this.http.post("/StuffController/DoStuff", { field: "field value goes here" })
Now this works OK. But now I need DoStuff() to handle different subclasses of RequestObj, for example:
public class RequestObjectA : RequestObject
{
public string FieldA { get; set; }
}
public class RequestObjectB : RequestObject
{
public string FieldB { get; set; }
}
and have DoStuff() somehow figure out whether it was given a RequestObjectA or a RequestObjectB.
Is this kind of thing possible? I have a feeling it is not as the JSON request transmits only the contents of the object, not its class, but I'd like to know for sure.
The short answer is that it is not possible.
The long answer is: your contract for the endpoint parameter is RequestObject class so nor additional parameter members will be respected during deserialization as nothing is known about them.

How to pass multiple parameters to Get Method and Route them in .NET Core Web API?

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

return decimal value in web api

I have following method in repository project , and I'm trying to get that value via web api,
Method
public decimal findBookPrice(int book_id)
{
var bookprice = (
from r in context.Books
where r.Book_Id == book_id
select r.Price
).FirstOrDefault();
return bookprice;
}
Book Class
public class Book
{
[Key]
public int Book_Id { get; set; }
[Required]
public string Book_Title { get; set; }
[DataType("decimal(16 ,3")]
public decimal Price { get; set; }
...
}
}
Web API method
// GET: api/BookPrice/3
[ResponseType(typeof(decimal))]
public IHttpActionResult GetBooksPriceById(int id)
{
decimal bookprice = db.findBookPrice(id);
return Ok(bookprice);
}
but once I direct to url which is http://localhost:13793/api/BookPrice/2
I'm getting following output not the decimal value
The shown error message is caused by a routing problem. The ASP.NET MVC framework was not able to find the right controller or action for the URL
http://localhost:13793/api/BookPrice/2
The default routing rule in ASP.NET MVC takes BookPriceand tries to find the BookPriceController. As you stated in your comment, the action is in a BooksWithAuthersController. Therefore the URL has to be (if you want to use the default routing rule):
http://localhost:13793/api/BooksWithAuthers/2
Have a look at article if you want to read more about this topic.
EDIT:
Looking at the whole controller code you will find the two action methods called GetBooksWithAuthersById and GetBooksPriceById. Because both start with get and have got the same parameter list (int id), the ASP.NET MVC framework has got two possible action methods for the URL /api/BooksWithAuthors/2. To solve this ambiguity you can give the GetBooksPriceById action a separate route via the [Route] annotation.
Like in this slightly adjusted BooksWithAuthersController:
public class BooksWithAuthersController : ApiController
{
[ResponseType(typeof(BookWithAuther))]
public IHttpActionResult GetBooksWithAuthersById(int id)
{
...
}
[ResponseType(typeof(decimal))]
[Route("api/bookswithauthers/{id}/price")]
public IHttpActionResult GetBooksPriceById(int id)
{
...
}
}
In order get the price of a book, the URL http://localhost:13793/api/BooksWithAuthers/2/price will return the decimal value.

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

What's the REST URL syntax for passing a nested complex type?

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.

Categories

Resources