How to send and receive multipart/form-data containing files in .NET Core? - asp.net-core

Using .NET Core 3.1, I have the following Http client which builds a HTTP POST containing multipart/form-data body:
public async Task SendRequest(string randomString, IFormFileCollection files)
{
var form = new MultipartFormDataContent();
form.Add(new StringContent(randomString), "randomString");
foreach (var file in files)
{
var stream = new MemoryStream();
await file.CopyToAsync(stream, cancellationToken);
form.Add(new StreamContent(stream), "files");
}
using var request = new HttpRequestMessage(HttpMethod.Post, $"api/endpoint")
{
Content = form
};
await _httpClient.SendAsync(request);
}
The receiving controller looks like this:
[Consumes("multipart/form-data")]
[HttpPost]
public async Task ReceiveForm([FromForm] RequestModel request)
{
//Do stuff
}
RequestModel looks like this:
public class RequestModel
{
[JsonPropertyName("randomString")]
public string RandomString { get; set; }
[JsonPropertyName("files")]
public IFormFileCollection Files { get; set; }
}
The problem I am seeing is that requestModel.RandomString gets populated, but the requestModel.Files does not - it is null.
What am I doing wrong?

thanks for the perfect code in example!
You should set both parameters (name and fileName) to files - form.Add(new StreamContent(stream), "files", "files");
Method description:
Parameters:
content: The HTTP content to add to the collection.
name: The name for the HTTP content to add.
fileName: The file name for the HTTP content to add to the collection.
This is my example:
var fileName = "files";
var byteArrayContent = new ByteArrayContent(fileBytes1);
form.Add(byteArrayContent, fileName, fileName);
var stream = new MemoryStream(fileBytes2);
form.Add(new StreamContent(stream), fileName, fileName);
Results:

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 send the form data including excel file to the controller in asp.net core

I am trying to send the form data including excel file through fetch. But unable to hit the server using asp.net core.
const formdata = new FormData();
formdata.append('excel', file);
formdata.append('name', "importFile");
Client Side:
fetch(url, {
method: 'POST',
body: formdata
})
.then(data => {
return data;
});
Server Side:
[HttpPost]
[Route("api/Home/ValidateFile")]
public ActionResult ValidateFile([FromBody] IFormFile file)
{
using (var reader = new StreamReader(file.OpenReadStream()))
{
var fileContent = reader.ReadToEnd();
var parsedContentDisposition = ContentDispositionHeaderValue.Parse(file.ContentDisposition);
}
}
Can you please let me know how to read the excel file.
send the form data including excel file through fetch. But unable to hit the server using asp.net core.
Please note that applying the [FromBody] attribute to a parameter will populate its properties from the body of an HTTP request, not from posted form fields.
To send file with other data through formdata and read it on server side, you can try to modify the code like below.
[HttpPost]
[Route("api/Home/ValidateFile")]
public IActionResult ValidateFile([FromForm]FileUploadViewModel file)
{
//...
//code logic here
//...
FileUploadViewModel class
public class FileUploadViewModel
{
public string name { get; set; }
public IFormFile excel { get; set; }
}
Test Result

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.

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?

Rest API Calls with RestSharp calling ASP.NET Web API

I'm currently testing out writing a RESTful API with ASP.NET Web API. I'm using RestSharp on a client to simulate different calls.
I want to submit an application ID query string, and the body should be a collection of type "Log". Every time, the application ID get's posted by the body received by the server is always NULL.
Code on the server:
public class LogsController : ApiController
{
public HttpStatusCode Post(Guid ID, [FromBody] List<Log> logs)
{
if (logs != null)
return HttpStatusCode.OK;
else
return HttpStatusCode.PreconditionFailed;
}
}
public class Log
{
public Guid ErrorId { get; set; }
}
Code on the client:
static void Main(string[] args)
{
var client = new RestClient("http://localhost:36146/api");
var json = JsonConvert.SerializeObject(new List<Log>()
{
new Log { ErrorId = Guid.NewGuid()}
});
var request = new RestRequest("Logs", Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddParameter("ID", Guid.NewGuid(), ParameterType.QueryString);
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json; charset=utf-8");
request.AddBody(json);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
Console.Read();
}
public class Log
{
public Guid ErrorId { get; set; }
}
I thought I got this working, however no matter what I do now the "logs" parameter on the server is always NULL.
I think I've found the issue.
RestSharp implicitly uses the JsonSerializer when populating the body of the request. As I was also called the Serializer I think it caused issues with the formatting.
I've removed that call to the serializer and now I'm receiving a 200 back from the server.
Happy days.