I'm having issues with DataMember-naming in UriParameters.
[DataContract]
public class testobj
{
[DataMember(Name = "Test")]
public string a {get; set; }
[DataMember(Name = "Test1")]
public string b {get; set; }
}
Then i have my controller:
public IHttpActionResult test([FromUri] testobj testparams)
{
return testparams;
}
In the response i get Test and Test1, that's correct. However i have to use a and b in the uriParameters, why can't i use Test and Test1 there?
How can i fix this?
You cannot pass an object type in the Query String, You have to pass it in the HTTP Packets Body, and use [FromBody] in your Controller methods Parameter.
here's a Stackoverflow link explaining more,
ASP.NET Web APi - Passing an object as parameter
Thank You,
Bhadhri
Related
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.
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
This is in OWIN & .Net 4.5.2
Using debug I'm proving this controller's method is being called by the web request.
My thing is the request body contains a JSON stringified object:
"{ 'id':'12', 'text1':'hello', 'test2':'world' }"
Which is applicably encoded as it is transferred on the line.
I've tried so many things I'm so confused now.
How do I get the decoded string so I can JSON.Parse() that or better yet get .Net to just given me an object?
In one version (long ago now) I had a defined type for this object. If I need that great, not a high challenge. But if I only have the JSON object from the string that's fine too.
public class cController : ApiController {
[HttpPut]
public string put(string id) {
var bdy = this.Request.Content;
//Console.WriteLine("PUT containers {0}", body);
return string.Empty;
}
}
In case it helps, the bdy.ContentReadStream is null. I don't know if this is good, bad, or important. Maybe Request.Content isn't the way to go but seems to me like if I'm going to read the body as a stream then it shouldn't be null.
I also tried working through System.Web.HttpContext. If that is somehow the answer I have no problem going back to that. But I couldn't find the secret sauce.
Pass the desired model as a parameter to the action and the frame work should be able to parse it provided it is valid JSON
public class cController : ApiController {
[HttpPut]
public IHttpActionResult Put(string id,[FromBody] Model body) {
if(ModelState.IsValue) {
return Ok(body.text1);
}
return BadRequest();
}
}
Where Model is defined as
public class Model {
public string id { get; set; }
public string text1 { get; set; }
public string test2 { get; set; }
}
We are creating RestService with Asp.Net WebApi. But for some reason Name property is ignored in DataMember attribute when trying to deserialize complex property with [FromURI] attribute.
For example we might have:
Method:
public IHttpActionResult Get([FromUri]User user)
Model:
[DataContract]
public class User
{
[DataMember(Name = "username")]
public string Username{ get; set; }
[DataMember(Name = "isActive", IsRequired = false)]
public bool? Active { get; set; }
}
When deserializing user we get username as expected, but null for Active. On the other hand when serializing data we get both isActive and username as expected. If we send request with active in query string it works as expected.
It's obviously problem with IModelBinder. It doesn't use DataMember's Name property for some reason. I checked what formaters are included and 4 default ones are registered:
System.Net.Http.Formatting.JsonMediaTypeFormatter
System.Net.Http.Formatting.XmlMediaTypeFormatter
System.Net.Http.Formatting.FormUrlEncodedMediaTypeFormatter
System.Net.Http.Formatting.JQueryMvcFormUrlEncodedFormatter
I don't have a way to check which one is used on request. I would assume that its FormUrlEncodedMediaTypeFormatter but I can't be sure. Also, I am not sure if it even supports Name property.
I already checked for a solution and closest topic I could find was WebAPI DataMember Name not used when de/serializing via application/x-www-form-urlencoded but it doesn't use [FromUri] but application/x-www-form-urlencoded property and it wasn't really solved.
Any ideas, pointers or suggestions would be much appreciated.
Use [FromQuery] instead other attributes.
And model for your request
http://localhost:8080/api/users?username=John&isActive=true
[Route("api/users")]
public class UsersController : Controller
{
[HttpGet]
public IHttpActionResult Get(User user)
{
//...
}
}
Will looks like
public class User
{
[FromQuery(Name = "username")]
public string Username{ get; set; }
[FromQuery(Name = "isActive")]
public bool? Active { get; set; }
}
Anyway best practice is to keep names in model as it parameters names in query.
In this case you dont have to provide "Name" parameter, only keep [FromQuery] on queryClass, and lower casing .Net provide automaticly.
You must check your "get" request. Your get request must be like this;
GET api/foo?username=fooname&active=false
You don't have to DataContract and DataMember attribute to just achieve this. These attribute just for another thing, its not the main reason to use for.
After get valid hit on your get method, in your method you can check modelstate like;
if (ModelState.IsValid) {
/// your code goes here
}
Being able to define the routing like so:
Route("Calculator/AddTwoNumbers/a/{a}/b/{a}")]
public IHttpActionResult AddTwoNumbers(double a, double b)
is great to invoke an endpoint like this:
http://localhost:64466/Calculator/AddTwoNumbers/a/1/b/2
I am curious what to do in scenarios where the payload/data transport object (DTO) becomes more complex (i.e. hierarchical). Take above's excample, this may be a corresponding DTO:
public class PayLoad
{
public double a { get; set; }
public double b { get; set; }
}
something like this:
Route("Calculator/AddTwoNumbers/a/{a}/b/{a}")]
public IHttpActionResult AddTwoNumbers(PayLoad payLoad)
does not work as there does not seem to be 'model binding' (?) as in asp.net mvc.
What am I suppose to do if the DTOs are more complex?
You need to decorate your payLoad parameter definition with the [FromUri] attribute:
public IHttpActionResult AddTwoNumbers([FromUri] PayLoad payLoad)
See MSDN
I think there are 2 ways to solve get data with WepApi:
1- Using [FromUri]:
To force Web API to read a complex type from the URI, add the [FromUri] attribute to the parameter. The following example defines a GeoPoint type, along with a controller method that gets the GeoPoint from the URI.
public class GeoPoint
{
public double Latitude { get; set; }
public double Longitude { get; set; }
}
public ValuesController : ApiController
{
public HttpResponseMessage Get([FromUri] GeoPoint location) { ... }
}
The client can put the Latitude and Longitude values in the query string and Web API will use them to construct a GeoPoint. For example:
http://localhost/api/values/?Latitude=47.678558&Longitude=-122.130989
2- Using [FromBody]:
To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter:
public HttpResponseMessage Post([FromBody] string name) { ... }
This code is explained here: Parameter Binding in ASP.NET Web API