Restsharp addFile() can't read file path correctly - api

I am trying to test response after uploading 3 .csv files, as part of integration tests for API on project I am currently on.
public void UploadFile(string token)
{
request = new RestRequest(Method.POST);
endpoint.Timeout = -1;
request.AlwaysMultipartFormData = true;
request.AddHeader("Authorization", $"Bearer {token}");
request.AddHeader("Content-Type", "multipart/form-data");
request.AddFile("Customers", "Users/Dell/Downloads/ProRecoFiles/CUSTOMERS.csv");
//request.AddFile(Constants.customers, Constants.customersPath);
//request.AddFile(Constants.orders, Constants.ordersPath);
//request.AddFile(Constants.products, Constants.productsPath);
endpoint.Execute(request);
}
Although I'm relatively new to Restsharp and C#, I would say this is pretty straight forward request. But apparently something is wrong as I get:
Message:
System.IO.FileNotFoundException : Could not find file 'C:\Users\Dell\Source\Repos\proreco-client-api\ProReco.Client.API\Tests\bin\Debug\netcoreapp3.1\Users\Dell\Downloads\ProRecoFiles\CUSTOMERS.csv'.
In the request.AddFile() I am passing name of file and path to the file. Somehow it concatenate value of file path to location of my project. Any idea how to fix that? Any suggestion would be great.

Preface the path with "C:/". E.g.:
request.AddFile("Customers","C:/Users/Dell/Downloads/ProRecoFiles/CUSTOMERS.csv");

Related

Design Minimal API and use HttpClient to post a file to it

I have a legacy system interfacing issue that my team has elected to solve by standing up a .NET 7 Minimal API which needs to accept a file upload. It should work for small and large files (let's say at least 500 MiB). The API will be called from a legacy system using HttpClient in a .NET Framework 4.7.1 app.
I can't quite seem to figure out how to design the signature of the Minimal API and how to call it with HttpClient in a way that totally works. It's something I've been hacking at on and off for several days, and haven't documented all of my approaches, but suffice it to say there have been varying results involving, among other things:
4XX and 500 errors returned by the HTTP call
An assortment of exceptions on either side
Calls that throw and never hit a breakpoint on the API side
Calls that get through but the Stream on the API end is not what I expect
Errors being different depending on whether the file being uploaded is small or large
Text files being persisted on the server that contain some of the HTTP headers in addition to their original contents
On the Minimal API side, I've tried all sorts of things in the signature (IFormFile, Stream, PipeReader, HttpRequest). On the calling side, I've tried several approaches (messing with headers, using the Flurl library, various content encodings and MIME types, multipart, etc).
This seems like it should be dead simple, so I'm trying to wipe the slate clean here, start with an example of something that partially works, and hope someone might be able to illuminate the path forward for me.
Example of Minimal API:
// IDocumentStorageManager is an injected dependency that takes an int and a Stream and returns a string of the newly uploaded file's URI
app.MapPost(
"DocumentStorage/CreateDocument2/{documentId:int}",
async (PipeReader pipeReader, int documentId, IDocumentStorageManager documentStorageManager) =>
{
using var ms = new MemoryStream();
await pipeReader.CopyToAsync(ms);
ms.Position = 0;
return await documentStorageManager.CreateDocument(documentId, ms);
});
Call the Minimal API using HttpClient:
// filePath is the path on local disk, uri is the Minimal API's URI
private static async Task<string> UploadWithHttpClient2(string filePath, string uri)
{
var fileStream = File.Open(filePath, FileMode.Open);
var content = new StreamContent(fileStream);
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, uri);
var httpClient = new HttpClient();
httpRequestMessage.Content = content;
httpClient.Timeout = TimeSpan.FromMinutes(5);
var result = await httpClient.SendAsync(httpRequestMessage);
return await result.Content.ReadAsStringAsync();
}
In the particular example above, a small (6 bytes) .txt file is uploaded without issue. However, a large (619 MiB) .tif file runs into problems on the call to httpClient.SendAsync which results in the following set of nested Exceptions:
System.Net.Http.HttpRequestException - "Error while copying content to a stream."
System.IO.IOException - "Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.."
System.Net.Sockets.SocketException - "An existing connection was forcibly closed by the remote host."
What's a decent way of writing a Minimal API and calling it with HttpClient that will work for small and large files?
Kestrel allows uploading 30MB per default.
To upload larger files via kestrel you might need to increase the max size limit. This can be done by adding the "RequestSizeLimit" attribute. So for example for 1GB:
app.MapPost(
"DocumentStorage/CreateDocument2/{documentId:int}",
[RequestSizeLimit(1_000_000_000)] async (PipeReader pipeReader, int documentId) =>
{
using var ms = new MemoryStream();
await pipeReader.CopyToAsync(ms);
ms.Position = 0;
return "";
});
You can also remove the size limit globally by setting
builder.WebHost.UseKestrel(o => o.Limits.MaxRequestBodySize = null);
This answer is good but the RequestSizeLimit filter doesn't work for minimal APIs, it's an MVC filter. You can use the IHttpMaxRequestBodySizeFeature to limit the size (assuming you're not running on IIS). Also, I made a change to accept the body as a Stream. This avoids the memory stream copy before calling the CreateDocument API:
app.MapPost(
"DocumentStorage/CreateDocument2/{documentId:int}",
async (Stream stream, int documentId, IDocumentStorageManager documentStorageManager) =>
{
return await documentStorageManager.CreateDocument(documentId, stream);
})
.AddEndpointFilter((context, next) =>
{
const int MaxBytes = 1024 * 1024 * 1024;
var maxRequestBodySizeFeature = context.HttpContext.Features.Get<IHttpMaxRequestBodySizeFeature>();
if (maxRequestBodySizeFeature is not null and { IsReadOnly: true })
{
maxRequestBodySizeFeature.MaxRequestBodySize = MaxBytes;
}
return next(context);
});
If you're running on IIS then https://learn.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/requestlimits/#configuration

How to read the html out of spa website with asp.net.core

As there are no API for this I need to get the HTML of the following website with WebClient response method.
HttpClient client = new HttpClient();
try
{
HttpResponseMessage response = await client.GetAsync("https://www.datawrapper.de/_/UPFwh/");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
}
catch(HttpRequestException e)
{
}
client.Dispose(true);
The problem is, when I do that I get only the source code of normal javascripts of this single page application and not the real HTML.
Anybody know how to grab the real html with
I profiled the traffic a bit and it looks like the response from that URL you're using is indeed mainly a script, which eventually will load the rest of the website.
Looking through the details the HTML part of the main data seems to be available under a different URL:
https://datawrapper.dwcdn.net/UPFwh/34/
Consider using that instead. Hope this helps!

How to solve file upload error in Postman?

I use file upload with webapi in my project. I am testing with Postman. However, Request.Content.IsMimeMultipartContent() always returns false.
Postman screenshot:
FileUploadController Code:
public async Task<HttpResponseMessage> UserImageUpload()
{
try
{
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
var userImageUploadPath = HttpContext.Current.Server.MapPath(CommonParameters.UserProfileImageServerPath);
var streamProvider = new CustomMultipartFormDataStreamProvider(userImageUploadPath);
await Request.Content.ReadAsMultipartAsync(streamProvider);
var files = new List<string>();
foreach (MultipartFileData file in streamProvider.FileData)
{
files.Add(Path.GetFileName(file.LocalFileName));
}
return Request.CreateResponse(HttpStatusCode.OK, files);
}
catch (Exception exception)
{
logger.ErrorFormat("An error occured in UserImageUpload() Method - Class:FileUploadController - Message:{0}", exception);
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
This is Postman bug. Try removing the Content-Type header. When sending the actual Post, the browser will automatically add the proper header and create the boundary.
There is no need to mention Content-Type in headers in Postman, I tried sending attachments without Content-Type it works fine for me.
When i used Content-Type: multipart/formdata it throws an error saying "Could not get any response". Postman sends your file attachments also with Content-Type →text/plain; charset=utf-8.
There are few things to consider:
1) Don't send any Content Type Header if you are using Postman
2) Specify the Key for your choosen file in Body (PFB Screenshot for your reference)
You need to uncheck Content-Type if you have it checked, and let Postman add it for you, like in this picture:
Might by a bit late. I encountered the same error in ARC and resolved by providing a name for the file field (after the blue check mark on your second screenshot)

How to upload a new file to OneDrive using new API?

I'm trying to get file uploads working with the new OneDrive API. I'm starting out with just simple files (i.e. < 100MB) with the intention of progressing to resumable uploads once I've got the simple ones working!
I've tried using http://onedrive.github.io/items/upload_put.htm but I'm getting 403 back. I thought that this might be because the file didn't already exist but I've uploaded the file using the OneDrive web UI successfully and the code still can't upload the file.
The URL I'm using is something like:
https://api.onedrive.com/v1.0/drive/root:/:/content?access_token=
The C# code is:
using (Stream fileStream = await file.OpenStreamForReadAsync())
{
try
{
HttpStreamContent streamContent = new HttpStreamContent(fileStream.AsInputStream());
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Put, oneDriveURI);
request.Content = streamContent;
request.Content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("application/octet-stream");
HttpResponseMessage response = await client.SendRequestAsync(request);
Debug.WriteLine("UploadFileToOneDrive: response = {0}", response.StatusCode);
}
catch (Exception ex)
{
Debug.WriteLine("UploadFileToOneDrive failed with exception {0}", ex.Message);
}
}
What have I got wrong/misunderstood? The API documentation needs a LOT more examples :-(
403 error code is related to permissions so it could be possible that you may have forgotten to include the proper scopes to upload the file. When you send your OAuth request, you'll want to also include "onedrive.readwrite" as one of the scopes.
GET https://login.live.com/oauth20_authorize.srf?client_id={client_id}&scope={scope}&response_type=token&redirect_uri={redirect_uri}
More scopes can be found at "http://onedrive.github.io/auth/msa_oauth.htm". I hope that helps.

Upload image with RESTSharp (addFile)

I'd like to send a picture from my Windows Phone on a webservice hosted on Windows Azure.
To communicate with my service, I use RESTSharp and I saw that there was a method named addFile for sending file.
RestRequest request;
request = new RestRequest("/report/add", Method.POST);
request.AddFile("test", ConvertToBytes(e.ChosenPhoto), "testfile");
App.Client.ExecuteAsync(request, response =>
{
RestResponse resource = response;
if (response.StatusCode == HttpStatusCode.OK)
{
MessageBox.Show("Your report has been sent! Thank you for your participation!");
}
});
However, I do not know how to retrieve the array of bytes sent when the request arrives at the service.
Can you help me please?
Could you show the code that you use to handle the file server side? It could be that you're looking in the wrong place.
Alternatively, you could try an other way to add the file:
request.AddBody(new { myFile = fileByteArray }))
Note: In both cases the file will be loaded in memory. This could be a problem for large files.