【问题标题】:Post multipart/form-data file from stream with HttpClient使用 HttpClient 从流中发布多部分/表单数据文件
【发布时间】:2020-09-17 20:51:35
【问题描述】:

问题:是否可以使用 httpclient 将 HttpPostedFileBase(仅在内存中可用,而不在磁盘上。)发布到另一个端点?

我的尝试:

我有以下控制器,我们从前端发布一个文件,它绑定到文件参数。

public class HomeController : Controller
{
    [System.Web.Http.HttpPost]
    public async Task<string> Index(HttpPostedFileBase file)
    {
        //file is not null here, everything works as it should.

        //Here im preparing a multipart/form-data request to my api endpoint
        var fileStreamContent = new StreamContent(file.InputStream);
        using (var client = new HttpClient())
        using (var formData = new MultipartFormDataContent())
        {
            formData.Add(fileStreamContent);
            var response = await client.PostAsync("http://restapi.dev/api/files/add", formData);
            var result = await response.Content.ReadAsStringAsync();
            return result;
        }
    }
}

我需要将此请求传递给另一个不公开的应用程序(因此我们不能直接从客户端发布) 该控制器如下所示:

[RoutePrefix("api/files")]
public class FilesController : ApiController
{
    [HttpPost]
    [Route("add")]
    public async Task<HttpResponseMessage> Add(HttpPostedFileBase file)
    {
        //This is the problem, file is always null when I post from my backend.
        var files = HttpContext.Current.Request.Files.Count > 0 ? HttpContext.Current.Request.Files[0] : null;
        return Request.CreateResponse(HttpStatusCode.OK);
    }
}

file 始终为空,files 也是如此。 我错过了什么?当我使用邮递员并直接发布到 API 端点时,它可以工作。所以我猜我在我的HomeController 中做错了什么?

【问题讨论】:

  • 所以这是一个简单的代理到另一台服务器?第一个控制器是什么? MVC/WebAPI?会不会是 WebAPI?
  • 没错,第一个控制器是 MVC 控制器,第二个是 WebAPI 控制器。如果这样更容易解决问题,我可以更改它们的类型。理想情况下,我只想将第一个请求直接传递给我的 api 控制器。

标签: c# multipartform-data


【解决方案1】:

您可以使用更新的技术对此进行改进。

在webapi中,你可以这样写一个控制器方法:

//[Route("api/Foo")] //maybe?
[HttpPost]
public async Task<HttpResponseMessage> MyResourceProxy(HttpRequestMessage request)

现在您可以接受该请求并重写其RequestUri 属性:

request.RequestUri = new Uri(blah);

新建一个HttpClient 并转发请求:

HttpClient client = new HttpClient();
//make sure that this fails if it's hung for more than 30 seconds
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
    response = await client.SendAsync(request,
                                      HttpCompletionOption.ResponseHeadersRead, 
                                      cts.Token);
}
catch (TaskCanceledException)
{
    response = new HttpResponseMessage(HttpStatusCode.GatewayTimeout);
}

确保一切都得到处理:

request.RegisterForDispose(new IDisposable[] {request, client, cts, response});

然后返回response

return response;

我添加了一个超时机制,它可能不适合您的需要。

【讨论】:

  • 这看起来真的很好,它正在工作,有点。我收到以下错误(来自我的 api){ "Message": "No HTTP resource was found that matches the request URI 'http://restproxy.dev/api/files/add'.", "MessageDetail": "No type was found that matches the controller named 'files'." } 这很奇怪,因为如果我尝试直接在浏览器中访问该 url,它就可以工作。感觉就像我在控制器中执行的请求看起来在错误的应用程序中,不知何故......将进一步调查!
  • @JOSEFtw 看起来您可能还需要更改 request.Headers.Host 属性。
  • 是的!就是这样。感谢您的解决方案,它真的很干净。祝你有美好的一天!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-27
  • 1970-01-01
  • 2014-08-06
相关资源
最近更新 更多