WebApi Route fails if streamcontentis not empty - c#

I'm trying to send an object using Web-APi 2 and protobuf-net. I keep getting a 404 error so I assume something goes wrong with the routing?
When I comment out the serialize line
ProtoBuf.Serializer.Serialize(memstream, package);
(so the memory stream stays empty) the routing works and RecievePackage is called. And the package parameter is empty. (the package object itsself is not NULL but all it's properties are.)
Whenever the memory stream is not empty I get a 404 error. What am I doing wrong here?
Receive code:
[Route("Receive")]
[HttpPost]
public async Task<IHttpActionResult> RecievePackage(PackageModel package)
{
id = await SavePackage(package);
return Created("", id);
}
Send code:
private async Task SyncUpAsync(string apiUrl, PackageModel package)
{
using (HttpClient client = new HttpClient())
using (var memstream = new MemoryStream())
{
var uri = new Uri(new Uri(ApiUrl), apiUrl);
ProtoBuf.Serializer.Serialize(memstream, package);
memstream.Position = 0;
var content = new StreamContent(memstream);
content.Headers.Add("SyncApiToken", ApiKey);
content.Headers.Add("Content-Type", "application/x-protobuf");
var response = await client.PostAsync(uri, content);
}
}

Related

C# controller to receive HttpRequest and respond back

I have very limited exprerience in MVC, WebAPI,C# etc.
I was asked to create a controller that contains a method that receives an HttpRequest (XML) and responds with back with an HttpResponse
I have come up to this state and I am confused:
[HttpPost]
[Consumes("application/xml")]
public async HttpResponseMessage ChannelUpload(HttpRequestMessage request)
{
using (var reader = new StreamReader(
request.Body,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false)
)
{
string bodyString = await reader.ReadToEndAsync();
string confirmation = ImportRequest(bodyString);
//Response
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(confirmation) };
}
}
I do not know whether the parameter of the method is the correct (is
HttpRequest valid?).
I do not know how to respond (CreateResponse() is not found)
Thank you in advance!

Define header for upload file in API

I am trying to upload excel file to convert it to Json, but i need to passing through API Gateway. I have problem to passing the file from API Gateway.
I try to set header in ContentDisposition, ContentLength and ContentType manually.
using (var client = new HttpClient())
{
using (var Content = new MultipartFormDataContent())
{
var name = Path.GetFileName(postedFile.FileName);
HttpContent content = new StringContent("");
content.Headers.Clear();
content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
{
Name = name,
FileName = name
};
content.Headers.Add("Content-Length", postedFile.ContentLength.ToString());
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("multipart/form-data");
Content.Add(content);
}
HttpResponseMessage reply = new HttpResponseMessage();
reply = await client.GetAsync(#"http://localhost:60897/api/ExceltoJSONConversion");
if (reply.IsSuccessStatusCode)
{
var responseString = await reply.Content.ReadAsStringAsync();
return Json(JsonConvert.DeserializeObject(responseString));
}
}
I have been tried several code but reply always return code 405 MethodNotAllowed.
here my controller where i proceed file
[HttpPost]
[Route("api/ExceltoJSONConversion")]
public IHttpActionResult ExceltoJSONConversion()
{
// Process the file from API Gateway
}
Am i missing something when define Header multipart/form-data? or my code just a mess?
Your API method accepts POST requests only ([HttpPost] attribute).
And in your client you are trying to get API through GET method (client.GetAsync ... ).
Either decorate your API method with [HttpGet] instead of [HttpPost], either change client part to use POST (client.PostAsync ... ).

.NET Core 3 MVC download a file from HTTP and re-deliver to client using minimum memory

I have a .NET Core 3 MVC app that needs to read a file from one location over HTTP and then re-deliver it back out to the response. Some of these files will be ~200MB in size.
What I have works, but it reads the whole file into memory before sending the File result out to the client. Is there a way to make it essentially a passthrough where the read stream flows into the response stream so that very little memory is required on the server?
This is what I have now but I do not think will perform well with large files:
if (requestedFile != null)
{
using (var client = new System.Net.Http.HttpClient())
{
using (var result = await client.GetAsync(requestedFile.DownloadUrl))
{
if (result.IsSuccessStatusCode)
{
var bytes = await result.Content.ReadAsByteArrayAsync();
return File(bytes, "application/zip", "largefile.zip");
}
}
}
}
I have also tried this which results in a runtime error of "Cannot access a closed Stream":
using (var client = new System.Net.Http.HttpClient())
{
using (var httpResponseMessage = await client.GetAsync(requestedFile.DownloadUrl))
{
return File(await httpResponseMessage.Content.ReadAsStreamAsync(), "application/zip", "largefile.zip");
}
}
Edit:
Solution after some trial and error was remocing all using statements and letting the FileStreamResult close the stream on its own. So I ended up with:
var client = new HttpClient();
var result = await client.GetAsync(requestedFile.DownloadUrl);
var stream = await result.Content.ReadAsStreamAsync();
return new FileStreamResult(stream, "application/zip")
{
FileDownloadName = "largefile.zip"
};
One of the overloads for File is a Stream. Just get that URL as a Stream or read the response body as a stream and immediately return that in the overload:
var client = new System.Net.Http.HttpClient();
var result = await client.GetAsync(requestedFile.DownloadUrl);
var stream = await result.Content.ReadAsStreamAsync();
return File(stream,"application/pdf", "Invoice.pdf");
Note: this will fail if you wrap the Stream in a using block as the FileResult already closes the Stream.
For Export Excel
var client = new System.Net.Http.HttpClient();
var comp = client.GetAsync($"RewardEmployee/ExportExcelCalculate?rewardConfigId={id}").Result;
`var stream = await result.Content.ReadAsStreamAsync();
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"ExportExcelCalculateRewardEmployee.xlsx");

HttpClient hangs for large request

I have an issue in an app developed for Windows Phone 8.1, where its working just fine, but in Windows Mobile 10 it gets stuck on GetAsync. It doesn't throw an exception or anything, just gets stuck there waiting endlessly. The content-length of the response is 22014030 bytes.
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.GetAsync(url))
{
response.EnsureSuccessStatusCode();
using (HttpContent content = response.Content)
{
return await content.ReadAsStringAsync();
}
}
}
I have also tried reading it as a stream, but as soon as i try to read the content body nothing happens anymore.
The code that calls this function is declared as:
public async Task<List<APIJsonObject>> DownloadJsonObjectAsync()
{
string jsonObjectString = await DownloadStringAsync(Constants.URL);
if (jsonObjectString != null && jsonObjectString.Length >= 50)
{
List<APIJsonObject> result = await Task.Run<List<APIJsonObject>>(() => JsonConvert.DeserializeObject<List<APIJsonObject>>(jsonObjectString));
return result;
}
return null;
}
The reason it is blocking is because you are getting a huge response (20.9MB) which the HttpClient will try to download before giving you a result.
However, you can tell the HttpClient to return a result as soon as the response headers are read from the server, which means you get a HttpResponseMessage faster. To do this, you will need to pass HttpCompletionOption.ResponseHeadersRead as a parameter to the SendRequestAsync method of the HttpClient
Here is how:
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("GET"), url))
{
using (var response = await client.SendRequestAsync(request, HttpCompletionOption.ResponseHeadersRead)
{
//Read the response here using a stream for example if you are downloading a file
//get the stream to download the file using: response.Content.ReadAsInputStreamAsync()
}
}
}

HttpClient PostAsync List<string> instead of file.csv

I am attempting to post content to a vendor. The data uploads when I save my data to a .csv then reference the file. What I would prefer to do is skip saving the file to disk and just upload the data. I can't seem to figure out how to serialize the data correctly.
Here is the data:
csv.Add(string.Join(",", "sara1234", "Ancient World Studies-1201A1-01"));
csv.Add(string.Join(",", "jazzy4567", "Ancient World Studies-1201A1-01"));
Here is the upload:
protected async Task<bool> RunAsync(string baseAddress, IEnumerable<string> file, string passkey)
{
byte[] csvBytes = File.ReadAllBytes("C:\\Projects\\AutomationServiceFiles\\DataMig_Test\\Drop\\Hapara\\HaparaStudent.csv");
var csvContent = new ByteArrayContent(csvBytes);
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
content.Add(new StringContent(passkey), "passkey");
content.Add(csvContent, "uploadFile", "student.csv");
var response = await client.PostAsync(baseAddress, content);
response.EnsureSuccessStatusCode();
string returnedContent = await response.Content.ReadAsStringAsync();
}
}
return true;
}
I have tried what's shown below (instead of the file from disk). I get a 200 success message back, but data does not load. Specifically, the first method returns a message that the students were not found (this is good because I know the data was evaluated), the second returns no message at all.
string jsonFile = JsonConvert.SerializeObject(file);
HttpContent contentPost = new StringContent(jsonFile, Encoding.UTF8, "application/json");
content.Add(contentPost, "uploadFile", "student.csv");
Any suggestions?
You could just build the CSV string and pass it to a new instance of StringContent instead of using ByteArrayContent.

Categories

Resources