.NET Core Web API with Swagger: Post both a file AND data on the same call - swagger

I've created a .NET Core 3.1 Web API and I use swagger for the documentation/preview of it. I want to both upload a file and send a long some data to a post call. I can make 1 call to upload data and that works. I can make another call to send object data and that works. But trying to combine the 2 together (by using swagger interface to fill out the data) never hits the method and gives me the error: Unsupported Media Type.
[HttpPost]
[Route("Send")]
public void Send(IFormFile file, Email emailInfo)
{
}
[HttpPost]
[Route("Test")]
public void Test(IFormFile file)
{
}
public class Email
{
public List<string> To { get; set; }
public string Body { get; set; }
public string Subject { get; set; }
public IFormFile Files { get; set; }
}
One thing you may notice is on the Send I have IFromFile as a parameter but IFromFile in Email obejct as well. For some reason in order for Swagger to show the file select button in the parameters it needed to be in both places.

Try using [From Query] binding:
[HttpPost]
[Route("Send")]
public void Send([FromQuery] Email emailInfo)
{
var emailFromParam = emailInfo;
}
Input:
Debug output showing param values:

Related

RestSharp and serializable Class

I have a webApi and an MVC application.
The webApi has all the logic and the MVC application is just the presentation.
Im using RestSharp to get the data from the WebApi to the MVC application.
Im sharing here one method that retrieves all user information
public IUser getUserInformationLogin(string palsoftID)
{
var request = new RestRequest("FrontDeskLog/GetUserInfo/{PalsoftID}", Method.POST) { RequestFormat = DataFormat.Json };
request.AddParameter("PalsoftID", palsoftID, ParameterType.UrlSegment);
var response = service.Execute<User>(request);
return response.Data
}
everything is good until I add Serialize attribute to the User class, I need to make User serializable in order to use session state StateServer for my MVC application.
But after adding the serialize attr the above method always returns null.
If I debug i can see that in the Response. Content all data is there, but response.data returns a null object.
Any help will be very appreciated.
The Method in the webApi is this one
public IUser GetUserInfo(string PalsoftID)
{
FrontDeskDb data = new FrontDeskDb();
return data.getUsersInfo(PalsoftID);
}
this is the class Roles
public class Roles
{
public int RoleID { get; set; }
public string Role { get; set; }
public bool Main { get; set; }
}
Thank you.

How to filter breeze.webapi returned entities from the server

I have a standard breeze web-server which exposes the Project
[HttpGet]
public IQueryable<Project> Projects()
{
return _db.Context.Projects;
}
How can i prevent a Project with NoLongerExist=true from begin included in queries returned to the JavaScript client from here affecting the functionality of the breezejs client?.. um i would also like to hide this property also, people wont like to see that their projects are not actually deleted!
you can try this on your breeze controller...
public IQueryable<Project> Projects()
{
return _db.Context.Projects.Where(o => o.NoLongerExist == true);
}
prevent json serialization of the NoLongerExist property using data annotations on your model by doing this, i'm assuming you are using EF6 with JSON.NET on your backend...
[Table("Project")]
public partial class Project
{
public Project()
{
}
public int id { get; set; }
[JsonIgnore]
public bool NoLongerExist { get; set; }
}

What can cause automapping to behave differently in different MVC app with same code?

I have a test app that works perfectly with the following classes in one app, but not in another:
public class ValueChange
{
public int GroupId { get; set; }
public List<ItemValueChange> Changes { get; set; }
}
public class ItemValueChange
{
public int ItemId { get; set; }
public string Value { get; set; }
public string Key { get; set; }
}
My plugin posts a JS structure that matches this structure (changes is a jQuery array).
The raw post data (from Fiddler2) looks like:
GroupId 1000
Changes[0][Value]
Changes[0][Key]
Changes[0][ItemId] 1
In the test app this works and maps the data sent to a ValueChange object correctly.
[HttpPost]
public JsonResult Validate(ValueChange change)
{
// The Changes property has the required array of objects/properties
}
In our main application, to which I just ported the plugin and classes, the post data sent looks like:
GroupId 3705
Changes[0][Value]
Changes[0][Key]
Changes[0][ItemId] 81866
and the validate method called looks identical:
[HttpPost]
public JsonResult Validate(ValueChange changes)
{
// changes contains a null list and no GroupId
}
If I break-point this method changes is non-null object with a GroupId of 0 and no child elements in Changes. I can however see these values available from Request.Form in the debugger:
Request.Form["GroupId"] "3705" string
Request.Form["Changes[0][Key]"] "" string
Request.Form["Changes[0][ItemId]"] "81866" string
Request.Form["Changes[0][Value]"] "" string
Q. What would cause the automapping to not work in a different MVC project with the type of data?
If I simplify ValueChange to this (below) it starts working and receives GroupId values:
public class ValueChange
{
public int GroupId { get; set; }
}
If I send JS object data without a changes property it works e.g.
{ GroupId: 123 }
Something about the list called Changes is causing the mapping to fail. I have tried it as an array and also sending a single hard-wired entry from JS like this (still fails):
{ GroupId: 123, Changes: [{ItemId: 456, Value: "V", Key: "K"}]
OMG. The auto-mapper will ignore properties if a property name matches the parameter name!!!
It was caused simply by having the parameter called changes (vs. change in the test app) when a property of the received data was also called changes.
Solution: I changed the parameter name e.g.
[HttpPost]
public JsonResult Validate(ValueChange valueChange)
{
}
To clarify, this problem occurs is a first-level property of the data passed matches a parameter name. If it were a nested property tit would not attempt to match the parameter name.
This little detail needs to be stapled to everyone's desk/hand/head.* :)

asp.net MVC 3 - reading POST payload in paramterized controller method

I had
[HttpPost]
public ActionResult Foo()
{
// read HTTP payload
var reqMemStream = new MemoryStream(HttpContext.Request.BinaryRead(HttpContext.Request.ContentLength));
....
}
The payload is application/json; worked fine; then I changed to
public ActionResult Foo(string thing)
{
....
}
The intention being to post to MyController/Foo?thing=yo
Now I cant read the payload(the length is correct but the stream is empty). My guess is that the controller plumbing has eaten the payload looking for form post data that can be mapped to the method parameters. Is there some way that I can stop this behavior (surely MVC should not have eaten a payload whose type is marked as JSON , it should only look at form post data). My work around is to add 'thing' to the json but I dont really like that
Try resetting the input stream position before reading:
public ActionResult Foo(string thing)
{
Request.InputStream.Position = 0;
var reqMemStream = new MemoryStream(HttpContext.Request.BinaryRead(HttpContext.Request.ContentLength));
....
}
Now this being said, if you are sending an application/json payload why on the holy Earth are you bothering to read directly the request stream instead of simply defining and using a view model:
public class MyViewModel
{
public string Thing { get; set; }
public string Foo { get; set; }
public string Bar { get; set; }
...
}
and then:
public ActionResult Foo(MyViewModel model)
{
// use the model here
....
}
ASP.NET MVC 3 has a built-in JsonValueProviderFactory which allows you to automatically bind JSON requests to models. And if you are using an older version it is trivially easy to add such factory your self as Phil Haack illustrates in his blog post.

ASP.NET Web-API not serializing readonly property

I migrated an API method from a standard MVC action to the new asp.net Web-API beta and suddenly a read only property is no longer serialized (both returning JSON). Is this expected behaviour?
Edit: Added code sample
I have both Newtonsoft.Json 4.0.8 and System.Json 4.0 referenced through nuget packages
public IQueryable<Car> Gets()
{
return _carRepository.GetCars();
}
public class Car
{
public IEnumerable<Photo> Photos
{
get { return _photos; }
}
public string PreviewImageUrl // No longer serialized
{
get
{
var mainImage = Photos.FirstOrDefault(o => o.IsMainPreview) Photos.FirstOrDefault();
return mainImage != null ? mainImage.Url : (string.Empty);
}
}
}
}
The JsonMediaTypeFormatter that ships with the Beta uses a serializer that does not support read-only properties (since they would not round-trip correctly). We are planning on addressing this for the next realese.
In the mean-time you could use a custom JSON MediaTypeFormatter implementation that uses Json.NET (there's one available here) instead of the built-in formatter.
Update: Also check out Henrik's blog about hooking up a JSON.NET formatter: http://blogs.msdn.com/b/henrikn/archive/2012/02/18/using-json-net-with-asp-net-web-api.aspx
I don't know if this is an expected behavior or not. I would say that this is expected for input parameters (because you cannot set their values) but not for output parameters. So I would say this is a bug for an output parameter. And here's an example illustrating the issue:
Model:
public class Product
{
public Product()
{
Prop1 = "prop1 value";
Prop2 = "prop2 value";
Prop3 = "prop3 value";
}
public string Prop1 { get; set; }
[ReadOnly(true)]
public string Prop2 { get; set; }
public string Prop3 { get; protected set; }
}
Controller:
public class ProductsController : ApiController
{
public Product Get(int id)
{
return new Product();
}
}
Request:
api/products/5
Result:
{"Prop1":"prop1 value","Prop2":"prop2 value"}
So if the property doesn't have a public setter it is not serialized which doesn't seem normal as the Product class is used as output in this case.
I would suggest opening a connect ticket so that Microsoft can fix this before the release or at least tell that this is by design.

Resources