I am writing a proxy for some site using ASP.NET Core 2.0. My proxy works fine if all it does is just re-translating HttpResponseMessage to the browser. My proxy based on this example. But I need to make some changes site content, for instance, some of the href contains an absolute reference. So when I click them from my proxy, I get to the original site, and it is a problem.
When I tried to get site HTML as a string, I got only special characters. After some search, I find this solution. It fits for my case well and I successfully get site content as a string, which starts with "<!DOCTYPE html>...". After changes are done I need to send this string to my browser, and here I have a problem. I work with the HttpResponseMessage the following way:
using (var responseStream = await responseMessage.Content.ReadAsStreamAsync())
{
string str;
using (var gZipStream = new GZipStream(responseStream, CompressionMode.Decompress))
using (var streamReader = new StreamReader(gZipStream))
{
str = await streamReader.ReadToEndAsync();
//some stings changes...
}
var bytes = Encoding.UTF8.GetBytes(str);
using (var msi = new MemoryStream(bytes))
using (var mso = new MemoryStream())
{
using (var gZipStream = new GZipStream(mso, CompressionMode.Compress))
{
await msi.CopyToAsync(gZipStream);
await gZipStream.CopyToAsync(response.Body, StreamCopyBufferSize, context.RequestAborted);
}
}
//next string works, but I don't change content this way
//await responseStream.CopyToAsync(response.Body, StreamCopyBufferSize, context.RequestAborted);
}
I create GZipStream, successfully copy MemoryStream into it, then I want to copy GZipStream into a response.Body (HttpResponse) using CopyToAsync. On that line I get `NotSupportedException' with message
GZipStream does not support reading
I find out, after compression into GZipStream gZipStream.CanRead is false for some reason. I tried to copy msi into response.Body, it doesn't throw exceptions, but in the browser, I get an empty page (document Response in Network in browser console is also empty).
Hope someone will be able to tell me, that I am doing wrong.
Related
I would like to be able to download files via links in my web app. I am using Amazon S3 to store files in the cloud and am able to retrieve them into a ResponseStream using the example here:
https://docs.aws.amazon.com/AmazonS3/latest/dev/RetrievingObjectUsingNetSDK.html
How do I go from having a ResponseStream to actually being able to download the file in the browser? I do not want to download it to the server, and I want to to be downloaded to the user's downloads folder. I feel like I do not know enough about how to do this to know where to start.
string responseBody = "";
try
{
GetObjectRequest request = new GetObjectRequest
{
BucketName = bucketName,
Key = keyName
};
using (GetObjectResponse response = await client.GetObjectAsync(request))
using (Stream responseStream = response.ResponseStream)
using (StreamReader reader = new StreamReader(responseStream))
{
string contentType = response.Headers["Content-Type"];
responseBody = reader.ReadToEnd(); // Now you process the response body.
// I read the response to the end, how do I create a file and download it?
}
}
If the stream is the file you want to return, just return the stream directly:
return File(responseStream, response.Headers["Content-Type"]);
Note, however, that you should not use using when getting the stream. ASP.NET Core will take care of disposing the stream when it's done with it, but you need it to persist past the scope of the action.
I'm working on a C# application that submits some small files to a device via multipart form data. I've been unable to get the device to actually accept the data that I'm sending it and I have a feeling that it has to do with the actual encoding of the file as it's being transmitted somehow, but I'm not entirely sure. It returns a 200/OK, as a valid POST, but doesn't like the payload.
I've been using Chrome Dev Tools for the devices web interface to match as closely as possible. One thing I've noticed is that Dev Tools doesn't show me the actual payload, just the following as if it can't display it:
------WebKitFormBoundaryWwZOi6WAr7yb3yRE
Content-Disposition: form-data; name="[Redacted]"; filename="[AlsoRedacted]"
Content-Type: application/octet-stream
------WebKitFormBoundaryWwZOi6WAr7yb3yRE--
Alternatively, the MessageBox I've been using to help debug seem to have no issue displaying it.
The relevant portions of the code are below and would appreciate any suggestions.
using (FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.Read))
{
using (MultipartFormDataContent Content = new MultipartFormDataContent(String.Format("----------{0:N}", Guid.NewGuid())))
{
StreamContent ContentStream = new StreamContent(fs);
Content.Add(ContentStream, "filename", System.IO.Path.GetFileName(Filename));
ContentStream.Headers.ContentDisposition.Name = "UploadEdid";
ContentStream.Headers.ContentDisposition.FileNameStar = null;
ContentStream.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
String ContentString = Content.ReadAsStringAsync().Result;
System.Windows.MessageBox.Show(ContentString, ContentString.Length.ToString());
using (HttpResponseMessage Response = await HttpClient.PostAsync(RequestUri, Content))
{
if (Response.IsSuccessStatusCode)
{
HttpLoggedIn = true;
string ResponseString = await Response.Content.ReadAsStringAsync();
ParseHttpResponse(ResponseString);
}
else
{
HttpLoggedIn = false;
}
}
}
}
I'm producing a REST API that does some file conversion / processing.
My Visual Studio 2015 and building on an AWS Serverless Core - ASP.Net Core Web API template.
I'm running some initial test methods and have encountered what appears to be an encoding issue.
My controller has the following. It simply pulls the posted file into a byte array via a memory stream and then passes it back. (The final application will process the byte array)
[HttpPost]
public IActionResult Post(IFormFile file)
{
var inputStream = new MemoryStream();
file.CopyTo(inputStream);
var fileBytes = inputStream.ToArray();
var outputStream = new MemoryStream(fileBytes);
return File(outputStream, "application/octet-stream");
}
I then have a test application I'm using to pass a file to this controller and save the return.
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
var inputFileStream = new FileStream(Server.MapPath("~/App_Data/InputFile.pdf"), FileMode.Open, FileAccess.Read);
var inputFileBytes = new Byte[inputFileStream.Length];
inputFileStream.Read(inputFileBytes, 0, inputFileBytes.Length);
inputFileStream.Close();
content.Add(new ByteArrayContent(inputFileBytes), "file", "InputFile.pdf");
var requestUri = "http://localhost:5000/api/controller";
//var requestUri = "https://xxxxxxxxxx.execute-api.eu-west-1.amazonaws.com/Prod/api/controller";
var result = client.PostAsync(requestUri, content).Result;
var resultStream = result.Content.ReadAsStreamAsync().Result;
var memoryStream = new MemoryStream();
resultStream.CopyTo(memoryStream);
var outputFileBytes = memoryStream.ToArray();
FileStream outputFileStream = new FileStream(Server.MapPath("~/App_Data/OutputFile.pdf"), FileMode.Create, FileAccess.ReadWrite);
outputFileStream.Write(outputFileBytes, 0, outputFileBytes.Length);
outputFileStream.Close();
}
}
When I run using the localhost application, the duplicate file is saved back. However, when I publish the API to AWS, the file returned is exactly double in size to the original, certainly indicated an encoding issue.
If I pass an ANSI text file with the contents TEST then the saved file contains VEVTVA==
Can someone point me at where I should be setting any encoding settings and any suggested settings to ensure that the output stream from my HttpClient is the same as my input?
So I noticed this as well with a AWS ASP.Net Core Web API. I changed the MIME type from application/octet-stream to application/text and that seemed to fix it on AWS.
[HttpPost]
public IActionResult Post(IFormFile file)
{
var inputStream = new MemoryStream();
file.CopyTo(inputStream);
var fileBytes = inputStream.ToArray();
var outputStream = new MemoryStream(fileBytes);
return File(outputStream, "application/text");
}
I'm trying to implement HMAC security for an API. Everything works fine until I try to post a file.
The HMAC solution can be found here - https://github.com/gavinharriss/WebAPI.HMAC - it's a fork from the original to allow GET requests as well as POST requests.
The code to attach a file:
var requestContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(file);
requestContent.Add(fileContent, "file", filename);
if I immediately call HttpContent.ReadAsByteArrayAsync() there is no issue, the byte array is available.
However, the HMAC HttpClient (HMACHttpClient) implements a DelegatingHandler (HMACDelegatingHandler) in order to attach the HMAC header to requests.
In the HMACDelegatingHandler the request is passed along as a HttpRequestMessage from which the HttpRequestMessage.Content property is used in a helper to build the HMAC signature.
When building the signature, the following code is called from a helper class:
private static async Task<byte[]> ComputeHash(HttpContent httpContent)
{
using (var md5 = MD5.Create())
{
byte[] hash = null;
if (httpContent != null)
{
var content = await httpContent.ReadAsByteArrayAsync(); // <-- Fails here
if (content.Length != 0)
{
hash = md5.ComputeHash(content);
}
}
return hash;
}
}
When stepping through the code the var content = await httpContent.ReadAsByteArrayAsync() line is hit, then nothing, no error. The requests just seems to go poof but everything is still running and the HttpClient request never gets sent.
Any ideas what's going on?
Having tested this with various sizes of file, I found the issue arose when files got around the 50,000 byte mark.
This post provided a solution: HttpContent.ReadAsStringAsync causes request to hang (or other strange behaviours).
If you replace erroring line in HMACHelper (line 66):
var content = await httpContent.ReadAsByteArrayAsync();
with this:
var ms = new MemoryStream();
await httpContent.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
var content = ms.ToArray();
It should stop hanging.
I have recently started working with web api's.
I need to download a file in C# project from a web api, which works fine when I hit the web api using postman's send and download option. Refer to the image, also please check the response in header's tab. This way, I am able to directly download the file to my computer.
I want to do the same from my C# project, I found following two links which shows how to download a file from web api.
https://code.msdn.microsoft.com/HttpClient-Downloading-to-4cc138fd
http://blogs.msdn.com/b/henrikn/archive/2012/02/16/downloading-a-google-map-to-local-file.aspx
I am using the following code in C# project to get the response:
private static async Task FileDownloadAsync()
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Accept", "text/html");
try
{
// _address is exactly same which I use from postman
HttpResponseMessage response = await client.GetAsync(_address);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
catch (Exception)
{
throw;
}
}
}
However I am not getting the response at all (before I can start to convert the response to a file), please check the error message coming:
What am I missing here, any help would be appreciated.
As the (500s) error says - it's the Server that rejects the request. The only thing I see that could cause an issues is the charset encoding. Yours is the default UTF-8. You could try with other encodings.
Below method uses:
SSL certificate (comment out code for cert, if you don't use it)
Custom api header for additional layer of security (comment out Custom_Header_If_You_Need code, if you don't need that)
EnsureSuccessStatusCode will throw an error, when response is not 200. This error will be caught in and converted to a human readable string format to show on your screen (if you need to). Again, comment it out if you don't need that.
private byte[] DownloadMediaMethod(string mediaId)
{
var cert = new X509Certificate2("Keystore/p12_keystore.p12", "p12_keystore_password");
var handler = new WebRequestHandler { ClientCertificates = { cert } };
using (var client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Add("Custom_Header_If_You_Need", "Value_For_Custom_Header");
var httpResponse = client.GetAsync(new Uri($"https://my_api.my_company.com/api/v1/my_media_controller/{mediaId}")).Result;
//Below one line is most relevant to this question, and will address your problem. Other code in this example if just to show the solution as a whole.
var result = httpResponse.Content.ReadAsByteArrayAsync().Result;
try
{
httpResponse.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
if (result == null || result.Length == 0) throw;
using (var ms = new MemoryStream(result))
{
var sr = new StreamReader(ms);
throw new Exception(sr.ReadToEnd(), ex);
}
}
return result;
}
}
Once you have your http response 200, you can use the received bytes[] as under to save them in a file:
using (var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
fs.Write(content, 0, content.Length);
}
Your request header says that you accept HTML responses only. That could be a problem.