Could not post value to web API using IFormFile - api

i have a web application that does an action to upload image to api
the code like this
[HttpPost]
public async Task<IActionResult> Upload([FromForm] UploadModel model)
{
var upload = AccountApi.UploadFile(model);
return Ok("OK");
}
public static object UploadFile(UploadModel model)
{
RestClient client = InitClient();
request.Resource = "Account/UploadFile";
request.Method = Method.POST;
request.AddJsonBody(model);
IRestResponse<object> response = client.Execute<object>(request);
return response.Data;
}
public class UploadModel
{
public long UserId { get; set; }
public string Token { get; set; }
public IFromFile File { get; set; }
}
and there's a web API to handle Rest request above
the code like this
[HttpPost("UploadFile")]
public async Task<object> UploadFileAction(UploadModel model)
{
// the code handle upload file request here
return "Success";
}
my issue is the UploadModel model in web application contains the right value that requested from front-end (UserId = 10, Token = "eyJ..........", File = [object])
but when it posted to API, the 3 properties in UploadModel didn't get the value posted from web application (UserId = 0, Token = null, File = null)
could you help me to find the solution for this. Thanks all

I found the solution myself.
instead of using IFormFile to post, I serialized the image file to a base64 string and post it over API. and in API, I convert it to File object
Thanks for following my question

Related

RestSharp RestClient not working with ASP.NET Core [FromForm]

Hi I am writing integration test for my asp.net core project and i am trying to use RestSharp RestClient to send Form Data. My Code is working fine as i am getting the desired result if use postman but if i copy restsharp code from postman and try to implement in my test case its not reaching the controller. Following is my code
Postman -
Test Code -
[Test]
public void ConvertToJson_CSV()
{
var client = new RestClient("https://localhost:44355/GroupContacts/ConvertToJson");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
//request.AddFile("files", #"C:\Users\RanaBoy\Desktop\ZTT\ZTTTestFiles\sample500.csv");
request.AddFile("files", System.IO.File.ReadAllBytes(#"C:\Users\RanaBoy\Desktop\ZTT\ZTTTestFiles\sample500.csv"), "sample500.csv");
request.AddParameter("optInStatus", "1");
request.AddParameter("SessionId", _SessionId);
request.AddParameter("AccountId", _AccountId);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
request.AlwaysMultipartFormData = true;
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
//Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
Controller Code -
[HttpPost]
[Route("ConvertToJson")]
[Consumes("multipart/form-data")]
public async Task<IActionResult> ConvertToJson([FromForm] ConvertFileToJsonCommand command)
{
var result = await _mediator.Send(command);
return Ok(result.ResultJson);
}
Model Class -
public class ConvertFileToJsonCommand : IRequest<ConvertFileToJsonResponse>
{
public int AccountId { get; set; }
public string SessionId { get; set; }
public List<IFormFile> files { get; set; }
public int optInStatus { get; set; }
}
Postman doesn't generate the correct code for RestSharp. For example, your code sets the content-type header twice.
The following code should produce the correct request:
public async Task ConvertToJson_CSV()
{
var client = new RestClient("https://localhost:44355/GroupContacts/ConvertToJson");
var request = new RestRequest(Method.POST)
.AddFile("files", "sample500.csv", "C:\Users\RanaBoy\Desktop\ZTT\ZTTTestFiles\sample500.csv")
.AddParameter("optInStatus", "1")
.AddParameter("SessionId", _sessionId)
var response = await client.ExecuteAsync(request);
Console.WriteLine(response.Content);
//Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
}
Notice that the code uses RestSharp 107. If for some reason it won't work, you can check the difference between Postman request and RestSharp request using HttpTracer (check the docs).

How to consume Azure Fuction from MVC Controller

I have created and published an Azure Function (HTTP Triggered) for a search functionality. When I type an ID in a search box and click on "Search", it should call the Azure Function and get the result back.
How to integrate the Azure Function with my Controller Action in .NETCore?
Here is the example how you could call your azure function into the controller.
I have a simple azure function which return a name and email once its called. Let's see the below example:
public class InvokeAzureFunctionController : ApiController
{
// GET api/<controller>
public async System.Threading.Tasks.Task<IEnumerable<object>> GetAsync()
{
HttpClient _client = new HttpClient();
HttpRequestMessage newRequest = new HttpRequestMessage(HttpMethod.Get, "http://localhost:7071/api/FunctionForController");
HttpResponseMessage response = await _client.SendAsync(newRequest);
dynamic responseResutls = await response.Content.ReadAsAsync<dynamic>();
return responseResutls;
}
}
Test Function For Controller Invocation:
public static class FunctionForController
{
[FunctionName("FunctionForController")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
// parse query parameter
string name = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
.Value;
if (name == null)
{
// Get request body
dynamic data = await req.Content.ReadAsAsync<object>();
name = data?.name;
}
ContactInformation objContact = new ContactInformation();
objContact.Name = "From Azure Function";
objContact.Email = "fromazure#function.com";
return req.CreateResponse(HttpStatusCode.OK, objContact);
}
}
Simple ContactInformation Class I have Used:
public class ContactInformation
{
public string Name { get; set; }
public string Email { get; set; }
}
PostMan Test:
I have called the controller action from Post Man and its successfully return data from my local azure function through the local controller action. See the screen shot below:
Hope you understand. Just plug and play now.

Asp.net core sending form-data and json body at the same to

I am trying to upload a file and also JSON body at the same time to a POST method as below
public async Task<ResponseModel<PersonWriteResponse>> AddPerson([FromForm]IFormFile file, [FromForm]PersonPostRequest request)
{
var person = await _service.AddPerson(file,request);
return ResponseModelHelper.BuildResponse(person, $"/production/person", "person");
}
Both parameters are always null. In postman, I am specifying the content-type as "Multipart/form-data"
Is this the correct way of passing file and json data?
Alan-
I try to use model witch include IFormFile, and it's works
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> update([FromForm]MyFile model)
{
return Ok("Success!");
}
public class MyFile
{
public string Id { get; set; }
public IFormFile File { get; set; }
// Other properties
}
PostMan request here
You may set breakPoint at the return Ok("Success!"); line and saw what you get

How to create an ASP.NET Core API controller action for HttpPost that can accept a request that has content of type MultipartFormDataContent

I am working on an ASP.NET Core 2.0 API in VS2017.
I want to create a controller action for an HTTP Post method that accept string and byte[] values that I will then use to create records in my SQL database.
From what I understand, if I want to post both string data and a byte[] that represents a file, I have to use MultipartFormDataContent as the type of content in the request from my client.
So, on the API controller action, how is that mapped? Can I have a DTO class in the API that has properties for both the string values and the byte[] value and have it passed into the API controller action via the [FromBody]UploadsDto dto
For example, have a DTO class like this...
public class UploadFileRecordForCreationDto
{
public int LocationId { get; set; }
public string FileName { get; set; }
public byte[] UploadedFile { get; set; }
}
Then have a controller action with this signature...
[HttpPost(Name = "CreateUploadFileRecord")]
public IActionResult CreateUploadFileRecord([FromBody]UploadFileRecordForCreationDto dto)
{
...
...
...
return CreatedAtRoute("GetUploadedFileFile", new { id = linkedResourceToReturn["Id"] }, linkedResourceToReturn);
}
And then have that API action accept a request created using something similar to what I am doing with this test console application on the client side;
static async Task CreateUploadFileRecordAsync()
{
httpClient.BaseAddress = new Uri("https://localhost:44369");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string relativeUrl = "/api/UploadFilesManager";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, relativeUrl);
HttpResponseMessage response = new HttpResponseMessage();
using (var content = new MultipartFormDataContent("--UploadTest"))
{
var values = new[]
{
new KeyValuePair<string,string>("LocationId","1"),
new KeyValuePair<string,string>("FileName","TestFile-01.txt"),
};
foreach (var keyvaluepair in values)
{
content.Add(new StringContent(keyvaluepair.Value, Encoding.UTF8, "application/json"), keyvaluepair.Key);
}
var fileContent = new ByteArrayContent(File.ReadAllBytes(#"C:\testfile-01.txt"));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue(DispositionTypeNames.Attachment)
{
Name = "UploadedFile",
FileName = "testfile-01.txt"
};
content.Add(fileContent);
request.Content = content;
response = await httpClient.SendAsync(request);
}
if (response.IsSuccessStatusCode)
{
string result = response.Headers.Location.ToString();
Console.WriteLine("Success:\n");
Console.WriteLine($"New Record Link: [{result}]\n");
}
else
{
Console.WriteLine($"Failed to create new UploadFile record. Error: {0}\n", response.ReasonPhrase);
}
}
If it doesn't just map to a DTO in the FromBody, can anyone provide an example of how to deal with this use case?

Model Binding for multipart/form-data (File + JSON) post in ASP.NET Core 1.1

I'm attempting to build an ASP.NET Core 1.1 Controller method to handle an HTTP Request that looks like the following:
POST https://localhost/api/data/upload HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------625450203542273177701444
Host: localhost
Content-Length: 474
----------------------------625450203542273177701444
Content-Disposition: form-data; name="file"; filename="myfile.txt"
Content-Type: text/plain
<< Contents of my file >>
----------------------------625450203542273177701444
Content-Disposition: form-data; name="text"
Content-Type: application/json
{"md5":"595f44fec1e92a71d3e9e77456ba80d0","sessionIds":["123","abc"]}
----------------------------625450203542273177701444--
It's a multipart/form-data request with one part being a (small) file and the other part a json blob that is based on a provided specification.
Ideally, I'd love my controller method to look like:
[HttpPost]
public async Task Post(UploadPayload payload)
{
// TODO
}
public class UploadPayload
{
public IFormFile File { get; set; }
[Required]
[StringLength(32)]
public string Md5 { get; set; }
public List<string> SessionIds { get; set; }
}
But alas, that doesn't Just Work {TM}. When I have it like this, the IFormFile does get populated, but the json string doesn't get deserialized to the other properties.
I've also tried adding a Text property to UploadPayload that has all the properties other than the IFormFile and that also doesn't receive the data. E.g.
public class UploadPayload
{
public IFormFile File { get; set; }
public UploadPayloadMetadata Text { get; set; }
}
public class UploadPayloadMetadata
{
[Required]
[StringLength(32)]
public string Md5 { get; set; }
public List<string> SessionIds { get; set; }
}
A workaround that I have is to avoid model binding and use MultipartReader along the lines of:
[HttpPost]
public async Task Post()
{
...
var reader = new MultipartReader(Request.GetMultipartBoundary(), HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
var filePart = section.AsFileSection();
// Do stuff & things with the file
section = await reader.ReadNextSectionAsync();
var jsonPart = section.AsFormDataSection();
var jsonString = await jsonPart.GetValueAsync();
// Use $JsonLibrary to manually deserailize into the model
// Do stuff & things with the metadata
...
}
Doing the above bypasses model validation features, etc. Also, I thought maybe I could take that jsonString and then somehow get it into a state that I could then call await TryUpdateModelAsync(payloadModel, ...) but couldn't figure out how to get there either - and that didn't seem all that clean either.
Is it possible to get to my desired state of "transparent" model binding like my first attempt? If so, how would one get to that?
The first problem here is that the data needs to be sent from the client in a slightly different format. Each property in your UploadPayload class needs to be sent in its own form part:
const formData = new FormData();
formData.append(`file`, file);
formData.append('md5', JSON.stringify(md5));
formData.append('sessionIds', JSON.stringify(sessionIds));
Once you do this, you can add the [FromForm] attribute to the MD5 property to bind it, since it is a simple string value. This will not work for the SessionIds property though since it is a complex object.
Binding complex JSON from the form data can be accomplished using a custom model binder:
public class FormDataJsonBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if(bindingContext == null) throw new ArgumentNullException(nameof(bindingContext));
// Fetch the value of the argument by name and set it to the model state
string fieldName = bindingContext.FieldName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(fieldName);
if(valueProviderResult == ValueProviderResult.None) return Task.CompletedTask;
else bindingContext.ModelState.SetModelValue(fieldName, valueProviderResult);
// Do nothing if the value is null or empty
string value = valueProviderResult.FirstValue;
if(string.IsNullOrEmpty(value)) return Task.CompletedTask;
try
{
// Deserialize the provided value and set the binding result
object result = JsonConvert.DeserializeObject(value, bindingContext.ModelType);
bindingContext.Result = ModelBindingResult.Success(result);
}
catch(JsonException)
{
bindingContext.Result = ModelBindingResult.Failed();
}
return Task.CompletedTask;
}
}
You can then use the ModelBinder attribute in your DTO class to indicate that this binder should be used to bind the MyJson property:
public class UploadPayload
{
public IFormFile File { get; set; }
[Required]
[StringLength(32)]
[FromForm]
public string Md5 { get; set; }
[ModelBinder(BinderType = typeof(FormDataJsonBinder))]
public List<string> SessionIds { get; set; }
}
You can read more about custom model binding in the ASP.NET Core documentation: https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding
I'm not 100% clear on how this would work for ASP.NET Core but for Web API (so I assume a similar path exists here) you'd want to go down the road of a Media Formatter. Here's an example (fairly similar to your question) Github Sample with blog post
Custom formatters might be the ticket? https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-formatters