Can I use an object in UriTemplate? - c#

I tried the following code:
[OperationContract]
[WebInvoke(UriTemplate="/Users/Register/{user}")]
void Register(User user);
But when I Try to run this, it tells me the UriTemplate must only contain strings. What if I need to pass in an object to my method as in this case, a User object to my Register method.
If I change the WebInvoke attribute to:
[WebInvoke(UriTemplate="/Users/Register/")]
The browswer displays the error Method not allow when I try to browse to http://localhost:8000/Users/Register for example

You are limited to strings in the UriTemplate. You could use multiple parameters to pass multiple strings but you cannot use a complex type. If you want to pass a complex type, then it cannot be anywhere in the URI but rather in the body of a POST/PUT request. The GET request does not take a message body. So your above code could be changed to this:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate="/Users/Register")]
void Register(User user);
Where you're passing the User object in, not via the Uri, but as part of the POST request.

It is exactly as it says. A URI is literally only capable of containing strings. They aren't made for objects. You could probably convert it to take a user name or user id instead if you needed... but you can never use a complex object type as a URI.

You don't pass objects around as query string parameters (how could you? would it serialize it somehow?) You should pass in the user id.

Related

Json object in post not recognized as object in web api

I have an angular client and want to execute a get request to my web api backend to get a list of items from the underlying Dapper Db Wrapper. Dapper allows me to pass in parameters as an anonymous object which would in csharp look like this:
connection.GetList<T>(new {myParam1:"a", myParam2: true});
What I want to achieve is, to create this parameter object in my angular frontend and pass it in a post request to the server which would then pass it on to the GetList function. The problem here is that the web api does not deserialize it as an (anonymous) object, but rather and IEnumerable of JTokens?
My web api signature is this:
public async Task<IHttpActionResult> MyFunction([FromBody]dynamic whereCond)
I have also tried to pass the object as string wrapped in an outer object like so (angular client):
this.migController.MigrationGetMigrationReports({whereCond: JSON.stringify({NotMigrated: true, MissingTargetFiles: 0})})
and then on the server I manually deserialize it as JObject:
string obj = whereCond.whereCond;
dynamic pObj = JObject.Parse(obj);
But this results in the exact same result: pObj is an IEnumerable and therefore I get an error message from the GetList call:
An enumerable sequence of parameters (arrays, lists, etc) is not allowed in this context
can anybody help?
The answer to my question turned out rather simple:
dynamic pObj = JObject.Parse(obj).ToObject<ExpandoObject>();
I had to cast it as ExpandoObject not just dynamic.
#Tsahi: this is not a design problem. My intention was to provide the server with parameters (filter) which is a quite common task for a client to reduce the dataset to be transferred. We could debate a standard way how to provide these parameters, however. In my special case the most practical way is the anonymous object.

Can swagger handle IEnumerable of string in Http requests?

I'm pretty new to web development and swagger in general so apologies if the question is too naive.
I'll use the Asp.Net Core Web Api template in visual studio 2019 to better explain my problem, so please consider that environment in the case I have omitted some information (or just ask for the missing part that I'll bring it).
There we have the WeatherForecastController class with a simple GET (which I included the names parameter):
[HttpGet]
public IEnumerable<WeatherForecast> Get(IEnumerable<string> names)
{
//...
}
When I try to test this request using the Swagger page, it doesn't recognize the name parameter.
I have ran other tests to find out what's going on and I found the following:
Swagger does work with IEnumerable as it works pretty well if the parameter is of type IEnumerable<IFormFile> (it displays a list of uploaded files, shows the file selection dialog, etc);
I tried encapsulating the parameter(s) in a DTO class and it seems to break even more stuff (even the IEnumerable<IFormFile> doesn't seem to work inside a DTO class; it only works if passed directly in the parameter list of the [HttpVerb] method;
I tried with other similar types as well as ICollection<string>, List<string>, string[]; none of them seem to work;
This same problem happens using primitive types like bool, int, etc as type arguments to IEnumerable<T>;
So what is happening? Should I set some sort of configuration value so it can work with collections of primitive types?
Update with images showing the problem:
... Get(string names, IEnumerable<IFormFile> file):
... Get(IEnumerable<string> names, IFormFile file):
As you can see, when any parameter in the param list is of IEnumerable the swagger UI doesn't properly show the requested fields like in the second image.
You must specify model binding sources in your case. Your action method should be like this:
[HttpPost("test/names")]
public IEnumerable<string> PostNames([FromQuery]IEnumerable<string> names, IEnumerable<IFormFile> files)
{
//...some code
return names;
}
You shouldn't use two or more complex type parameter as an action parameters until you specify the source they are binding from. That's because complex objects are bound to request body by default and only one parameter can be bound to request body.
As of microsoft docs :
Don't apply [FromBody] to more than one parameter per action method. Once the request stream is read by an input formatter, it's no longer available to be read again for binding other [FromBody] parameters.
Complex type means class variables, arrays, and those which are not primary types like int,double, string and etc.
Swagger generates this UI for action above:
As the final word , Note that you can't send something in your request body when you are using GET http request.

Pass Complex Parameters using Http.GetAsync

how do i pass two complex parameters, say two class objects, when i call HttpClient.GetAsync ?
I understand we could add it along the url if its simple parameters like string. But when it is complex ones such as class, how do we do it ?
use HttpClient.PostAsync
if you want to send complex data, and not just simple string
also on the server side, check if the api accepts post requests
you can test requests using http://Hurl.it

REST API different resources for GET and POST/PUT?

I'm currently in the process of designing as RESTful of an API as I can using Microsoft's Web API 2 in C#. What I'm struggling on is how best to represent resources or the proper way to do it where the GET call and POST/PUT are very different.
For example say I have something calls states that have an id, name, status, etc., these can be assigned to a document. So I have a route like this /documents/{id}/states/ . If I call a GET here I need to get the full list of all assigned states including their id, name, etc.
However, in order to change which states are assigned to the document I simply need to pass the id. I cannot do this individually, it must be an array that gets sent up since users may be interacting with hundreds or thousands at a time.
So in this case I have a few issues. I don't even know if POST or PUT is correct here, and second whichever one it is can I just take in an array of integers?
In your case, I would suggest PUT is the method you would be wanting to use, as you know the location of the resource that you are updating. For more info, see here: http://restcookbook.com/HTTP%20Methods/put-vs-post/
In ASP.NET Web API 2 you can use the [FromBody] parameter attribute, so that your method signature would be:
public void UpdateStates(int id, [FromBody]List<int> states) {}
More info on parameter attributes can be found here: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

When HTTP-POST has body, url parameter is null

This is self-hosted RESTful MVC4 Web API, the only route is api/{controller}/{state}. When I send an HTTP-POST that has a body, the state argument comes in null. If I remove the body the state variable is present.
The way I thought it worked for HTTP-POST was that the url parameters get mapped then the body gets serialized into the extra parameter, which in this case is data parameter. The content is just string data which I had to write a custom MediaTypeFormatter (which I thought was odd it couldn't handle a regular string).
Here is my controller signature
public class MyController : ApiController
{
public void Post(string state, string data)
{
}
}
Has anyone seen this behavior before or can explain to me why having a body present is affecting my url parameter?
One Solution:
I tried changing data parameter into a complex type (Just a class with a public property) and sending the content as text/xml instead of text/plain and it worked as expected. The state parameter wasn't null and I had my strongly typed object with the data. I suppose MVC wants to have something to deserialize like XML or JSON for the http-request body...
More Research:
I've had the chance to run some more tests. If the body of a post is XML/JSON it will first try to map the properties of the body-object to the method parameters like so. If still has unmapped properties then it will match the remaining properties to the properties of any strongly-typed objects in the method parameters
PostMethod(string p1, string p2, myClass obj) // if myClass has a p3 property it will be mapped from the xml body.
{
}
// xml in body of http-post
<Xml>
</p1>
</p2>
</p3>
</Xml>
If all the parameters were not mapped, then it will attempt to map the url parameters. To relate it directly to my initial problem. The best and easiest solution I see at this time is to send text/xml like this.
PostMethod(string state, string data)
{
}
<data>put data here</data>
Urlencoded key/value pairs also work very well.
var r = client.PostAsync(url, new StringContent("data=Something", Encoding.UTF8, "application/x-www-form-urlencoded"));
My best guess is that the key/value nature of JSON and XML, FormEncoded help it to map to parameters so that is why it doesn't like plain strings.
This sure gave me a headache and I find the MVC4 documentation to be rather scarce (its still in beta), but I hope this can help someone else who may have the same problem.

Categories

Resources