post multipart/form-data in c# HttpClient 4.5 - c#

Problem
I am trying to post API to send data to API which calls my internal API service to send that data to other API i service. Entity contains property with files . this send only file to the other derive but the NameSender property not send with the file.
Entity
public class Email
{
public string NameSender{ get; set; }
public List<IFormFile> Files { get; set; }
}
Api
[Consumes("multipart/form-data")]
[HttpPost]
public IActionResult SendEmail([FromForm]Entity entity)
{
try
{
string Servicesfuri = this.serviceContext.CodePackageActivationContext.ApplicationName + "/" + this.configSettings.SendNotificationServiceName;
string proxyUrl = $"http://localhost:{this.configSettings.ReverseProxyPort}/{Servicesfuri.Replace("fabric:/", "")}/api/values/Send";
//attachments
var requestContent = new MultipartFormDataContent();
foreach (var item in entity.Files)
{
StreamContent streamContent = new StreamContent(item.OpenReadStream());
var fileContent = new ByteArrayContent(streamContent.ReadAsByteArrayAsync().Result);
requestContent.Add(fileContent, item.Name, item.FileName);
}
HttpResponseMessage response = this.httpClient.PostAsync(proxyUrl, requestContent).Result;
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
return this.StatusCode((int)response.StatusCode);
}
return this.Ok(response.Content.ReadAsStringAsync().Result);
}
catch (Exception e)
{
throw e;
}
}

This method works for me. You can use form data and file
public async Task<bool> Upload(FileUploadRequest model)
{
var httpClientHandler = new HttpClientHandler()
{
Proxy = new WebProxy("proxyAddress", "proxyPort")
{
Credentials = CredentialCache.DefaultCredentials
},
PreAuthenticate = true,
UseDefaultCredentials = true
};
var fileContent = new StreamContent(model.File.OpenReadStream())
{
Headers =
{
ContentLength = model.File.Length,
ContentType = new MediaTypeHeaderValue(model.File.ContentType)
}
};
var formDataContent = new MultipartFormDataContent();
formDataContent.Add(fileContent, "File", model.File.FileName); // file
formDataContent.Add(new StringContent("Test Full Name"), "FullName"); // form input
using (var client = new HttpClient(handler: httpClientHandler, disposeHandler: true))
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + tokenString);
using (var res = await client.PostAsync("http://filestorageurl", formDataContent))
{
return res.IsSuccessStatusCode;
}
}
}

Related

I am trying to submit an object that contains some data and a file from the client app to the API app using ASP.CORE 5

I have tried to submit an object from client app to an API which contains some data and a file, but I failed to do so.
Here is the code:
(1) The model:
public class ABC
{
public int Id { get; set; }
public string Image { get; set; }
public string Name { get; set; }
[NotMapped]
public IFormFile File { get; set; }
}
(2) The client app controller
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ABC aBC)
{
using (var httpClient = new HttpClient())
{
StringContent content = new StringContent(JsonConvert.SerializeObject(aBC), Encoding.UTF8, "application/json");
using (var response = httpClient.PostAsync(BaseURLManager.BaseUrl + "ABCs", content))
{
var apiResponse = response.Result.Content.ReadAsStringAsync();
aBC = JsonConvert.DeserializeObject<ABC>(apiResponse.Result);
}
}
return View(aBC);
}
(3) The API controller
// POST: api/ABCs
[HttpPost]
public async Task<ActionResult<ABC>> PostABC([FromForm] ABC aBC)
{
_context.ABC.Add(aBC);
await _context.SaveChangesAsync();
return CreatedAtAction("GetABC", new { id = aBC.Id }, aBC);
}
Could any one provide me a working code, I am using ASP.NET Core 5.
Thank you in advance
Since you use the FromForm attribute inside the api method, you should send request in formdata instead of json format.
More details, you could refer to below codes:
var client = new HttpClient
{
BaseAddress = new("https://localhost:7105/api/")
};
var path = $"{_hostEnvironment.ContentRootPath}test.json";
var stream = System.IO.File.OpenRead(path);
var request = new HttpRequestMessage(HttpMethod.Post, "PostABC");
var content = new MultipartFormDataContent
{
// file
{ new StreamContent(stream), "File", "test.json" },
// payload
{ new StringContent("1"), "Id" },
{ new StringContent("bbbb"), "Image" },
{ new StringContent("ccc"), "Name" }
};
request.Content = content;
var re = client.SendAsync(request).Result;
Result:

Unable to test endpoint with Xunit - StatusCode: 400, ReasonPhrase: 'Bad Request'

I am writing xunit test to test this endpoint.
[Route("Document")]
[HttpPost]
public async Task<IActionResult> UploadFileAsync([FromForm] DocumentUploadDto documentUploadDto)
{
// code removed for brevity
}
When I put a breakpoint in this method, it doesn't reach here.
This is my code in XUnit
[Fact]
public async Task When_DocumentTypeInvalidFileType_Then_ShouldFail()
{
using (var client = new HttpClient() { BaseAddress = new Uri(TestSettings.BaseAddress) })
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _authToken);
var filePath = #"D:\Files\NRIC.pdf";
using (var stream = File.OpenRead(filePath))
{
FormFile formFile = new FormFile(stream, 0, stream.Length, null, Path.GetFileName(stream.Name))
{
Headers = new HeaderDictionary(),
ContentType = "application/pdf"
};
var documentUploadDto = new DocumentUploadDto
{
DocumentType = ApplicationDocumentType.ICFRONT,
DraftId = Guid.NewGuid(),
File = formFile
};
var encodedContent = new StringContent(JsonConvert.SerializeObject(documentUploadDto), Encoding.UTF8, "application/json");
// Act
var response = await client.PostAsync("/ABC/Document", encodedContent);
var responseString = await response.Content.ReadAsStringAsync();
_output.WriteLine("response: {0}", responseString);
}
}
}
In response, I am getting StatusCode: 400, ReasonPhrase: 'Bad Request'
This is DocumentUploadDto
public class DocumentUploadDto
{
[FileValidation]
public IFormFile File { get; set; }
public ApplicationDocumentType DocumentType { get; set; }
public Guid DraftId { get; set; }
public Guid Id { get; set; }
}
You are sending JSON request (content type application/json) but server expects form data. You need to use multipart form like this:
using (var stream = File.OpenRead(filePath)) {
using (var form = new MultipartFormDataContent()) {
// metadata from your DocumentUploadDto
form.Add(new StringContent(ApplicationDocumentType.ICFRONT.ToString()), "DocumentType");
form.Add(new StringContent(Guid.NewGuid().ToString()), "DraftId");
form.Add(new StringContent(Guid.NewGuid().ToString()), "Id");
// here is the file
var file = new StreamContent(stream);
// set Content-Type
file.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
form.Add(file, "File", Path.GetFileName(filePath));
// Act
var response = await client.PostAsync("/ABC/Document", form);
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
_output.WriteLine("response: {0}", responseString);
}
}

How to use http postasync in asp.net core 3.1

I am trying to post a request to an API in asp.net. The code below does not give the same result like the code that I run in an ionic application, which I have also included below
Asp.net core 3.1 code
public class AccountInqueryParm
{
public string message_id { get; set; }
public string token { get; set; }
public string acc { get; set; }
public string br { get; set; }
public string appType { get; set; }
}
public async Task<IEnumerable<dynamic>> BalanceInquery(AccountInqueryParm parm)
{
try
{
// return new[] { parm };
HttpClientHandler handler = new HttpClientHandler() { UseDefaultCredentials = false };
using (var client = new HttpClient(handler))
{
client.BaseAddress = new Uri(mbBaseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", encoding(mbUsername + ':' + mbPassword));
parm = new AccountInqueryParm
{
message_id = "7ac972059c7740c5855bc603facee4f2",
token = "541f21c5f730a024b72b7239df9b38055784ebf2",
acc = "1100016",
br = "00000",
appType = "1"
};
StringContent content = new StringContent(JsonConvert.SerializeObject(parm), Encoding.UTF8, "application/json");
using (var response = await client.PostAsync(client.BaseAddress + "accountEnquiry", content))
{
string apiResponse = await response.Content.ReadAsStringAsync();
return new[] { apiResponse };
}
}
}
catch (Exception)
{
throw;
}
}
This is the ionic-angular code that works fine
MBServiceLocal() {
const msgid = '7ac972059c7740c5855bc603facee4f2';
const accNo = '1100016';
const brID = '00000';
const appTypeID = '1'
const tKen = '541f21c5f730a024b72b7239df9b38055784ebf2';
const url = 'api url';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa('username' + ':'+ 'password')
})
};
const parmObject = { messageId: msgid, token: tKen, acc: accNo, br: brID, appType: appTypeID };
this.http.post(url, parmObject, httpOptions)
.subscribe(response => {
console.log('MBIBService Result: ', response);
}, error => {
console.log('MBIBService server error: ' + error);
});
}
}
How can I make c# code work the same way based on the ionic-angular code? I have tried several workarounds. For example, how can I pass httpOptions in asp.net httpclient postasync() method?

why does my web api client call not work in Raspberry Pi2 Iot

I have this code:
private const string route = "/api/Print";
public bool Update(string header, string tc)
{
bool success = false;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("my uri");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var print = new Print { CompanyRef = new Guid(), Header = header, TC = tc };
var response = client.PutAsJsonAsync(route, print);
}
success = true;
return success;
}
public sealed class Print
{
public string Header { get; set; }
public string TC { get; set; }
public System.Guid CompanyRef { get; set; }
}
I call it like so:
Update(" header", " string tc");
In C# desktop app it works.
In Windows 10 IoT on a Raspberry Pi2 device it does not work.
Yet, when i am calling a Get from my Web API server *in Iot) it works fine.
?
I am using this code for a year now and it works:
using Windows.Web.Http;
using (HttpClient httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");
try
{
var o = new
{
operation = "NewEvent",
location_id = locationID,
eventName = eventName
};
HttpStringContent content = new HttpStringContent(JsonConvert.SerializeObject(o), Windows.Storage.Streams.UnicodeEncoding.Utf8, "application/json");
HttpResponseMessage response = await httpClient.PostAsync(new Uri(urlPostData), content);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
// TODO: Do something with the responseBody
}
catch (Exception)
{
// TODO: Deal with exception - could be a server not found, 401, 404, etc.
}
}

HttpClient Multipart Form Post in C#

I'm trying to do a multipart form post using the HttpClient in C# and am finding the following code does not work.
Important:
var jsonToSend = JsonConvert.SerializeObject(json, Formatting.None, new IsoDateTimeConverter());
var multipart = new MultipartFormDataContent();
var body = new StringContent(jsonToSend, Encoding.UTF8, "application/json");
multipart.Add(body);
multipart.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")), "test", "test.txt");
var httpClient = new HttpClient();
var response = httpClient.PostAsync(new Uri("http://localhost:55530"), multipart).Result;
Full Program :
namespace CourierMvc.Worker
{
class Program
{
static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Hit any key to make request.");
Console.ReadKey();
try
{
var request = new RestRequest(Method.POST)
{
Resource = "http://localhost:55530"
};
var json = new CourierMessage
{
Id = Guid.NewGuid().ToString(),
Key = "awesome",
From = "khalid#home.com",
To = new[] { "me#test.com", "you#test.com" },
Subject = "test",
Body = "body",
Processed = DateTimeOffset.UtcNow,
Received = DateTime.Now,
Created = DateTime.Now,
Sent = DateTime.Now,
Links = new[] { new Anchor { Link = "http://google.com" }, new Anchor { Link = "http://yahoo.com" } }
};
var jsonToSend = JsonConvert.SerializeObject(json, Formatting.None, new IsoDateTimeConverter());
var multipart = new MultipartFormDataContent();
var body = new StringContent(jsonToSend, Encoding.UTF8, "application/json");
multipart.Add(body);
multipart.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")), "test", "test.txt");
var httpClient = new HttpClient();
var response = httpClient.PostAsync(new Uri("http://localhost:55530"), multipart).Result;
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
}
I really have no idea why it doesn't work. I get the file to post to the endpoint, but the body (json) never gets there. Am I doing something wrong?
Server Side Code Request:
namespace CourierMvc.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return Content("Home#Index");
}
[ValidateInput(false)]
public ActionResult Create(CourierMessage input)
{
var files = Request.Files;
return Content("OK");
}
}
}
Route Config:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Create", id = UrlParameter.Optional }
);
}
public class CourierMessage
{
public string Id { get; set; }
public string Key { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
public DateTimeOffset Processed { get; set; }
public DateTime Received { get; set; }
public DateTime Created { get; set; }
public DateTime Sent { get; set; }
public HttpPostedFileBase File { get; set; }
}
while (true)
{
Console.WriteLine("Hit any key to make request.");
Console.ReadKey();
using (var client = new HttpClient())
{
using (var multipartFormDataContent = new MultipartFormDataContent())
{
var values = new[]
{
new KeyValuePair<string, string>("Id", Guid.NewGuid().ToString()),
new KeyValuePair<string, string>("Key", "awesome"),
new KeyValuePair<string, string>("From", "khalid#home.com")
//other values
};
foreach (var keyValuePair in values)
{
multipartFormDataContent.Add(new StringContent(keyValuePair.Value),
String.Format("\"{0}\"", keyValuePair.Key));
}
multipartFormDataContent.Add(new ByteArrayContent(File.ReadAllBytes("test.txt")),
'"' + "File" + '"',
'"' + "test.txt" + '"');
var requestUri = "http://localhost:5949";
var result = client.PostAsync(requestUri, multipartFormDataContent).Result;
}
}
}
This is an example of how to post string and file stream with HTTPClient using MultipartFormDataContent. The Content-Disposition and Content-Type need to be specified for each HTTPContent:
Here's my example. Hope it helps:
private static void Upload()
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "CBS Brightcove API Service");
using (var content = new MultipartFormDataContent())
{
var path = #"C:\B2BAssetRoot\files\596086\596086.1.mp4";
string assetName = Path.GetFileName(path);
var request = new HTTPBrightCoveRequest()
{
Method = "create_video",
Parameters = new Params()
{
CreateMultipleRenditions = "true",
EncodeTo = EncodeTo.Mp4.ToString().ToUpper(),
Token = "x8sLalfXacgn-4CzhTBm7uaCxVAPjvKqTf1oXpwLVYYoCkejZUsYtg..",
Video = new Video()
{
Name = assetName,
ReferenceId = Guid.NewGuid().ToString(),
ShortDescription = assetName
}
}
};
//Content-Disposition: form-data; name="json"
var stringContent = new StringContent(JsonConvert.SerializeObject(request));
stringContent.Headers.Add("Content-Disposition", "form-data; name=\"json\"");
content.Add(stringContent, "json");
FileStream fs = File.OpenRead(path);
var streamContent = new StreamContent(fs);
streamContent.Headers.Add("Content-Type", "application/octet-stream");
streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + Path.GetFileName(path) + "\"");
content.Add(streamContent, "file", Path.GetFileName(path));
//content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
Task<HttpResponseMessage> message = client.PostAsync("http://api.brightcove.com/services/post", content);
var input = message.Result.Content.ReadAsStringAsync();
Console.WriteLine(input.Result);
Console.Read();
}
}
}
So the problem I'm seeing is that the MultipartFormDataContent request message will always set the content type of the request to "multipart/form-data". Endcoding json and placing that into the request only "looks" like to the model binder as a string.
Your options are:
have your mvc action method receive a string and deserialize into your object
post each property of your model as a form part
create a custom model binder that will handle your request.
Breakup the operation into two posts, first sends the json metadata, the other sends the file. The response from the server should send some id or key to correlate the two requests.
Reading through the RFC document and the MSDN documentation you may be able to do this, if you replace MultipartFormDataContent with MultipartContent. But I have not tested this yet.
string path = #"C:\New folder\Test.pdf"; // **ANY FILE**
var formContent = new MultipartFormDataContent
{
{ new ByteArrayContent(File.ReadAllBytes(path)), "file", Path.GetFileName(path) }
};
var client = new HttpClient();
var response = client.PostAsync(_configuration["Url"], formContent).Result;

Categories