使用ASP.NET Web API发送HTTP POST请求中的PDF文件。

huangapple go评论72阅读模式
英文:

Send PDF file in HTTP POST request using ASP.NET Web API

问题

我使用ASP.NET Core Web API项目模板在Visual Studio中创建了一个Web API,允许客户端上传文件到我的应用程序。我创建了处理来自想要将PDF报告上传到服务器的客户端请求的控制器。以下是操作方法的代码:

[HttpPost("Report/{reportId}")]
public async Task<IActionResult> AttachReport(string reportId)
{
    Reporter client = new Reporter();
    await client.AttachReportToRequest(reportId);
    return Ok(); // 这里是您的返回语句
}

这是实际发送请求到我的应用程序服务器的方法:

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();

        // 创建值的JSON负载
        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;
    }
}

我遇到的问题是,每次httpClient尝试发送请求时,客户端都会超时。我从服务器那里从未收到任何HTTP响应。我尝试使用Postman从相同的端点使用相同的multipart/form-data内容类型访问相同的应用程序,它可以正常工作。是否有特定的原因,导致SendAsync方法在发送请求时超时?

我尝试将FileStream放在using块中,并尝试发送不同类型的文件,但这些方法都没有奏效。

英文:

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(&quot;Report/{reportId&quot;)]
public async Task&lt;IActionResult&gt; 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&lt;HttpResponseMessage&gt; AttachReportToRequest(string reportId)
{
	try
	{
		if (authToken is null)
		{
			authToken = await login(&quot;username&quot;, &quot;passw0rd&quot;);
		}
		HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, &quot;/api/to/app/server/reportInfo&quot;);
		requestMessage.Headers.Authorization = new AuthenticationHeaderValue(&quot;app-JWT&quot;, authToken);

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

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

		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.

答案1

得分: 3

以下是您要翻译的内容:

经过许多、许多个漫长的小时,我终于能够弄清楚了。原来我的代码有一些问题。我最初以为Content-Type标头的边界参数有问题。事实证明,.NET Framework会自动处理为您生成边界的工作。

我错过的第一件事是,虽然MultipartFormDataContent是HttpContent的子类,但它实际上是HttpContent对象的容器。将单个HttpContent对象附加到请求和附加MultipartFormDataContent类型的对象之间的一个关键区别是,对于您添加到容器的每个对象,您需要指定一个Content-Type标头;没有为每个对象提供该标头,服务器将不知道每个部分的类型。例如,在对requestBody.Add()的第一次调用中,我正在传递一个带有JSON负载字符串的StringContent对象。但是,StringContent构造函数有一种可以指定编码和媒体类型的重载。没有"application/json",StringContent对象默认为"text/plain"媒体类型,如果服务器期望JSON,则无法工作。这样做解决了第一个问题:

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

第二个问题基本上与第一个问题相同。与在调用requestBody.Add()时内联创建StreamContent对象不同,我可以首先将StreamContent对象分配给一个变量,然后添加必要的标头。由于我发送的文件类型是pdf,正确的媒体类型是"application/octet-stream"。这是对我有用的。

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

一旦我这样做了,HttpClient成功发送了请求,不再超时。您还可以使用File.ReadAllBytes()方法做类似的事情:

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

这也应该可以工作。

英文:

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($&quot;...&quot;, Encoding.UTF8, &quot;application/json&quot;), &quot;entry&quot;)

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(&quot;C:/User/.../report.pdf&quot;));
myContent.Headers.ContentType = MediaTypeHeaderValue.Parse(&quot;application/octet-stream&quot;);
requestBody.Add(myContent, &quot;attachment&quot;);

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(&quot;path/to/file&quot;));
fileContents.Headers.ContentType = MediaTypeHeaderValue.Parse(&quot;application/octet-stream&quot;);

This should also work.

答案2

得分: 0

尝试不同的方法(或使用RestSharp库 - 更清晰)

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);
英文:

try different approach (or use RestSharp library - much cleaner)

Uri uri = new Uri($&quot;{_BaseUrl}api/endpoint&quot;);
HttpRequestMessage hrm = GetDefaultRequestMessage(HttpMethod.Post, uri);
var reqContent = new MultipartFormDataContent();
 reqContent.Add(new StringContent(reportID), &quot;ReportId&quot;);
//...

string fileName = $&quot;report.txt&quot;;
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), &quot;Files&quot;, fileName);

hrm.Content = reqContent;
var response = await this._client.SendAsync(hrm);

huangapple
  • 本文由 发表于 2023年5月25日 05:04:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/76327398.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定