Task:
I am trying to send the image from the C# client at the C# Http Server. The image is stored in the memory only and should not be stored on any hard drive.
State:
I have C# client (.NET FW 4.5) sending an Http request on C# MVC API Controller, using MultiPartFormDataContent.
The client minimal code:
HttpContent content = new StreamContent(new FileStream("R:\\a.png", FileMode.Open));
using (var client = new HttpClient())
using (var form = new MultipartFormDataContent())
{
form.Add(content, "img");
var post = client.PostAsync("http://localhost:58652/api/bubla", form);
var resp = post.Result.Content.ReadAsStringAsync().Result;
}
I am trying to consume the stream on the server-side using:
[Route("api/bubla")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpPost]
public async Task<ActionResult<string>> Post([FromForm] MultipartFormDataContent content)
{
// ???
// e.g. :
// var provider = new MultipartFormDataStreamProvider("R:\\temp");
// await content.ReadAsMultipartAsync(provider);
// not working
}
}
Issue:
I have no idea how to consume the data at the server-side. I have read many tutorials, most of them requiring Request.Content which I do not have. The server-side code above will provide 'something' in the content variable, but I have no idea how to read it out.
Additional notes:
For the final solution, the image cannot be stored on the hard drive - only memory streams. (The reading from the disc in the code above is for the illustration only.
Maybe I am doing something generally wrong
Related
I have a Rest API written in .Net core that accepts a File as input as Multipart/Form-data. The API works absolutely fine when I run it from Swagger/Postman.
Here is the API endpoint.
[HttpPost("CreateStudy")]
public ActionResult CreateStudy([FromForm] APIRequest request)
{
// rest of the code
Also here is the APIRequest object. it has only one property which is IFormFile Type.
public class APIRequest
{
public IFormFile XMLFile { get; set; }
}
So far it works well. The problem is that I am trying to write a client side code that will call this API and pass the File from C# code.
But I am always getting a 400-Bad request in the client code.
This is the client code I am trying with.
public string CallServiceWithFileAsync(string EndPointURL, string FilePath)
{
string ResultStatusCode;
Uri uri = new Uri(EndPointURL);
var Client = new HttpClient();
Client.DefaultRequestHeaders.Clear();
//Prepare Message
HttpRequestMessage Message = new HttpRequestMessage();
Message.Method = HttpMethod.Post;
Message.Headers.Add("Accept", "application/octet-stream");
Message.RequestUri = new Uri(EndPointURL);
using (Stream fileStream = File.OpenRead(FilePath))
{
var content = new StreamContent(fileStream);
var response = Client.PostAsync(uri, content);
ResultStatusCode = response.Result.StatusCode.ToString();
}
return ResultStatusCode;
}
What am I doing wrong here? What is the correct way of sending a file into REST endpoint ?
[FromForm] expects an accept header with application/x-www-url-formencoded. If this is not the case, check your output-logs to see why the request is not processed.
I am building a APIGateway proxy for our dotnet core microservices platform.
I used https://medium.com/#mirceaoprea/api-gateway-aspnet-core-a46ef259dc54 as a starting place, this picks up all requests by using
app.Run(async (context) =>
{
// Do things with context
});
You have the context for the request to the gateway, but how do I copy over the content data from the gateway request to a new request I am going to make to my API?
I see the ability to set the request content to a HttpContent object:
newRequest.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
But I want my application to take file uploads through the gateway, the only way I found to do it is to create a MultipartFormDataContent, but all examples on how to create a MultipartFormDataContent use a IFormFile instead of a HttpContext.
Is there a way to just copy the content on the initial apigateway request to my internal request:
using (var newRequest = new HttpRequestMessage(new HttpMethod(request.Method), serviceUrl))
{
// Add headers, etc
newRequest.Content = // TODO: how to get content from HttpContext
using (var serviceResponse = await new HttpClient().SendAsync(newRequest))
{
// handle response
}
}
You can use StreamContent for this, passing in the HttpContext.Request.Body Stream as the actual content to use. Here's what that looks like in your example:
newRequest.Content = new StreamContent(context.Request.Body);
As an aside, make sure that you use a shared instance of HttpClient.
I have tried to create a simple console application.
We have a call system from 8x8 that provide a web streaming API but their documentation is very limited and nothing in C#.
The api service streams call statuses in near real time and I would like to get that 'stream' and be able to read and process it in realtime if possible. The response or Content Type is 'text/html'. But the actual body of the response can be declared as json - sample below:
{"Interaction":{"attachedData":{"attachedDatum":[{"attachedDataKey":"#pri","attachedDataValue":100},{"attachedDataKey":"callingName","attachedDataValue":999999999999},{"attachedDataKey":"cha","attachedDataValue":99999999999},{"attachedDataKey":"cnt","attachedDataValue":0},{"attachedDataKey":"con","attachedDataValue":0},{"attachedDataKey":"med","attachedDataValue":"T"},{"attachedDataKey":"pho","attachedDataValue":9999999999},{"attachedDataKey":"phoneNum","attachedDataValue":9999999999},{"attachedDataKey":"tok","attachedDataValue":999999999}]},"event":"InteractionCreated","inboundChannelid":9999999999,"interactionEventTS":9999999,"interactionGUID":"int-15b875d0da2-DJOJkDhDsrh3AIaFP8VkICv9t-phone-01-testist","resourceType":0}}
I have seen several posts concerning httpClient and the GetAsync methods but none of these appear to work as they appear to be for calls when a response is made, not something that constantly has a response.
Using fiddler for the call it does not appear to close so the stream is constantly running, so fiddler does not display any data until a separate user or instance connects.
When I use a browser the content is 'streamed' to the page and updates automatically and shows all the content (as above).
The api contains authentication so when another client connects and retrieves data the connected client closes and finally I am able to see the data that was gathering.
This is the code so and does return the big stream when another client connects but ideally I want a real time response and appears to just get stuck in the GETASYNC method:
var response = await client.GetAsync(address, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
string responseString = await responseContent.ReadAsStringAsync();
Console.WriteLine(responseString);
}
Hopefully that's enough information for one of you clever people to help me in my predicament.
I was also having an issue consuming their streaming API and the examples I found that worked with the Twitter and CouchBase streaming API's did not work with 8x8. Both Twitter and CouchBase send line terminators in their pushes so the solution relied on ReadLine to pull in the feed. Since 8x8 does not send terminators you'll need to use ReadBlock or better ReadBlockAsync.
The following code shows how to connect using credentials and consume their feed:
private static async Task StreamAsync(string url, string username, string password)
{
var handler = new HttpClientHandler()
{
Credentials = new NetworkCredential {UserName = username, Password = password},
PreAuthenticate = true
};
// Client can also be singleton
using (var client = new HttpClient(handler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.Connection.Add("keep-alive");
using (var response = await client.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead))
{
using (var body = await response.Content.ReadAsStreamAsync())
{
using (var reader = new StreamReader(body))
{
while (!reader.EndOfStream)
{
var buffer = new char[1024];
await reader.ReadBlockAsync(buffer, 0, buffer.Length);
Console.WriteLine(new string(buffer));
}
}
}
}
}
}
I have a HttpResponse object as a result of HttpClient.SendAsync() call. The response has a chunked transfer encoding and results in 1.5 GB of data.
I want to pass this data through OWIN pipeline. To do this I need to convert it to a stream. Simplified code to do this is:
public async Task Invoke(IDictionary<string, object> environment)
{
var httpContent = GetHttpContent();
var responseStream = (Stream)environment["owin.ResponseBody"];
await httpContent.CopyToAsync(responseStream);
}
However, the last line results in copying the entire stream to the memory. And when I use wget to download the data directly from the backend server, it is downloaded successfully and shows a progress bar (although it doesn't know the overall size since it is chunked). But when I use wget to download data from my OWIN-hosted application it sticks on sending the request.
How should I stream this data through an OWIN pipeline to prevent copying it to memory?
EDIT
This is how I get the HttpResponse:
var client = new HttpClient(new HttpClientHandler());
// …and then:
using (var request = new HttpRequestMessage { RequestUri = uri, Method = HttpMethod.Get })
{
return client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).Result;
}
I assume this is in IIS? System.Web also buffers responses: https://msdn.microsoft.com/en-us/library/system.web.httpresponse.bufferoutput(v=vs.110).aspx
See server.DisableResponseBuffering in
https://katanaproject.codeplex.com/wikipage?title=OWIN%20Keys&referringTitle=Documentation
I'am trying to pass values from a controller to another controller in another domain. I'am adding data to a NameValueCollection and pass it to another controller [httppost] method and receiving data there mapped to a Model same as i passed from.
Currently i'am running it locally by opening two instance of VS simultaneously. When the both VS is opened the values are passed correctly and the information is written to db correctly and i receive a response like "{byte[0]}". Now when i try stopping the destination controller Project and try to submit data then it wont work but still i get the same response as "{byte[0]}". Can somebody please help me how to return the response command in this scenario. Is there a way a understand the UploadValues are completed or not completed.
.........
.........
NameValueCollection resumeDetails = new NameValueCollection();
resumeDetails.Add("FirstName", "KRIZTE");
byte[] res = this.Post(ConfigurationManager.AppSettings["RedirectionUrl"].ToString(), resumeDetails);
return View("Index");
}
public byte[] Post(string uri, NameValueCollection resumeDetails)
{
byte[] response = null;
WebClient client = new WebClient();
response = client.UploadValues(uri, resumeDetails);
return response;
}
You should not use the WebClient because of problems like this.
Microsoft implemented HttpClient class as a newer API and it has these benefits:
HttpClient is the newer of the APIs and it has the benefits of
has a good async programming model
1- being worked on by Henrik F Nielson who is basically one of the inventors of HTTP, and he designed the API so it is easy for you to follow the HTTP standard, e.g. generating standards-compliant headers
2- is in the .Net framework 4.5, so it has some guaranteed level of support for the forseeable future
3- also has the xcopyable/portable-framework version of the library if you want to use it on other platforms - .Net 4.0, Windows Phone etc.
so I'm gonna show you an example of using HttpClient:
var uri = "http://google.com";
var client = new HttpClient();
try
{
var values = new List<KeyValuePair<string, string>>();
// add values to data for post
values.Add(new KeyValuePair<string, string>("FirstName", "KRITZTE"));
FormUrlEncodedContent content = new FormUrlEncodedContent(values);
// Post data
var result = await client.PostAsync(uri, content);
// Access content as stream which you can read into some string
Console.WriteLine(result.Content);
// Access the result status code
Console.WriteLine(result.StatusCode);
}
catch(AggregateException ex)
{
// get all possible exceptions which are thrown
foreach (var item in ex.Flatten().InnerExceptions)
{
Console.WriteLine(item.Message);
}
}