2

The problem: Is it possible using httpclient to post a HttpPostedFileBase(only available in memory, not on disk.) to another endpoint?

What I've tried:

I have the following controller, we are posting in a file from our frontend and it binds to the file parameter.

public class HomeController : Controller
{
    [System.Web.Http.HttpPost]
    public async Task<Stream> 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.ReadAsStreamAsync();
            return result;
        }
    }
}

I need to pass this request on to another application that's not publicly available(so we can't post directly from the client) That controller looks like this:

[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 is always null, so are files. What am I missing? When I use postman and post directly to the API endpoint, it works. So im guessing that im doing something wrong in my HomeController?

2
  • So this is a simple proxy onto another server? What is the first controller? MVC/ WebAPI? Could it be WebAPI? Commented Jul 8, 2016 at 17:12
  • Yeah exactly, the first controller is a MVC controller and the second one is a WebAPI controller. I can change the type on both of them if that would make it easier to solve the problem. Ideally i would just like to passthrough the first request straight to my api controller. Commented Jul 8, 2016 at 17:14

1 Answer 1

1

You can improve on this with newer tech.

In webapi, you can write a controller method like this:

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

now you can take that request and rewrite its RequestUri property:

request.RequestUri = new Uri(blah);

new up an HttpClient and forward the request:

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

make sure that everything gets disposed:

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

and then return the response

return response;

I added a timeout mechanism in that might not be appropriate for you needs.

Sign up to request clarification or add additional context in comments.

3 Comments

This looks really nice, it's working, kind of. Im recieving the following error(from my 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'." } That's weird because if I try to access that url directly in the browser, it works. It feels like the request im doing in my controller looks in the wrong application, somehow...will investigate further!
@JOSEFtw It looks like you may need to also change the request.Headers.Host property.
yep! That was it. Thanks for your solution man, it's really clean. Have a nice day!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.