1

I need to send a file from one Mvc4 application to another (another one is a Mvc4, WebApi application). For the purpose of sending I'm using HttpClient's PostAsync method. Here is the code that performs sending:

public class HomeController : Controller
{
    public async Task<ActionResult> Index()
    {
        var result =
            await Upload("http://localhost/target/api/test/post", "Test", System.IO.File.Open(@"C:\SomeFile", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite));

        return View(result);
    }

    public async Task<String> Upload(String url, string filename, Stream stream)
    {
        using (var client = new HttpClient())
        {
            var formData = new MultipartFormDataContent();

            var fileContent = new StreamContent(stream);

            var header = new ContentDispositionHeaderValue("attachment") { FileName = filename };

            fileContent.Headers.ContentDisposition = header;

            formData.Add(fileContent);

            var result = await client.PostAsync(url, formData); // Use your url here

            return "123";
        }
    }
}

For the purpose of receiving, I'm using one of the examples found on official web api tutorial, here is the code that does it:

public class TestController : ApiController
{
    // POST api/values
    public Task<HttpResponseMessage> Post()
    {
        if (!Request.Content.IsMimeMultipartContent())
        {
            throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
        }

        string root = HttpContext.Current.Server.MapPath("~/App_Data");


        var provider = new MultipartFormDataStreamProvider(root);


        // Read the form data and return an async task.
        var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
        {
            if (t.IsFaulted || t.IsCanceled)
            {
                return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
            }

            // This illustrates how to get the file names.
            foreach (MultipartFileData file in provider.FileData)
            {
                Trace.WriteLine(file.Headers.ContentDisposition.FileName);
                Trace.WriteLine("Server file path: " + file.LocalFileName);

            }

            return new HttpResponseMessage
            {
                Content = new StringContent("File uploaded.")
            };

            //var response = Request.CreateResponse(HttpStatusCode.OK);

            //return response;
        });

        return task;
    }
}

The problem:

The receiver obtains file successfully, but when it responses with the following code:

return new HttpResponseMessage
{
    Content = new StringContent("File uploaded.")
};

The sender side is breaks somewhere deep inside internals of .Net's mscorlib. Even if I wrap the await call with try / catch the exception is not handled.

I'd like to keep the async implementation and don't want to use the sync one, is that possible? Why does the problem occurs? Any thoughts?

1
  • Can you post the full exception, including stack trace? Commented Jun 20, 2013 at 12:17

1 Answer 1

7

Double-check your tutorial code. I assume you're talking about this page, in which case you should be using the .NET 4.5 version of the code (with await), not the old .NET 4.0 example.

The ASP.NET intrinsics (HttpContext, request, response, etc) can only be accessed from the request context. Your code is using ContinueWith without specifying a TaskScheduler, which in this case will cause that lambda to be run on the thread pool outside the request context.

In async code, you should not use ContinueWith at all (unless you really need it); use await instead:

// POST api/values
public async Task<HttpResponseMessage> Post()
{
    if (!Request.Content.IsMimeMultipartContent())
    {
        throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
    }

    string root = HttpContext.Current.Server.MapPath("~/App_Data");
    var provider = new MultipartFormDataStreamProvider(root);

    try
    {
        // Read the form data.
        await Request.Content.ReadAsMultipartAsync(provider);

        // This illustrates how to get the file names.
        foreach (MultipartFileData file in provider.FileData)
        {
            Trace.WriteLine(file.Headers.ContentDisposition.FileName);
            Trace.WriteLine("Server file path: " + file.LocalFileName);
        }

        return new HttpResponseMessage
        {
            Content = new StringContent("File uploaded.")
        };
    }
    catch (Exception ex)
    {
        return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

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.