skip to Main Content

I created a web API using the ASP.NET Core Web API project template in Visual Studio to allow clients to upload files to my application. I created the controller which handles request from clients that want to upload PDF reports to the server. Here is the code for the action method:

[HttpPost("Report/{reportId")]
public async Task<IActionResult> AttachReport(string reportId)
{
    Reporter client = new Reporter();
    await client.AttachReportToRequest(reportId);
    return 
}

This is the method that actually sends the request to my application server.

public async Task<HttpResponseMessage> AttachReportToRequest(string reportId)
{
    try
    {
        if (authToken is null)
        {
            authToken = await login("username", "passw0rd");
        }
        HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, "/api/to/app/server/reportInfo");
        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("app-JWT", authToken);

        string scanReportFile = $"{Directory.GetCurrentDirectory()}\Reports\sample_report.pdf";
        MultipartFormDataContent requestBody = new MultipartFormDataContent();

        //Create values JSON payload
        requestBody.Add(new StringContent($""values":{{
            "ReportId":"{reportId}",
            "Description":"test",
            "Submitter":"John Smith",
            "Number of Attachment":"1"
            }}"), "entry");
        
        requestBody.Add(new StreamContent(File.OpenRead("C:/Users/myuser/Documents/ReporterApp/myreport.pdf")), "file-attachment", "C:/Users/myuser/Documents/ReporterApp/myreport.pdf");

        requestMessage.Content = requestBody;
        HttpResponseMessage attachmentResponse = await httpClient.SendAsync(requestMessage);
        return attachmentResponse;
            }
    catch (Exception)
    {

        throw;
    }
}

The issue I’m having is that every time the httpClient tries to send the request the client times out. I never receive any kind of HTTP response from the server. I tried hitting the same application using the same endpoint and the same multipart/form-data content type from Postman and it works. Is there any particular reason why the SendAsync method would be timing out when sending the request?

I tried putting the FileStream in a using block and tried sending different file types but none of those things worked.

2

Answers


  1. Chosen as BEST ANSWER

    After many, many long hours, I was finally able to figure it out. It turns out there are actually a few things wrong with my code. I initially thought there was an issue with the boundary parameter of the Content-Type header. As it turns out, the .NET Framework automatically takes care of generating the boundary for you.

    The first thing I missed was that, even though MultipartFormDataContent is a subclass of HttpContent, it is actually a container of HttpContent objects. A key difference between attaching a single HttpContent object to your request and attaching an object of type MultipartFormDataContent is that for every object you add to the container you need to specify a Content-Type header; without that header for each object the server won't know what the type of each part is. For example, in the first call to requestBody.Add() I am passing it a StringContent object with the JSON payload string. However, the StringContent constructor has an overload where you can specify the encoding and media type. Without the "application/json", the StringContent object defaults to a media type of "text/plain", which won't work if the server is expecting JSON. Doing this solves the first problem:

    requestBody.Add(new StringContent($"...", Encoding.UTF8, "application/json"), "entry")
    

    The second issue was pretty much the same as the first. Rather than create the StreamContent object inline with the call to requestBody.Add(), I could first assign the StreamContent object to a variable and then add the necessary headers to it. Since the type of file I'm sending is a pdf, the correct media type is "application/octet-stream". This is what worked for me.

    StreamContent myContent = new StreamContent(File.OpenRead("C:/User/.../report.pdf"));
    myContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
    requestBody.Add(myContent, "attachment");
    

    Once I did this, the HttpClient sent the request successfully and no longer timed out. You could also do something like this with the File.ReadAllBytes() method:

    ByteArrayContent fileContents = new ByteArrayContent(File.ReadAllBytes("path/to/file"));
    fileContents.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
    

    This should also work.


  2. try different approach (or use RestSharp library – much cleaner)

    Uri uri = new Uri($"{_BaseUrl}api/endpoint");
    HttpRequestMessage hrm = GetDefaultRequestMessage(HttpMethod.Post, uri);
    var reqContent = new MultipartFormDataContent();
     reqContent.Add(new StringContent(reportID), "ReportId");
    //...
    
    string fileName = $"report.txt";
    MemoryStream ms = new(System.Text.Encoding.UTF8.GetBytes(fileContent.ToString()));
    var bytes = new Byte[ms.Length];
    ms.Read(bytes, 0, bytes.Length);
    reqContent.Add(new ByteArrayContent(bytes), "Files", fileName);
    
    hrm.Content = reqContent;
    var response = await this._client.SendAsync(hrm);
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search